PromucFlow_constructor/app/client/src/utils/WidgetPropsUtils.tsx

325 lines
8.5 KiB
TypeScript
Raw Normal View History

2019-11-25 05:07:27 +00:00
import { FetchPageResponse } from "api/PageApi";
2020-01-16 11:46:21 +00:00
import {
CANVAS_DEFAULT_WIDTH_PX,
CANVAS_DEFAULT_HEIGHT_PX,
CANVAS_BACKGROUND_COLOR,
CANVAS_DEFAULT_GRID_HEIGHT_PX,
CANVAS_DEFAULT_GRID_WIDTH_PX,
} from "constants/AppConstants";
import { XYCoord } from "react-dnd";
2019-11-25 05:07:27 +00:00
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
import {
WidgetProps,
WidgetOperations,
WidgetOperation,
2019-11-25 05:07:27 +00:00
} from "widgets/BaseWidget";
2020-01-17 09:28:26 +00:00
import { WidgetType } from "constants/WidgetConstants";
2019-11-25 05:07:27 +00:00
import { generateReactKey } from "utils/generators";
2020-01-16 11:46:21 +00:00
import {
GridDefaults,
WidgetTypes,
MAIN_CONTAINER_WIDGET_ID,
MAIN_CONTAINER_WIDGET_NAME,
2020-01-17 09:27:38 +00:00
CONTAINER_GRID_PADDING,
2020-01-16 11:46:21 +00:00
} from "constants/WidgetConstants";
import { snapToGrid } from "./helpers";
import { OccupiedSpace } from "constants/editorConstants";
2020-01-17 09:28:26 +00:00
import { DerivedPropFactory } from "utils/DerivedPropertiesFactory";
export type WidgetOperationParams = {
operation: WidgetOperation;
widgetId: string;
payload: any;
};
2020-01-16 11:46:21 +00:00
const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROW_HEIGHT } = GridDefaults;
2019-09-25 21:21:04 +00:00
type Rect = {
top: number;
left: number;
right: number;
bottom: number;
};
2019-09-26 11:11:28 +00:00
const defaultDSL = {
2020-01-16 11:46:21 +00:00
type: WidgetTypes.CONTAINER_WIDGET,
widgetId: MAIN_CONTAINER_WIDGET_ID,
widgetName: MAIN_CONTAINER_WIDGET_NAME,
backgroundColor: CANVAS_BACKGROUND_COLOR,
2019-09-26 11:11:28 +00:00
children: [],
2020-01-16 11:46:21 +00:00
2019-09-26 11:11:28 +00:00
leftColumn: 0,
2020-01-16 11:46:21 +00:00
rightColumn: CANVAS_DEFAULT_WIDTH_PX,
parentColumnSpace: CANVAS_DEFAULT_GRID_WIDTH_PX,
snapColumns: GridDefaults.DEFAULT_GRID_COLUMNS,
2019-09-26 11:11:28 +00:00
topRow: 0,
2020-01-16 11:46:21 +00:00
bottomRow: CANVAS_DEFAULT_HEIGHT_PX,
parentRowSpace: CANVAS_DEFAULT_GRID_HEIGHT_PX,
// 1 row needs to be removed, as padding top and bottom takes up some 1 row worth of space.
// Widget padding: 8px
// Container padding: 12px;
// Total = (8 + 12) * 2 = GridDefaults.DEFAULT_GRID_ROW_HEIGHT = 40
snapRows: CANVAS_DEFAULT_HEIGHT_PX / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
2019-09-26 11:11:28 +00:00
};
export const extractCurrentDSL = (
fetchPageResponse: FetchPageResponse,
): ContainerWidgetProps<WidgetProps> => {
const currentDSL = fetchPageResponse.data.layouts[0].dsl || defaultDSL;
2020-01-16 11:46:21 +00:00
// 1 row needs to be removed, as padding top and bottom takes up some 1 row worth of space.
// Widget padding: 8px
// Container padding: 12px;
// Total = (8 + 12) * 2 = GridDefaults.DEFAULT_GRID_ROW_HEIGHT = 40
currentDSL.snapRows =
Math.floor(currentDSL.bottomRow / DEFAULT_GRID_ROW_HEIGHT) - 1;
return currentDSL;
};
2019-09-25 21:21:04 +00:00
export const getDropZoneOffsets = (
colWidth: number,
rowHeight: number,
dragOffset: XYCoord,
parentOffset: XYCoord,
) => {
// Calculate actual drop position by snapping based on x, y and grid cell size
return snapToGrid(
colWidth,
rowHeight,
2020-01-17 09:27:38 +00:00
dragOffset.x - parentOffset.x - CONTAINER_GRID_PADDING,
dragOffset.y - parentOffset.y - CONTAINER_GRID_PADDING,
2019-09-25 21:21:04 +00:00
);
};
const areIntersecting = (r1: Rect, r2: Rect) => {
return !(
r2.left >= r1.right ||
r2.right <= r1.left ||
r2.top >= r1.bottom ||
r2.bottom <= r1.top
2019-09-25 21:21:04 +00:00
);
};
export const isDropZoneOccupied = (
offset: Rect,
2019-10-08 06:19:10 +00:00
widgetId: string,
occupied?: OccupiedSpace[],
2019-09-25 21:21:04 +00:00
) => {
if (occupied) {
occupied = occupied.filter(widgetDetails => {
2019-10-02 21:14:58 +00:00
return (
2019-10-08 06:19:10 +00:00
widgetDetails.id !== widgetId && widgetDetails.parentId !== widgetId
2019-10-02 21:14:58 +00:00
);
2019-09-25 21:21:04 +00:00
});
for (let i = 0; i < occupied.length; i++) {
if (areIntersecting(occupied[i], offset)) {
return true;
}
}
return false;
}
return false;
};
2019-10-08 06:19:10 +00:00
export const isWidgetOverflowingParentBounds = (
parentRowCols: { rows?: number; cols?: number },
offset: Rect,
2020-01-16 11:46:21 +00:00
): boolean => {
const result =
offset.right < 0 ||
offset.top < 0 ||
2019-10-08 06:19:10 +00:00
(parentRowCols.cols || GridDefaults.DEFAULT_GRID_COLUMNS) < offset.right ||
2020-01-16 11:46:21 +00:00
(parentRowCols.rows || 0) < offset.bottom;
return result;
2019-10-08 06:19:10 +00:00
};
2019-09-25 21:21:04 +00:00
export const noCollision = (
clientOffset: XYCoord,
colWidth: number,
rowHeight: number,
widget: WidgetProps & Partial<WidgetConfigProps>,
dropTargetOffset: XYCoord,
occupiedSpaces?: OccupiedSpace[],
2019-10-08 06:19:10 +00:00
rows?: number,
cols?: number,
2019-09-25 21:21:04 +00:00
): boolean => {
if (clientOffset && dropTargetOffset && widget) {
const [left, top] = getDropZoneOffsets(
colWidth,
rowHeight,
clientOffset as XYCoord,
dropTargetOffset,
);
2020-01-16 11:46:21 +00:00
if (left < 0 || top < 0) {
return false;
}
2019-09-25 21:21:04 +00:00
const widgetWidth = widget.columns
? widget.columns
: widget.rightColumn - widget.leftColumn;
const widgetHeight = widget.rows
? widget.rows
: widget.bottomRow - widget.topRow;
const currentOffset = {
left,
right: left + widgetWidth,
top,
bottom: top + widgetHeight,
};
2019-10-08 06:19:10 +00:00
return (
!isDropZoneOccupied(currentOffset, widget.widgetId, occupiedSpaces) &&
!isWidgetOverflowingParentBounds({ rows, cols }, currentOffset)
);
2019-09-25 21:21:04 +00:00
}
return false;
};
2020-01-16 11:46:21 +00:00
export const currentDropRow = (
dropTargetRowSpace: number,
dropTargetVerticalOffset: number,
draggableItemVerticalOffset: number,
widget: WidgetProps & Partial<WidgetConfigProps>,
) => {
const widgetHeight = widget.rows
? widget.rows
: widget.bottomRow - widget.topRow;
const top = Math.round(
(draggableItemVerticalOffset - dropTargetVerticalOffset) /
dropTargetRowSpace,
);
const currentBottomOffset = top + widgetHeight;
return currentBottomOffset;
};
export const widgetOperationParams = (
widget: WidgetProps & Partial<WidgetConfigProps>,
widgetOffset: XYCoord,
parentOffset: XYCoord,
parentColumnSpace: number,
parentRowSpace: number,
parentWidgetId: string, // parentWidget
): WidgetOperationParams => {
const [leftColumn, topRow] = getDropZoneOffsets(
parentColumnSpace,
parentRowSpace,
widgetOffset,
parentOffset,
);
// If this is an existing widget, we'll have the widgetId
// Therefore, this is a move operation on drop of the widget
if (widget.widgetId) {
return {
operation: WidgetOperations.MOVE,
widgetId: widget.widgetId,
payload: {
leftColumn,
topRow,
parentId: widget.parentId,
newParentId: parentWidgetId,
},
};
// If this is not an existing widget, we'll not have the widgetId
// Therefore, this is an operation to add child to this container
}
const widgetDimensions = {
columns: widget.columns,
rows: widget.rows,
};
return {
operation: WidgetOperations.ADD_CHILD,
widgetId: parentWidgetId,
payload: {
type: widget.type,
newWidgetId: generateReactKey(),
leftColumn,
topRow,
...widgetDimensions,
parentRowSpace,
parentColumnSpace,
},
};
};
export const updateWidgetPosition = (
widget: WidgetProps,
leftColumn: number,
topRow: number,
) => {
const newPositions = {
leftColumn,
topRow,
rightColumn: leftColumn + (widget.rightColumn - widget.leftColumn),
bottomRow: topRow + (widget.bottomRow - widget.topRow),
};
2020-01-16 11:46:21 +00:00
if (widget.type === WidgetTypes.CONTAINER_WIDGET) {
widget.snapRows = newPositions.bottomRow - newPositions.topRow - 1;
}
return {
...widget,
2020-01-16 11:46:21 +00:00
...newPositions,
};
};
export const generateWidgetProps = (
parent: ContainerWidgetProps<WidgetProps>,
type: WidgetType,
leftColumn: number,
topRow: number,
columns: number,
rows: number,
parentRowSpace: number,
parentColumnSpace: number,
widgetName: string,
widgetConfig: Partial<WidgetProps>,
): Partial<ContainerWidgetProps<WidgetProps>> => {
if (parent && parent.snapColumns && parent.snapRows) {
const sizes = {
leftColumn,
rightColumn: leftColumn + columns,
topRow,
bottomRow: topRow + rows,
};
let others = {};
if (type === WidgetTypes.CONTAINER_WIDGET) {
others = {
snapColumns: DEFAULT_GRID_COLUMNS,
2020-01-16 11:46:21 +00:00
snapRows: rows - 1,
orientation: "VERTICAL",
children: [],
};
}
2020-01-17 09:28:26 +00:00
const derivedProperties = DerivedPropFactory.getDerivedPropertiesOfWidgetType(
type,
widgetName,
);
const dynamicBindings: Record<string, true> = {};
Object.keys(derivedProperties).forEach(prop => {
dynamicBindings[prop] = true;
});
return {
...widgetConfig,
type,
2020-01-17 09:28:26 +00:00
widgetName,
isVisible: true,
2019-11-20 08:10:01 +00:00
isLoading: false,
parentColumnSpace,
parentRowSpace,
2020-01-17 09:28:26 +00:00
dynamicBindings,
...sizes,
...others,
2020-01-17 09:28:26 +00:00
...derivedProperties,
};
} else {
2020-01-16 11:46:21 +00:00
if (parent) {
throw Error("Failed to create widget: Parent's size cannot be calculate");
2020-01-16 11:46:21 +00:00
} else throw Error("Failed to create widget: Parent was not provided ");
}
};
export default {
extractCurrentDSL,
generateWidgetProps,
};