PromucFlow_constructor/app/client/src/utils/hooks/useReflow.ts
Ashok Kumar M 0149085bf8
feat: Reflow and Resize while Dragging and Resizing widgets. (#9054)
* resize n reflow rough cut

* removing warnings

* relatively stable changes

* minor bug fix

* reflow relative collision

* working dp cut

* fix for reflow of widgets closer next to each other

* disabling scroll

* Drag with reflow

* reflow fix

* overlap and retracing fix

* On Drop updates.

* bug when no displacement but resize update.

* temp fix for new widget addition.

* reflow bug fixes

* new widget addition bug.

* stop reflow on leave.

* fix corner case overlap

* update bottom row when reflowed widgets go beyond bottom boundary.

* capture mouse positions on enter

* enable container jumps with faster mouse movements.

* reflow only for snap changes.

* restructured reflow Algorithm

* collision check and bug fixes

* undo redo fix for new widget drop

* resizable fix snapRows fix

* directional stability

* self collision fix

* first round of perf fixes

* update bottom row while resizing and resize-reflowing

* performance fix and overlapping fix

* Remove eslint warning

* remove eslint warning

* eslint warning

* can reflowed Drop Indication Stability

* container jumps and force direction on entering canvas

* fixing scroll on resize jitters.

* reflow when jumping into container.

* reflow ux fixes while leaving container

* resizing fixes.

* fixes for edge move.

* restrict container jumps into reflowed containers.

* container jump direction reflow

* checkbox dimensions fix.

* Excess bottom rows not lost post dragging or resizing widgets.

* fixing the after drop css glitch.

* double first move trigger bug fix.

* stop reflow only if reflowing

* stabilize container exit directions

* using acceleration and speed instead of movement covered to restrict reflow.

* fixing modal drops.

* remove warnings.

* reflow resize styles

* moving acceleration and movement logic to a monitoring effect.

* adding beta flag for reflow.

* fixing jest tests

* Adding analytics to beta flag toggle.

* Adding placeholder for reflow beta screens.

* fixing initial load's screen

* few more crashes.

* force close onboarding for the session.

* fixing bugs in reset canvas.

* Beta flag bug fixes.

* fixing bugs.

* restrict reflow screens during onboarding.

* disabling reflow screens in tests.

* code review comments.

* fixing store based specs.

* fixing cypress failures.

* fixing specs.

* code cleanup

* reverting yarn lock changes

* removing onboarding screens.

* more cleanup and function descriptors

* keeping reflow under the hood.

Co-authored-by: rahulramesha <rahul@appsmith.com>
Co-authored-by: Arpit Mohan <arpit@appsmith.com>
2022-01-13 18:51:57 +05:30

139 lines
3.9 KiB
TypeScript

import { reflowMoveAction, stopReflowAction } from "actions/reflowActions";
import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants";
import { isEmpty, throttle } from "lodash";
import { useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getWidgetSpacesSelectorForContainer } from "selectors/editorSelectors";
import { reflow } from "reflow";
import {
CollidingSpace,
GridProps,
ReflowDirection,
ReflowedSpaceMap,
} from "reflow/reflowTypes";
import { getLimitedMovementMap } from "reflow/reflowUtils";
import { getBottomRowAfterReflow } from "utils/reflowHookUtils";
import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer";
type WidgetCollidingSpace = CollidingSpace & {
type: string;
};
type WidgetCollidingSpaceMap = {
[key: string]: WidgetCollidingSpace;
};
export interface ReflowInterface {
(
newPositions: OccupiedSpace,
OGPositions: OccupiedSpace,
direction: ReflowDirection,
stopMoveAfterLimit?: boolean,
shouldSkipContainerReflow?: boolean,
forceDirection?: boolean,
immediateExitContainer?: string,
): {
canHorizontalMove: boolean;
canVerticalMove: boolean;
movementMap: ReflowedSpaceMap;
bottomMostRow: number;
};
}
export const useReflow = (
widgetId: string,
parentId: string,
gridProps: GridProps,
): ReflowInterface => {
const dispatch = useDispatch();
const throttledDispatch = throttle(dispatch, 50);
const isReflowing = useRef<boolean>(false);
const reflowSpacesSelector = getWidgetSpacesSelectorForContainer(parentId);
const widgetSpaces: WidgetSpace[] = useSelector(reflowSpacesSelector) || [];
const originalSpacePosition = widgetSpaces?.find(
(space) => space.id === widgetId,
);
const prevPositions = useRef<OccupiedSpace | undefined>(
originalSpacePosition,
);
const prevCollidingSpaces = useRef<WidgetCollidingSpaceMap>();
const prevMovementMap = useRef<ReflowedSpaceMap>({});
// will become a state if we decide that resize should be a "toggle on-demand" feature
const shouldResize = true;
return function reflowSpaces(
newPositions: OccupiedSpace,
OGPositions: OccupiedSpace,
direction: ReflowDirection,
stopMoveAfterLimit = false,
shouldSkipContainerReflow = false,
forceDirection = false,
immediateExitContainer?: string,
) {
const { collidingSpaceMap, movementLimit, movementMap } = reflow(
newPositions,
OGPositions,
widgetSpaces,
direction,
gridProps,
forceDirection,
shouldResize,
immediateExitContainer,
prevPositions.current,
prevCollidingSpaces.current,
);
prevPositions.current = newPositions;
prevCollidingSpaces.current = collidingSpaceMap as WidgetCollidingSpaceMap;
let correctedMovementMap = movementMap || {};
if (stopMoveAfterLimit)
correctedMovementMap = getLimitedMovementMap(
movementMap,
prevMovementMap.current,
movementLimit,
);
if (shouldSkipContainerReflow) {
const collidingSpaces = Object.values(
(collidingSpaceMap as WidgetCollidingSpaceMap) || {},
);
for (const collidingSpace of collidingSpaces) {
if (checkIsDropTarget(collidingSpace.type)) {
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,
newPositions.bottom,
widgetSpaces,
gridProps,
);
return {
...movementLimit,
movementMap: correctedMovementMap,
bottomMostRow,
};
};
};