2022-01-13 13:21:57 +00:00
|
|
|
import { reflowMoveAction, stopReflowAction } from "actions/reflowActions";
|
|
|
|
|
import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants";
|
|
|
|
|
import { isEmpty, throttle } from "lodash";
|
2022-04-01 14:57:03 +00:00
|
|
|
import { useEffect, useRef } from "react";
|
2022-01-13 13:21:57 +00:00
|
|
|
import { useDispatch, useSelector } from "react-redux";
|
2022-08-19 10:10:36 +00:00
|
|
|
import { getContainerWidgetSpacesSelectorWhileMoving } from "selectors/editorSelectors";
|
2022-01-13 13:21:57 +00:00
|
|
|
import { reflow } from "reflow";
|
|
|
|
|
import {
|
|
|
|
|
CollidingSpace,
|
2022-04-01 14:57:03 +00:00
|
|
|
CollidingSpaceMap,
|
2022-01-13 13:21:57 +00:00
|
|
|
GridProps,
|
2022-04-01 14:57:03 +00:00
|
|
|
MovementLimitMap,
|
|
|
|
|
PrevReflowState,
|
2022-01-13 13:21:57 +00:00
|
|
|
ReflowDirection,
|
|
|
|
|
ReflowedSpaceMap,
|
2022-04-01 14:57:03 +00:00
|
|
|
SecondOrderCollisionMap,
|
2022-01-13 13:21:57 +00:00
|
|
|
} from "reflow/reflowTypes";
|
2022-04-01 14:57:03 +00:00
|
|
|
import {
|
|
|
|
|
getBottomMostRow,
|
|
|
|
|
getLimitedMovementMap,
|
|
|
|
|
getSpacesMapFromArray,
|
|
|
|
|
} from "reflow/reflowUtils";
|
2022-01-13 13:21:57 +00:00
|
|
|
import { getBottomRowAfterReflow } from "utils/reflowHookUtils";
|
|
|
|
|
import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer";
|
2022-04-01 14:57:03 +00:00
|
|
|
import { getIsReflowing } from "selectors/widgetReflowSelectors";
|
|
|
|
|
import { AppState } from "reducers";
|
2022-04-13 14:57:44 +00:00
|
|
|
import { areIntersecting } from "utils/WidgetPropsUtils";
|
2022-01-13 13:21:57 +00:00
|
|
|
|
|
|
|
|
type WidgetCollidingSpace = CollidingSpace & {
|
|
|
|
|
type: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type WidgetCollidingSpaceMap = {
|
2022-04-01 14:57:03 +00:00
|
|
|
horizontal: WidgetCollisionMap;
|
|
|
|
|
vertical: WidgetCollisionMap;
|
|
|
|
|
};
|
|
|
|
|
export type WidgetCollisionMap = {
|
2022-01-13 13:21:57 +00:00
|
|
|
[key: string]: WidgetCollidingSpace;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export interface ReflowInterface {
|
|
|
|
|
(
|
2022-04-01 14:57:03 +00:00
|
|
|
newPositions: OccupiedSpace[],
|
2022-01-13 13:21:57 +00:00
|
|
|
direction: ReflowDirection,
|
|
|
|
|
stopMoveAfterLimit?: boolean,
|
|
|
|
|
shouldSkipContainerReflow?: boolean,
|
|
|
|
|
forceDirection?: boolean,
|
|
|
|
|
immediateExitContainer?: string,
|
2022-04-13 14:57:44 +00:00
|
|
|
mousePosition?: OccupiedSpace,
|
2022-01-13 13:21:57 +00:00
|
|
|
): {
|
2022-04-01 14:57:03 +00:00
|
|
|
movementLimitMap?: MovementLimitMap;
|
2022-01-13 13:21:57 +00:00
|
|
|
movementMap: ReflowedSpaceMap;
|
|
|
|
|
bottomMostRow: number;
|
2022-04-13 14:57:44 +00:00
|
|
|
isIdealToJumpContainer: boolean;
|
2022-01-13 13:21:57 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const useReflow = (
|
2022-04-01 14:57:03 +00:00
|
|
|
OGPositions: OccupiedSpace[],
|
2022-01-13 13:21:57 +00:00
|
|
|
parentId: string,
|
|
|
|
|
gridProps: GridProps,
|
|
|
|
|
): ReflowInterface => {
|
|
|
|
|
const dispatch = useDispatch();
|
2022-04-01 14:57:03 +00:00
|
|
|
const isReflowingGlobal = useSelector(getIsReflowing);
|
|
|
|
|
const isDragging = useSelector(
|
|
|
|
|
(state: AppState) => state.ui.widgetDragResize.isDragging,
|
|
|
|
|
);
|
2022-01-13 13:21:57 +00:00
|
|
|
|
|
|
|
|
const throttledDispatch = throttle(dispatch, 50);
|
|
|
|
|
|
|
|
|
|
const isReflowing = useRef<boolean>(false);
|
|
|
|
|
|
2022-08-19 10:10:36 +00:00
|
|
|
const reflowSpacesSelector = getContainerWidgetSpacesSelectorWhileMoving(
|
|
|
|
|
parentId,
|
|
|
|
|
);
|
2022-01-13 13:21:57 +00:00
|
|
|
const widgetSpaces: WidgetSpace[] = useSelector(reflowSpacesSelector) || [];
|
|
|
|
|
|
2022-04-01 14:57:03 +00:00
|
|
|
const prevPositions = useRef<OccupiedSpace[] | undefined>(OGPositions);
|
2022-01-13 13:21:57 +00:00
|
|
|
const prevCollidingSpaces = useRef<WidgetCollidingSpaceMap>();
|
|
|
|
|
const prevMovementMap = useRef<ReflowedSpaceMap>({});
|
2022-04-01 14:57:03 +00:00
|
|
|
const prevSecondOrderCollisionMap = useRef<SecondOrderCollisionMap>({});
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
//only have it run when the user has completely stopped dragging and stopped Reflowing
|
|
|
|
|
if (!isReflowingGlobal && !isDragging) {
|
|
|
|
|
isReflowing.current = false;
|
|
|
|
|
prevPositions.current = [...OGPositions];
|
|
|
|
|
prevCollidingSpaces.current = { horizontal: {}, vertical: {} };
|
|
|
|
|
prevMovementMap.current = {};
|
|
|
|
|
prevSecondOrderCollisionMap.current = {};
|
|
|
|
|
}
|
|
|
|
|
}, [isReflowingGlobal, isDragging]);
|
|
|
|
|
|
2022-01-13 13:21:57 +00:00
|
|
|
// will become a state if we decide that resize should be a "toggle on-demand" feature
|
|
|
|
|
const shouldResize = true;
|
|
|
|
|
return function reflowSpaces(
|
2022-04-01 14:57:03 +00:00
|
|
|
newPositions: OccupiedSpace[],
|
2022-01-13 13:21:57 +00:00
|
|
|
direction: ReflowDirection,
|
|
|
|
|
stopMoveAfterLimit = false,
|
|
|
|
|
shouldSkipContainerReflow = false,
|
|
|
|
|
forceDirection = false,
|
|
|
|
|
immediateExitContainer?: string,
|
2022-04-13 14:57:44 +00:00
|
|
|
mousePosition?: OccupiedSpace,
|
2022-01-13 13:21:57 +00:00
|
|
|
) {
|
2022-04-01 14:57:03 +00:00
|
|
|
const prevReflowState: PrevReflowState = {
|
|
|
|
|
prevSpacesMap: getSpacesMapFromArray(prevPositions.current),
|
|
|
|
|
prevCollidingSpaceMap: prevCollidingSpaces.current as CollidingSpaceMap,
|
|
|
|
|
prevMovementMap: prevMovementMap.current,
|
|
|
|
|
prevSecondOrderCollisionMap: prevSecondOrderCollisionMap.current,
|
|
|
|
|
};
|
|
|
|
|
|
2022-04-13 14:57:44 +00:00
|
|
|
// To track container jumps
|
|
|
|
|
let isIdealToJumpContainer = false;
|
|
|
|
|
|
2022-04-01 14:57:03 +00:00
|
|
|
const {
|
|
|
|
|
collidingSpaceMap,
|
|
|
|
|
movementLimitMap,
|
|
|
|
|
movementMap,
|
|
|
|
|
secondOrderCollisionMap,
|
|
|
|
|
} = reflow(
|
2022-01-13 13:21:57 +00:00
|
|
|
newPositions,
|
|
|
|
|
OGPositions,
|
|
|
|
|
widgetSpaces,
|
|
|
|
|
direction,
|
|
|
|
|
gridProps,
|
|
|
|
|
forceDirection,
|
|
|
|
|
shouldResize,
|
2022-04-01 14:57:03 +00:00
|
|
|
prevReflowState,
|
2022-01-13 13:21:57 +00:00
|
|
|
immediateExitContainer,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
prevPositions.current = newPositions;
|
|
|
|
|
prevCollidingSpaces.current = collidingSpaceMap as WidgetCollidingSpaceMap;
|
2022-04-01 14:57:03 +00:00
|
|
|
prevSecondOrderCollisionMap.current = secondOrderCollisionMap || {};
|
2022-01-13 13:21:57 +00:00
|
|
|
|
|
|
|
|
let correctedMovementMap = movementMap || {};
|
|
|
|
|
|
|
|
|
|
if (stopMoveAfterLimit)
|
|
|
|
|
correctedMovementMap = getLimitedMovementMap(
|
|
|
|
|
movementMap,
|
|
|
|
|
prevMovementMap.current,
|
2022-04-01 14:57:03 +00:00
|
|
|
{ canHorizontalMove: true, canVerticalMove: true },
|
2022-01-13 13:21:57 +00:00
|
|
|
);
|
|
|
|
|
|
2022-04-01 14:57:03 +00:00
|
|
|
if (shouldSkipContainerReflow && collidingSpaceMap) {
|
|
|
|
|
const collidingSpaces = [
|
|
|
|
|
...Object.values(collidingSpaceMap.horizontal),
|
|
|
|
|
...Object.values(collidingSpaceMap.vertical),
|
|
|
|
|
] as WidgetCollidingSpace[];
|
|
|
|
|
|
2022-01-13 13:21:57 +00:00
|
|
|
for (const collidingSpace of collidingSpaces) {
|
2022-04-13 14:57:44 +00:00
|
|
|
if (
|
|
|
|
|
checkIsDropTarget(collidingSpace.type) &&
|
|
|
|
|
mousePosition &&
|
|
|
|
|
areIntersecting(mousePosition, collidingSpace)
|
|
|
|
|
) {
|
|
|
|
|
isIdealToJumpContainer = true;
|
2022-01-13 13:21:57 +00:00
|
|
|
correctedMovementMap = {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prevMovementMap.current = correctedMovementMap;
|
|
|
|
|
|
|
|
|
|
if (!isEmpty(correctedMovementMap)) {
|
|
|
|
|
isReflowing.current = true;
|
|
|
|
|
if (forceDirection) dispatch(reflowMoveAction(correctedMovementMap));
|
|
|
|
|
else throttledDispatch(reflowMoveAction(correctedMovementMap));
|
|
|
|
|
} else if (isReflowing.current) {
|
|
|
|
|
isReflowing.current = false;
|
|
|
|
|
throttledDispatch.cancel();
|
|
|
|
|
dispatch(stopReflowAction());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bottomMostRow = getBottomRowAfterReflow(
|
|
|
|
|
movementMap,
|
2022-04-01 14:57:03 +00:00
|
|
|
getBottomMostRow(newPositions),
|
2022-01-13 13:21:57 +00:00
|
|
|
widgetSpaces,
|
|
|
|
|
gridProps,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
2022-04-01 14:57:03 +00:00
|
|
|
movementLimitMap,
|
2022-01-13 13:21:57 +00:00
|
|
|
movementMap: correctedMovementMap,
|
|
|
|
|
bottomMostRow,
|
2022-04-13 14:57:44 +00:00
|
|
|
isIdealToJumpContainer,
|
2022-01-13 13:21:57 +00:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|