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

351 lines
9.5 KiB
TypeScript
Raw Normal View History

import { FlexLayer, LayerChild } from "./autoLayoutTypes";
import {
FLEXBOX_PADDING,
GridDefaults,
MAIN_CONTAINER_WIDGET_ID,
} from "constants/WidgetConstants";
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer";
import {
defaultAutoLayoutWidgets,
FlexLayerAlignment,
Positioning,
ResponsiveBehavior,
} from "utils/autoLayout/constants";
import { updateWidgetPositions } from "utils/autoLayout/positionUtils";
import { AlignmentColumnInfo } from "./autoLayoutTypes";
import { getWidgetWidth } from "./flexWidgetUtils";
export function updateFlexLayersOnDelete(
allWidgets: CanvasWidgetsReduxState,
widgetId: string,
parentId: string,
isMobile?: boolean,
): 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 updateWidgetPositions(widgets, parentId, isMobile);
}
export function alterLayoutForMobile(
allWidgets: CanvasWidgetsReduxState,
parentId: string,
canvasWidth: number,
): 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] };
if (widget.responsiveBehavior === ResponsiveBehavior.Fill) {
widget.mobileRightColumn = GridDefaults.DEFAULT_GRID_COLUMNS;
widget.mobileLeftColumn = 0;
} else if (
widget.responsiveBehavior === ResponsiveBehavior.Hug &&
widget.minWidth
) {
const { minWidth, rightColumn } = widget;
const columnSpace =
(canvasWidth - FLEXBOX_PADDING * 2) / GridDefaults.DEFAULT_GRID_COLUMNS;
if (columnSpace * rightColumn < minWidth) {
widget.mobileLeftColumn = 0;
widget.mobileRightColumn = Math.min(
minWidth / columnSpace,
GridDefaults.DEFAULT_GRID_COLUMNS,
);
}
}
widget.mobileTopRow = widget.topRow;
widget.mobileBottomRow = widget.bottomRow;
widgets = alterLayoutForMobile(
widgets,
child,
(canvasWidth * (widget.mobileRightColumn || 1)) /
GridDefaults.DEFAULT_GRID_COLUMNS,
);
widgets[child] = widget;
widgets = updateWidgetPositions(widgets, child, true);
}
widgets = updateWidgetPositions(widgets, parentId, true);
return widgets;
}
export function alterLayoutForDesktop(
allWidgets: CanvasWidgetsReduxState,
parentId: string,
): 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);
for (const child of children) {
widgets = alterLayoutForDesktop(widgets, child);
}
return widgets;
}
/**
* START: COPY PASTE UTILS
*/
export function pasteWidgetInFlexLayers(
allWidgets: CanvasWidgetsReduxState,
parentId: string,
widget: any,
originalWidgetId: string,
isMobile: boolean,
): CanvasWidgetsReduxState {
let widgets = { ...allWidgets };
const parent = widgets[parentId];
let flexLayers: FlexLayer[] = parent.flexLayers || [];
/**
* 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;
const 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 updateWidgetPositions(widgets, parentId, isMobile);
}
/**
* 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,
): 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);
}
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 getFillWidgetLengthForLayer(
layer: any,
allWidgets: any,
): number | undefined {
let fillLength = GridDefaults.DEFAULT_GRID_COLUMNS;
let hugLength = 0,
fillCount = 0;
for (const child of layer.children) {
const childWidget = allWidgets[child.id];
if (!childWidget) {
continue;
}
if (childWidget.responsiveBehavior !== ResponsiveBehavior.Fill) {
hugLength += childWidget.rightColumn - childWidget.leftColumn;
} 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,
};
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,
};
}