PromucFlow_constructor/app/client/src/utils/autoLayout/AutoLayoutUtils.ts

687 lines
18 KiB
TypeScript
Raw Normal View History

feat: Vertical alignment and row gaps for Auto Layout (#21888) ## Description - Defined the default vertical alignment of widgets within a row. Now, all widgets will align bottom except container widget which will align top - Added a predefined row gap of 12px in the case of desktop and 8px in the case of mobile viewport. Fixes #22227 Fixes #22228 ## Type of change - New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Manual - Drag n drop multiple widgets, verified all the widgets except container widget are bottom aligned and container widget is top aligned - Checked if the row gap is 12px in the case of non-mobile viewport and it is 8px in the case of mobile viewport - Checked if the highlights are shown in the proper position - Tested some templates after conversion ### 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 - [x] 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 - [x] New and existing unit tests pass locally with my changes - [x] 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: rahulramesha <rahul@appsmith.com> Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: Arsalan <arsalanyaldram0211@outlook.com> Co-authored-by: Preet <preetsidhu.bits@gmail.com>
2023-04-20 05:14:37 +00:00
import type {
AlignmentColumnData,
FlexLayer,
LayerChild,
} from "./autoLayoutTypes";
import {
FLEXBOX_PADDING,
layoutConfigurations,
GridDefaults,
MAIN_CONTAINER_WIDGET_ID,
WIDGET_PADDING,
CONTAINER_GRID_PADDING,
DefaultDimensionMap,
} from "constants/WidgetConstants";
import type {
CanvasWidgetsReduxState,
FlattenedWidgetProps,
} from "reducers/entityReducers/canvasWidgetsReducer";
import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer";
import {
defaultAutoLayoutWidgets,
FlexLayerAlignment,
Positioning,
ResponsiveBehavior,
} from "utils/autoLayout/constants";
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";
import {
getWidgetMinMaxDimensionsInPixel,
getWidgetWidth,
} from "./flexWidgetUtils";
import type { DSLWidget } from "widgets/constants";
export function updateFlexLayersOnDelete(
allWidgets: CanvasWidgetsReduxState,
widgetId: string,
parentId: string,
isMobile: boolean,
mainCanvasWidth: number,
): 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;
return updatePositionsOfParentAndSiblings(
widgets,
parentId,
layerIndex,
isMobile,
mainCanvasWidth,
);
}
export function alterLayoutForMobile(
allWidgets: CanvasWidgetsReduxState,
parentId: string,
canvasWidth: number,
mainCanvasWidth: number,
firstTimeDSLUpdate = false,
): 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] };
const { minWidth } = getWidgetMinMaxDimensionsInPixel(
widget,
mainCanvasWidth,
);
if (widget.responsiveBehavior === ResponsiveBehavior.Fill) {
widget.mobileRightColumn = GridDefaults.DEFAULT_GRID_COLUMNS;
widget.mobileLeftColumn = 0;
} else if (minWidth) {
const { leftColumn, rightColumn } = widget;
const columnSpace =
(canvasWidth - FLEXBOX_PADDING * 2) / GridDefaults.DEFAULT_GRID_COLUMNS;
if (columnSpace * (rightColumn - leftColumn) < minWidth) {
widget.mobileLeftColumn = 0;
widget.mobileRightColumn = Math.min(
minWidth / columnSpace,
GridDefaults.DEFAULT_GRID_COLUMNS,
);
}
} 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;
}
widgets = alterLayoutForMobile(
widgets,
child,
(canvasWidth * (widget.mobileRightColumn || 1)) /
GridDefaults.DEFAULT_GRID_COLUMNS,
mainCanvasWidth,
);
widgets[child] = widget;
widgets = updateWidgetPositions(
widgets,
child,
true,
mainCanvasWidth,
firstTimeDSLUpdate,
);
}
widgets = updateWidgetPositions(
widgets,
parentId,
true,
mainCanvasWidth,
firstTimeDSLUpdate,
);
return widgets;
}
export function alterLayoutForDesktop(
allWidgets: CanvasWidgetsReduxState,
parentId: string,
mainCanvasWidth: number,
firstTimeDSLUpdate = false,
): CanvasWidgetsReduxState {
let widgets = { ...allWidgets };
const parent = widgets[parentId];
const children = parent.children;
if (!isStack(allWidgets, parent)) {
return widgets;
}
if (!children || !children.length) {
return widgets;
}
widgets = updateWidgetPositions(
widgets,
parentId,
false,
mainCanvasWidth,
firstTimeDSLUpdate,
);
for (const child of children) {
widgets = alterLayoutForDesktop(
widgets,
child,
mainCanvasWidth,
firstTimeDSLUpdate,
);
}
return widgets;
}
/**
* START: COPY PASTE UTILS
*/
export function pasteWidgetInFlexLayers(
allWidgets: CanvasWidgetsReduxState,
parentId: string,
widget: any,
originalWidgetId: string,
isMobile: boolean,
mainCanvasWidth: number,
): CanvasWidgetsReduxState {
let widgets = { ...allWidgets };
const parent = widgets[parentId];
let flexLayers: FlexLayer[] = parent.flexLayers || [];
let flexLayerIndex = -1;
/**
* 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;
flexLayerIndex = flexLayers.findIndex((layer: FlexLayer) => {
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,
},
};
return updatePositionsOfParentAndSiblings(
widgets,
parentId,
flexLayerIndex,
isMobile,
mainCanvasWidth,
);
}
/**
* 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,
mainCanvasWidth: number,
): 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,
},
};
return updateWidgetPositions(
widgets,
parent.widgetId,
isMobile,
mainCanvasWidth,
);
}
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
);
});
}
export function getViewportClassName(viewportWidth: number) {
if (viewportWidth > layoutConfigurations.MOBILE.maxWidth) {
return "desktop-view";
} else {
return "mobile-view";
}
}
export function getFillWidgetLengthForLayer(
layer: any,
allWidgets: any,
dimensionMap = DefaultDimensionMap,
): number | undefined {
let fillLength = GridDefaults.DEFAULT_GRID_COLUMNS;
let hugLength = 0,
fillCount = 0;
const { leftColumn: leftColumnMap, rightColumn: rightColumnMap } =
dimensionMap;
for (const child of layer.children) {
const childWidget = allWidgets[child.id];
if (!childWidget) {
continue;
}
if (childWidget.responsiveBehavior !== ResponsiveBehavior.Fill) {
hugLength += childWidget[rightColumnMap] - childWidget[leftColumnMap];
} 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,
[FlexLayerAlignment.None]: 0,
};
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,
[FlexLayerAlignment.None]: 0,
};
}
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") {
padding = (CONTAINER_GRID_PADDING + FLEXBOX_PADDING) * 2;
} else if (canvas.isCanvas) {
padding = CONTAINER_GRID_PADDING * 2;
}
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;
}
feat: Vertical alignment and row gaps for Auto Layout (#21888) ## Description - Defined the default vertical alignment of widgets within a row. Now, all widgets will align bottom except container widget which will align top - Added a predefined row gap of 12px in the case of desktop and 8px in the case of mobile viewport. Fixes #22227 Fixes #22228 ## Type of change - New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Manual - Drag n drop multiple widgets, verified all the widgets except container widget are bottom aligned and container widget is top aligned - Checked if the row gap is 12px in the case of non-mobile viewport and it is 8px in the case of mobile viewport - Checked if the highlights are shown in the proper position - Tested some templates after conversion ### 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 - [x] 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 - [x] New and existing unit tests pass locally with my changes - [x] 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: rahulramesha <rahul@appsmith.com> Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: Arsalan <arsalanyaldram0211@outlook.com> Co-authored-by: Preet <preetsidhu.bits@gmail.com>
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);
}