2023-04-20 05:14:37 +00:00
|
|
|
import type {
|
|
|
|
|
AlignmentColumnData,
|
|
|
|
|
FlexLayer,
|
|
|
|
|
LayerChild,
|
|
|
|
|
} from "./autoLayoutTypes";
|
2023-03-04 07:25:54 +00:00
|
|
|
import {
|
|
|
|
|
FLEXBOX_PADDING,
|
2023-04-07 13:51:35 +00:00
|
|
|
layoutConfigurations,
|
2023-03-04 07:25:54 +00:00
|
|
|
GridDefaults,
|
|
|
|
|
MAIN_CONTAINER_WIDGET_ID,
|
2023-04-07 13:51:35 +00:00
|
|
|
WIDGET_PADDING,
|
|
|
|
|
DefaultDimensionMap,
|
2023-04-26 17:39:11 +00:00
|
|
|
AUTO_LAYOUT_CONTAINER_PADDING,
|
2023-03-04 07:25:54 +00:00
|
|
|
} from "constants/WidgetConstants";
|
2023-04-07 13:51:35 +00:00
|
|
|
import type {
|
|
|
|
|
CanvasWidgetsReduxState,
|
|
|
|
|
FlattenedWidgetProps,
|
|
|
|
|
} from "reducers/entityReducers/canvasWidgetsReducer";
|
2023-03-04 07:25:54 +00:00
|
|
|
import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer";
|
|
|
|
|
import {
|
|
|
|
|
defaultAutoLayoutWidgets,
|
|
|
|
|
FlexLayerAlignment,
|
|
|
|
|
Positioning,
|
|
|
|
|
ResponsiveBehavior,
|
|
|
|
|
} from "utils/autoLayout/constants";
|
2023-04-07 13:51:35 +00:00
|
|
|
import {
|
|
|
|
|
updatePositionsOfParentAndSiblings,
|
|
|
|
|
updateWidgetPositions,
|
|
|
|
|
} from "utils/autoLayout/positionUtils";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import type { AlignmentColumnInfo } from "./autoLayoutTypes";
|
2023-04-07 13:51:35 +00:00
|
|
|
import {
|
|
|
|
|
getWidgetMinMaxDimensionsInPixel,
|
|
|
|
|
getWidgetWidth,
|
|
|
|
|
} from "./flexWidgetUtils";
|
|
|
|
|
import type { DSLWidget } from "widgets/constants";
|
2023-03-04 07:25:54 +00:00
|
|
|
|
|
|
|
|
export function updateFlexLayersOnDelete(
|
|
|
|
|
allWidgets: CanvasWidgetsReduxState,
|
|
|
|
|
widgetId: string,
|
|
|
|
|
parentId: string,
|
2023-04-07 13:51:35 +00:00
|
|
|
isMobile: boolean,
|
|
|
|
|
mainCanvasWidth: number,
|
2023-03-04 07:25:54 +00:00
|
|
|
): CanvasWidgetsReduxState {
|
|
|
|
|
const widgets = { ...allWidgets };
|
|
|
|
|
if (
|
|
|
|
|
widgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType ===
|
|
|
|
|
AppPositioningTypes.FIXED ||
|
|
|
|
|
widgets[MAIN_CONTAINER_WIDGET_ID].positioning === Positioning.Fixed
|
|
|
|
|
)
|
|
|
|
|
return widgets;
|
|
|
|
|
let parent = widgets[parentId];
|
|
|
|
|
if (!parent) return widgets;
|
|
|
|
|
|
|
|
|
|
let flexLayers = [...(parent.flexLayers || [])];
|
|
|
|
|
if (!flexLayers.length) return widgets;
|
|
|
|
|
let layerIndex = -1; // Find the layer in which the deleted widget exists.
|
|
|
|
|
let index = 0;
|
|
|
|
|
let updatedChildren: LayerChild[] = [];
|
|
|
|
|
for (const layer of flexLayers) {
|
|
|
|
|
const children = layer.children || [];
|
|
|
|
|
if (!children.length) continue;
|
|
|
|
|
const childIndex = children.findIndex(
|
|
|
|
|
(each: LayerChild) => each.id === widgetId,
|
|
|
|
|
);
|
|
|
|
|
if (childIndex === -1) {
|
|
|
|
|
index += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updatedChildren = children.filter(
|
|
|
|
|
(each: LayerChild) => each.id !== widgetId,
|
|
|
|
|
);
|
|
|
|
|
layerIndex = index;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (layerIndex === -1) return widgets;
|
|
|
|
|
|
|
|
|
|
flexLayers = [
|
|
|
|
|
...flexLayers.slice(0, layerIndex),
|
|
|
|
|
{
|
|
|
|
|
children: updatedChildren,
|
|
|
|
|
},
|
|
|
|
|
...flexLayers.slice(layerIndex + 1),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
parent = {
|
|
|
|
|
...parent,
|
|
|
|
|
flexLayers: flexLayers.filter(
|
|
|
|
|
(layer: FlexLayer) => layer?.children?.length,
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
widgets[parentId] = parent;
|
|
|
|
|
|
2023-04-07 13:51:35 +00:00
|
|
|
return updatePositionsOfParentAndSiblings(
|
|
|
|
|
widgets,
|
|
|
|
|
parentId,
|
|
|
|
|
layerIndex,
|
|
|
|
|
isMobile,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function alterLayoutForMobile(
|
|
|
|
|
allWidgets: CanvasWidgetsReduxState,
|
|
|
|
|
parentId: string,
|
|
|
|
|
canvasWidth: number,
|
2023-04-07 13:51:35 +00:00
|
|
|
mainCanvasWidth: number,
|
|
|
|
|
firstTimeDSLUpdate = false,
|
2023-03-04 07:25:54 +00:00
|
|
|
): CanvasWidgetsReduxState {
|
|
|
|
|
let widgets = { ...allWidgets };
|
|
|
|
|
const parent = widgets[parentId];
|
|
|
|
|
const children = parent.children;
|
|
|
|
|
|
|
|
|
|
if (!isStack(allWidgets, parent)) {
|
|
|
|
|
return widgets;
|
|
|
|
|
}
|
|
|
|
|
if (!children || !children.length) return widgets;
|
|
|
|
|
|
|
|
|
|
for (const child of children) {
|
|
|
|
|
const widget = { ...widgets[child] };
|
2023-04-07 13:51:35 +00:00
|
|
|
const { minWidth } = getWidgetMinMaxDimensionsInPixel(
|
|
|
|
|
widget,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
if (widget.responsiveBehavior === ResponsiveBehavior.Fill) {
|
|
|
|
|
widget.mobileRightColumn = GridDefaults.DEFAULT_GRID_COLUMNS;
|
|
|
|
|
widget.mobileLeftColumn = 0;
|
2023-04-07 13:51:35 +00:00
|
|
|
} else if (minWidth) {
|
|
|
|
|
const { leftColumn, rightColumn } = widget;
|
2023-03-04 07:25:54 +00:00
|
|
|
const columnSpace =
|
|
|
|
|
(canvasWidth - FLEXBOX_PADDING * 2) / GridDefaults.DEFAULT_GRID_COLUMNS;
|
2023-04-07 13:51:35 +00:00
|
|
|
if (columnSpace * (rightColumn - leftColumn) < minWidth) {
|
2023-03-04 07:25:54 +00:00
|
|
|
widget.mobileLeftColumn = 0;
|
|
|
|
|
widget.mobileRightColumn = Math.min(
|
|
|
|
|
minWidth / columnSpace,
|
|
|
|
|
GridDefaults.DEFAULT_GRID_COLUMNS,
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-04-07 13:51:35 +00:00
|
|
|
} else {
|
|
|
|
|
widget.mobileLeftColumn = widget.leftColumn;
|
|
|
|
|
widget.mobileRightColumn = widget.rightColumn;
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
widget.mobileTopRow === undefined ||
|
|
|
|
|
widget.mobileBottomRow === undefined ||
|
|
|
|
|
widget.mobileTopRow + widget.mobileBottomRow === 0
|
|
|
|
|
) {
|
|
|
|
|
widget.mobileTopRow = widget.topRow;
|
|
|
|
|
widget.mobileBottomRow = widget.bottomRow;
|
2023-03-04 07:25:54 +00:00
|
|
|
}
|
|
|
|
|
widgets = alterLayoutForMobile(
|
|
|
|
|
widgets,
|
|
|
|
|
child,
|
|
|
|
|
(canvasWidth * (widget.mobileRightColumn || 1)) /
|
|
|
|
|
GridDefaults.DEFAULT_GRID_COLUMNS,
|
2023-04-07 13:51:35 +00:00
|
|
|
mainCanvasWidth,
|
2023-03-04 07:25:54 +00:00
|
|
|
);
|
|
|
|
|
widgets[child] = widget;
|
2023-04-07 13:51:35 +00:00
|
|
|
widgets = updateWidgetPositions(
|
|
|
|
|
widgets,
|
|
|
|
|
child,
|
|
|
|
|
true,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
firstTimeDSLUpdate,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
}
|
2023-04-07 13:51:35 +00:00
|
|
|
widgets = updateWidgetPositions(
|
|
|
|
|
widgets,
|
|
|
|
|
parentId,
|
|
|
|
|
true,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
firstTimeDSLUpdate,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
return widgets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function alterLayoutForDesktop(
|
|
|
|
|
allWidgets: CanvasWidgetsReduxState,
|
|
|
|
|
parentId: string,
|
2023-04-07 13:51:35 +00:00
|
|
|
mainCanvasWidth: number,
|
|
|
|
|
firstTimeDSLUpdate = false,
|
2023-03-04 07:25:54 +00:00
|
|
|
): CanvasWidgetsReduxState {
|
|
|
|
|
let widgets = { ...allWidgets };
|
|
|
|
|
const parent = widgets[parentId];
|
|
|
|
|
const children = parent.children;
|
|
|
|
|
|
2023-04-07 13:51:35 +00:00
|
|
|
if (!isStack(allWidgets, parent)) {
|
|
|
|
|
return widgets;
|
|
|
|
|
}
|
|
|
|
|
if (!children || !children.length) {
|
|
|
|
|
return widgets;
|
|
|
|
|
}
|
2023-03-04 07:25:54 +00:00
|
|
|
|
2023-04-07 13:51:35 +00:00
|
|
|
widgets = updateWidgetPositions(
|
|
|
|
|
widgets,
|
|
|
|
|
parentId,
|
|
|
|
|
false,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
firstTimeDSLUpdate,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
for (const child of children) {
|
2023-04-07 13:51:35 +00:00
|
|
|
widgets = alterLayoutForDesktop(
|
|
|
|
|
widgets,
|
|
|
|
|
child,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
firstTimeDSLUpdate,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
}
|
|
|
|
|
return widgets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* START: COPY PASTE UTILS
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
export function pasteWidgetInFlexLayers(
|
|
|
|
|
allWidgets: CanvasWidgetsReduxState,
|
|
|
|
|
parentId: string,
|
|
|
|
|
widget: any,
|
|
|
|
|
originalWidgetId: string,
|
|
|
|
|
isMobile: boolean,
|
2023-04-07 13:51:35 +00:00
|
|
|
mainCanvasWidth: number,
|
2023-03-04 07:25:54 +00:00
|
|
|
): CanvasWidgetsReduxState {
|
|
|
|
|
let widgets = { ...allWidgets };
|
|
|
|
|
const parent = widgets[parentId];
|
|
|
|
|
let flexLayers: FlexLayer[] = parent.flexLayers || [];
|
2023-04-07 13:51:35 +00:00
|
|
|
let flexLayerIndex = -1;
|
2023-03-04 07:25:54 +00:00
|
|
|
/**
|
|
|
|
|
* If the new parent is not the same as the original parent,
|
|
|
|
|
* then add a new flex layer.
|
|
|
|
|
*/
|
|
|
|
|
if (
|
|
|
|
|
!widgets[originalWidgetId] ||
|
|
|
|
|
widgets[originalWidgetId].parentId !== parentId
|
|
|
|
|
) {
|
|
|
|
|
flexLayers = [
|
|
|
|
|
...flexLayers,
|
|
|
|
|
{
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
id: widget.widgetId,
|
|
|
|
|
align: FlexLayerAlignment.Start,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
/**
|
|
|
|
|
* If the new parent is the same as the original parent,
|
|
|
|
|
* then update the flex layer.
|
|
|
|
|
*/
|
|
|
|
|
let rowIndex = -1,
|
|
|
|
|
alignment = FlexLayerAlignment.Start;
|
2023-04-07 13:51:35 +00:00
|
|
|
flexLayerIndex = flexLayers.findIndex((layer: FlexLayer) => {
|
2023-03-04 07:25:54 +00:00
|
|
|
const temp = layer.children.findIndex(
|
|
|
|
|
(child: LayerChild) => child.id === originalWidgetId,
|
|
|
|
|
);
|
|
|
|
|
if (temp > -1) {
|
|
|
|
|
rowIndex = temp;
|
|
|
|
|
alignment = layer.children[temp].align;
|
|
|
|
|
}
|
|
|
|
|
return temp > -1;
|
|
|
|
|
});
|
|
|
|
|
if (flexLayerIndex > -1 && rowIndex > -1) {
|
|
|
|
|
let selectedLayer = flexLayers[flexLayerIndex];
|
|
|
|
|
selectedLayer = {
|
|
|
|
|
children: [
|
|
|
|
|
...selectedLayer.children.slice(0, rowIndex + 1),
|
|
|
|
|
{ id: widget.widgetId, align: alignment },
|
|
|
|
|
...selectedLayer.children.slice(rowIndex + 1),
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
flexLayers = [
|
|
|
|
|
...flexLayers.slice(0, flexLayerIndex),
|
|
|
|
|
selectedLayer,
|
|
|
|
|
...flexLayers.slice(flexLayerIndex + 1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
widgets = {
|
|
|
|
|
...widgets,
|
|
|
|
|
[parentId]: {
|
|
|
|
|
...parent,
|
|
|
|
|
flexLayers,
|
|
|
|
|
},
|
|
|
|
|
};
|
2023-04-07 13:51:35 +00:00
|
|
|
return updatePositionsOfParentAndSiblings(
|
|
|
|
|
widgets,
|
|
|
|
|
parentId,
|
|
|
|
|
flexLayerIndex,
|
|
|
|
|
isMobile,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add nested children to flex layers of the new pasted canvas.
|
|
|
|
|
* The flexLayers get copied from the original canvas.
|
|
|
|
|
* This method matches the copied widgetId with the original widgetId
|
|
|
|
|
* and replaces them in position.
|
|
|
|
|
*/
|
|
|
|
|
export function addChildToPastedFlexLayers(
|
|
|
|
|
allWidgets: CanvasWidgetsReduxState,
|
|
|
|
|
widget: any,
|
|
|
|
|
widgetIdMap: Record<string, string>,
|
|
|
|
|
isMobile: boolean,
|
2023-04-07 13:51:35 +00:00
|
|
|
mainCanvasWidth: number,
|
2023-03-04 07:25:54 +00:00
|
|
|
): CanvasWidgetsReduxState {
|
|
|
|
|
let widgets = { ...allWidgets };
|
|
|
|
|
const parent = widgets[widget.parentId];
|
|
|
|
|
const flexLayers = parent.flexLayers || [];
|
|
|
|
|
if (flexLayers.length > 0) {
|
|
|
|
|
let index = 0;
|
|
|
|
|
for (const layer of flexLayers) {
|
|
|
|
|
let children = layer.children;
|
|
|
|
|
let childIndex = 0;
|
|
|
|
|
for (const child of children) {
|
|
|
|
|
if (widgetIdMap[child.id] === widget.widgetId) {
|
|
|
|
|
children = [
|
|
|
|
|
...children.slice(0, childIndex),
|
|
|
|
|
{ id: widget.widgetId, align: child.align },
|
|
|
|
|
...children.slice(childIndex + 1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
childIndex += 1;
|
|
|
|
|
}
|
|
|
|
|
flexLayers[index] = {
|
|
|
|
|
children,
|
|
|
|
|
};
|
|
|
|
|
index += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
widgets = {
|
|
|
|
|
...widgets,
|
|
|
|
|
[parent.widgetId]: {
|
|
|
|
|
...parent,
|
|
|
|
|
flexLayers,
|
|
|
|
|
},
|
|
|
|
|
};
|
2023-04-07 13:51:35 +00:00
|
|
|
return updateWidgetPositions(
|
|
|
|
|
widgets,
|
|
|
|
|
parent.widgetId,
|
|
|
|
|
isMobile,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
);
|
2023-03-04 07:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isStack(
|
|
|
|
|
allWidgets: CanvasWidgetsReduxState,
|
|
|
|
|
widget: any,
|
|
|
|
|
): boolean {
|
|
|
|
|
let parent = widget.parentId ? allWidgets[widget.parentId] : undefined;
|
|
|
|
|
if (parent && parent.type === "CANVAS_WIDGET" && parent.parentId)
|
|
|
|
|
parent = allWidgets[parent.parentId];
|
|
|
|
|
return (
|
|
|
|
|
widget.positioning === Positioning.Vertical ||
|
|
|
|
|
((parent && defaultAutoLayoutWidgets.includes(parent.type)) ||
|
|
|
|
|
parent?.widgetId === MAIN_CONTAINER_WIDGET_ID
|
|
|
|
|
? allWidgets[MAIN_CONTAINER_WIDGET_ID].positioning ===
|
|
|
|
|
Positioning.Vertical
|
|
|
|
|
: false)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* END: copy paste utils
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
export function getLayerIndexOfWidget(
|
|
|
|
|
flexLayers: FlexLayer[],
|
|
|
|
|
widgetId: string,
|
|
|
|
|
): number {
|
|
|
|
|
if (!flexLayers) return -1;
|
|
|
|
|
return flexLayers.findIndex((layer: FlexLayer) => {
|
|
|
|
|
return (
|
|
|
|
|
layer.children.findIndex((child: LayerChild) => child.id === widgetId) !==
|
|
|
|
|
-1
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-04-07 13:51:35 +00:00
|
|
|
|
|
|
|
|
export function getViewportClassName(viewportWidth: number) {
|
|
|
|
|
if (viewportWidth > layoutConfigurations.MOBILE.maxWidth) {
|
|
|
|
|
return "desktop-view";
|
|
|
|
|
} else {
|
|
|
|
|
return "mobile-view";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-04 07:25:54 +00:00
|
|
|
export function getFillWidgetLengthForLayer(
|
|
|
|
|
layer: any,
|
|
|
|
|
allWidgets: any,
|
2023-04-07 13:51:35 +00:00
|
|
|
dimensionMap = DefaultDimensionMap,
|
2023-03-04 07:25:54 +00:00
|
|
|
): number | undefined {
|
|
|
|
|
let fillLength = GridDefaults.DEFAULT_GRID_COLUMNS;
|
|
|
|
|
let hugLength = 0,
|
|
|
|
|
fillCount = 0;
|
2023-04-07 13:51:35 +00:00
|
|
|
const { leftColumn: leftColumnMap, rightColumn: rightColumnMap } =
|
|
|
|
|
dimensionMap;
|
2023-03-04 07:25:54 +00:00
|
|
|
for (const child of layer.children) {
|
|
|
|
|
const childWidget = allWidgets[child.id];
|
|
|
|
|
if (!childWidget) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (childWidget.responsiveBehavior !== ResponsiveBehavior.Fill) {
|
2023-04-07 13:51:35 +00:00
|
|
|
hugLength += childWidget[rightColumnMap] - childWidget[leftColumnMap];
|
2023-03-04 07:25:54 +00:00
|
|
|
} else {
|
|
|
|
|
fillCount += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fillLength = (fillLength - hugLength) / (fillCount || 1);
|
|
|
|
|
return fillLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getAlignmentColumnInfo(
|
|
|
|
|
widgets: CanvasWidgetsReduxState,
|
|
|
|
|
layer: FlexLayer,
|
|
|
|
|
isMobile: boolean,
|
|
|
|
|
): AlignmentColumnInfo {
|
|
|
|
|
if (!layer)
|
|
|
|
|
return {
|
|
|
|
|
[FlexLayerAlignment.Start]: 0,
|
|
|
|
|
[FlexLayerAlignment.Center]: 0,
|
|
|
|
|
[FlexLayerAlignment.End]: 0,
|
2023-04-07 13:51:35 +00:00
|
|
|
[FlexLayerAlignment.None]: 0,
|
2023-03-04 07:25:54 +00:00
|
|
|
};
|
|
|
|
|
let start = 0,
|
|
|
|
|
end = 0,
|
|
|
|
|
center = 0;
|
|
|
|
|
for (const child of layer.children) {
|
|
|
|
|
const widget = widgets[child.id];
|
|
|
|
|
if (!widget) continue;
|
|
|
|
|
if (child.align === FlexLayerAlignment.End)
|
|
|
|
|
end += getWidgetWidth(widget, isMobile);
|
|
|
|
|
else if (child.align === FlexLayerAlignment.Center)
|
|
|
|
|
center += getWidgetWidth(widget, isMobile);
|
|
|
|
|
else start += getWidgetWidth(widget, isMobile);
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
[FlexLayerAlignment.Start]: start,
|
|
|
|
|
[FlexLayerAlignment.Center]: center,
|
|
|
|
|
[FlexLayerAlignment.End]: end,
|
2023-04-07 13:51:35 +00:00
|
|
|
[FlexLayerAlignment.None]: 0,
|
2023-03-04 07:25:54 +00:00
|
|
|
};
|
|
|
|
|
}
|
2023-04-07 13:51:35 +00:00
|
|
|
|
|
|
|
|
export function getCanvasDimensions(
|
|
|
|
|
canvas: FlattenedWidgetProps,
|
|
|
|
|
widgets: CanvasWidgetsReduxState,
|
|
|
|
|
mainCanvasWidth: number,
|
|
|
|
|
isMobile: boolean,
|
|
|
|
|
): { canvasWidth: number; columnSpace: number } {
|
|
|
|
|
const canvasWidth: number = getCanvasWidth(
|
|
|
|
|
canvas,
|
|
|
|
|
widgets,
|
|
|
|
|
mainCanvasWidth,
|
|
|
|
|
isMobile,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const columnSpace: number = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS;
|
|
|
|
|
|
|
|
|
|
return { canvasWidth: canvasWidth, columnSpace };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCanvasWidth(
|
|
|
|
|
canvas: FlattenedWidgetProps,
|
|
|
|
|
widgets: CanvasWidgetsReduxState,
|
|
|
|
|
mainCanvasWidth: number,
|
|
|
|
|
isMobile: boolean,
|
|
|
|
|
): number {
|
|
|
|
|
if (!mainCanvasWidth) return 0;
|
|
|
|
|
if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID)
|
|
|
|
|
return mainCanvasWidth - getPadding(canvas);
|
|
|
|
|
|
|
|
|
|
const stack = [];
|
|
|
|
|
let widget = canvas;
|
|
|
|
|
while (widget.parentId) {
|
|
|
|
|
stack.push(widget);
|
|
|
|
|
widget = widgets[widget.parentId];
|
|
|
|
|
|
|
|
|
|
//stop at modal
|
|
|
|
|
if (widget.type === "MODAL_WIDGET") {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stack.push(widget);
|
|
|
|
|
|
|
|
|
|
let width = mainCanvasWidth;
|
|
|
|
|
|
|
|
|
|
//modal will be the total width instead of the mainCanvasWidth
|
|
|
|
|
if (widget.type === "MODAL_WIDGET") {
|
|
|
|
|
width = widget.width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (stack.length) {
|
|
|
|
|
const widget = stack.pop();
|
|
|
|
|
if (!widget) continue;
|
|
|
|
|
const columns = getWidgetWidth(widget, isMobile);
|
|
|
|
|
const padding = getPadding(widget);
|
|
|
|
|
const factor = widget.detachFromLayout
|
|
|
|
|
? 1
|
|
|
|
|
: columns / GridDefaults.DEFAULT_GRID_COLUMNS;
|
|
|
|
|
width = width * factor - padding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getPadding(canvas: FlattenedWidgetProps): number {
|
|
|
|
|
let padding = 0;
|
|
|
|
|
if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) {
|
|
|
|
|
padding = FLEXBOX_PADDING * 2;
|
|
|
|
|
} else if (canvas.type === "CONTAINER_WIDGET") {
|
2023-04-26 17:39:11 +00:00
|
|
|
padding = (AUTO_LAYOUT_CONTAINER_PADDING + FLEXBOX_PADDING) * 2;
|
2023-04-07 13:51:35 +00:00
|
|
|
} else if (canvas.isCanvas) {
|
2023-04-26 17:39:11 +00:00
|
|
|
padding = AUTO_LAYOUT_CONTAINER_PADDING * 2;
|
2023-04-07 13:51:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (canvas.noPad) {
|
|
|
|
|
padding -= WIDGET_PADDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return padding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This method preserves the flexLayers of the parent canvas,
|
|
|
|
|
* but only for the selected widgets
|
|
|
|
|
* @param selectedWidgets
|
|
|
|
|
* @param parentCanvas
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
export function getFlexLayersForSelectedWidgets(
|
|
|
|
|
selectedWidgets: string[],
|
|
|
|
|
parentCanvas: FlattenedWidgetProps | undefined,
|
|
|
|
|
): FlexLayer[] {
|
|
|
|
|
if (
|
|
|
|
|
!parentCanvas ||
|
|
|
|
|
!parentCanvas.flexLayers ||
|
|
|
|
|
parentCanvas.flexLayers.length <= 0
|
|
|
|
|
)
|
|
|
|
|
return [];
|
|
|
|
|
|
|
|
|
|
const currFlexLayers: FlexLayer[] = parentCanvas.flexLayers;
|
|
|
|
|
|
|
|
|
|
const selectedFlexLayers: FlexLayer[] = [];
|
|
|
|
|
|
|
|
|
|
for (const flexLayer of currFlexLayers) {
|
|
|
|
|
const layerChildren = [];
|
|
|
|
|
|
|
|
|
|
for (const layerChild of flexLayer.children) {
|
|
|
|
|
if (selectedWidgets.indexOf(layerChild.id) > -1) {
|
|
|
|
|
layerChildren.push(layerChild);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (layerChildren.length > 0) {
|
|
|
|
|
selectedFlexLayers.push({ children: layerChildren });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selectedFlexLayers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This method helps in Converting the widgetId inside flexLayers
|
|
|
|
|
* to the new corresponding widgetIds in widgetIdMap
|
|
|
|
|
* @param flexLayers
|
|
|
|
|
* @param widgetIdMap
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
export function getNewFlexLayers(
|
|
|
|
|
flexLayers: FlexLayer[],
|
|
|
|
|
widgetIdMap: Record<string, string>,
|
|
|
|
|
) {
|
|
|
|
|
const newFlexLayers: FlexLayer[] = [];
|
|
|
|
|
|
|
|
|
|
for (const flexLayer of flexLayers) {
|
|
|
|
|
const newChildren = [];
|
|
|
|
|
|
|
|
|
|
for (const layerChild of flexLayer.children) {
|
|
|
|
|
if (widgetIdMap[layerChild.id]) {
|
|
|
|
|
newChildren.push({
|
|
|
|
|
id: widgetIdMap[layerChild.id],
|
|
|
|
|
align: layerChild.align,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newFlexLayers.push({ children: newChildren });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newFlexLayers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function checkIsDSLAutoLayout(dsl: DSLWidget): boolean {
|
|
|
|
|
return dsl.useAutoLayout && dsl.positioning === Positioning.Vertical;
|
|
|
|
|
}
|
2023-04-20 05:14:37 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find out which alignment is placed in which row upon flex wrap.
|
|
|
|
|
*
|
|
|
|
|
* In case of flex wrap,
|
|
|
|
|
* - alignments within a FlexLayer are placed in multiple rows.
|
|
|
|
|
* Logic:
|
|
|
|
|
* - for each alignment in arr
|
|
|
|
|
* - if alignment.columns < 64
|
|
|
|
|
* - add it to the current row (res[resIndex])
|
|
|
|
|
* - and track the total occupied columns in this row (total)
|
|
|
|
|
* - else
|
|
|
|
|
* - add the current row to the output rows
|
|
|
|
|
* - and start a new row to repeat the process recursively.
|
|
|
|
|
* @param arr | AlignmentColumnData[]: array of alignment and its columns.
|
|
|
|
|
* @param res | FlexLayerAlignment[][]: array of rows of alignments.
|
|
|
|
|
* @param resIndex | number: index of the current row.
|
|
|
|
|
* @returns FlexLayerAlignment[][]
|
|
|
|
|
*/
|
|
|
|
|
export function getLayerWrappingInfo(
|
|
|
|
|
arr: AlignmentColumnData[],
|
|
|
|
|
res: FlexLayerAlignment[][] = [[], [], []],
|
|
|
|
|
resIndex = 0,
|
|
|
|
|
): FlexLayerAlignment[][] {
|
|
|
|
|
if (arr.length === 0) return res;
|
|
|
|
|
if (arr.length === 1) {
|
|
|
|
|
res[resIndex].push(arr[0].alignment);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
let index = 0;
|
|
|
|
|
let total = 0;
|
|
|
|
|
for (const each of arr) {
|
|
|
|
|
if (total + each.columns > GridDefaults.DEFAULT_GRID_COLUMNS) {
|
|
|
|
|
let x = index;
|
|
|
|
|
if (!res[resIndex].length) {
|
|
|
|
|
res[resIndex].push(arr[0].alignment);
|
|
|
|
|
x += 1;
|
|
|
|
|
}
|
|
|
|
|
return getLayerWrappingInfo(arr.slice(x), res, resIndex + 1);
|
|
|
|
|
}
|
|
|
|
|
total += each.columns;
|
|
|
|
|
index += 1;
|
|
|
|
|
res[resIndex].push(each.alignment);
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* If a layer is flex wrapped, then individual sub-wrappers will have bottom margins, except the last sub-wrapper.
|
|
|
|
|
* @param arr | AlignmentColumnData[]: array of alignment and its columns.
|
|
|
|
|
* @param res | FlexLayerAlignment[][]: array of rows of alignments.
|
|
|
|
|
* @param resIndex | number: index of the current row.
|
|
|
|
|
* @returns boolean[]
|
|
|
|
|
*/
|
|
|
|
|
export function getAlignmentMarginInfo(
|
|
|
|
|
arr: AlignmentColumnData[],
|
|
|
|
|
res: FlexLayerAlignment[][] = [[], [], []],
|
|
|
|
|
resIndex = 0,
|
|
|
|
|
): boolean[] {
|
|
|
|
|
if (!arr.length) return [];
|
|
|
|
|
const wrapInfo: FlexLayerAlignment[][] = getLayerWrappingInfo(
|
|
|
|
|
arr,
|
|
|
|
|
res,
|
|
|
|
|
resIndex,
|
|
|
|
|
);
|
|
|
|
|
const marginInfo: {
|
|
|
|
|
[key: string]: (arr: AlignmentColumnData[]) => boolean[];
|
|
|
|
|
} = {
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
|
"300": (arr): boolean[] => [false, false, false],
|
|
|
|
|
"120": (arr): boolean[] => [
|
|
|
|
|
arr[0].columns > 0 && arr[1].columns + arr[2].columns > 0,
|
|
|
|
|
false,
|
|
|
|
|
false,
|
|
|
|
|
],
|
|
|
|
|
"210": (arr): boolean[] => [
|
|
|
|
|
arr[0].columns > 0 && arr[2].columns > 0,
|
|
|
|
|
arr[1].columns > 0 && arr[2].columns > 0,
|
|
|
|
|
false,
|
|
|
|
|
],
|
|
|
|
|
"111": (arr): boolean[] => [
|
|
|
|
|
arr[0].columns > 0,
|
|
|
|
|
arr[1].columns > 0 && arr[2].columns > 0,
|
|
|
|
|
false,
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return marginInfo[wrapInfo.map((x) => x.length).join("")](arr);
|
|
|
|
|
}
|