## Description - Update Storybook and related dependencies - Delete the stories for old widget components - Rewrite stories for new widgets(mdx to tsx) Note: local chromatic doesn't work because of this https://github.com/storybookjs/storybook/issues/22531 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced new component stories across various packages to enhance design system documentation and user experience. - **Documentation** - Updated `.gitignore` files to optimize version control settings for Storybook and build logs. - **Refactor** - Modified UI element positioning in Storybook theming for improved layout management. - **Style** - Adjusted CSS properties for the `Select` component to ensure better visual presentation. - **Chores** - Updated dependencies and scripts in package configurations to maintain up-to-date development tools. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
982 lines
35 KiB
TypeScript
982 lines
35 KiB
TypeScript
import type { OccupiedSpace } from "constants/CanvasEditorConstants";
|
|
import { GridDefaults } from "constants/WidgetConstants";
|
|
import { isEmpty } from "lodash";
|
|
import type {
|
|
CollidingSpace,
|
|
CollisionAccessors,
|
|
CollisionMap,
|
|
CollisionTree,
|
|
CollisionTreeCache,
|
|
Delta,
|
|
DirectionalMovement,
|
|
DirectionalVariables,
|
|
GridProps,
|
|
PrevReflowState,
|
|
ReflowedSpaceMap,
|
|
SecondOrderCollisionMap,
|
|
SpaceMap,
|
|
SpaceMovementMap,
|
|
} from "./reflowTypes";
|
|
import {
|
|
HORIZONTAL_RESIZE_MIN_LIMIT,
|
|
ReflowDirection,
|
|
VERTICAL_RESIZE_MIN_LIMIT,
|
|
} from "./reflowTypes";
|
|
import {
|
|
checkReCollisionWithOtherNewSpacePositions,
|
|
filterCommonSpaces,
|
|
buildArrayToCollisionMap,
|
|
getAccessor,
|
|
getCollidingSpacesInDirection,
|
|
getMaxX,
|
|
getMaxY,
|
|
getModifiedOccupiedSpacesMap,
|
|
getReflowDistance,
|
|
getReflowedDimension,
|
|
getResizedDimensions,
|
|
shouldReplaceOldMovement,
|
|
sortCollidingSpacesByDistance,
|
|
getModifiedCollidingSpace,
|
|
checkProcessNodeForTree,
|
|
getRelativeCollidingValue,
|
|
} from "./reflowUtils";
|
|
|
|
/**
|
|
* returns movement map of all the cascading colliding spaces
|
|
* Movement map has X, Y, width and height of each reflowed spaces in absolute values
|
|
*
|
|
* @param newSpacePositions new/current positions array of the space/block
|
|
* @param newSpacePositionsMap new/current positions map of the space/block
|
|
* @param occupiedSpaces array of all the occupied spaces on the canvas
|
|
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
|
|
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
|
|
* @param collidingSpaces array of Colliding spaces of the dragging/resizing space
|
|
* @param collidingSpaceMap Map of Colliding spaces of the dragging/resizing space
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param delta X and Y coordinate displacement of the newPosition from the original position
|
|
* @param shouldResize boolean to indicate if colliding spaces should resize
|
|
* @param globalDirection ReflowDirection, direction of reflow
|
|
* @param globalIsHorizontal boolean to identify if the current orientation is horizontal
|
|
* @param prevReflowState this contains a map of reference to the key values of previous reflow method call to back trace widget movements
|
|
* @param primaryMovementMap movement map/information from previous run of the algorithm
|
|
* @returns movement map of all the cascading colliding spaces
|
|
*/
|
|
export function getMovementMap(
|
|
newSpacePositions: OccupiedSpace[],
|
|
newSpacePositionsMap: SpaceMap,
|
|
occupiedSpaces: OccupiedSpace[],
|
|
occupiedSpacesMap: SpaceMap,
|
|
OGOccupiedSpacesMap: SpaceMap,
|
|
collidingSpaces: CollidingSpace[],
|
|
collidingSpaceMap: CollisionMap,
|
|
gridProps: GridProps,
|
|
delta = { X: 0, Y: 0 },
|
|
shouldResize = true,
|
|
globalDirection: ReflowDirection,
|
|
globalIsHorizontal: boolean,
|
|
prevReflowState: PrevReflowState,
|
|
primaryMovementMap?: ReflowedSpaceMap,
|
|
primarySecondOrderCollisionMap?: SecondOrderCollisionMap,
|
|
) {
|
|
const movementMap: ReflowedSpaceMap = { ...primaryMovementMap };
|
|
//create a tree structure of collision
|
|
const { collisionTrees, secondOrderCollisionMap } = getCollisionTree(
|
|
newSpacePositions,
|
|
occupiedSpaces,
|
|
occupiedSpacesMap,
|
|
OGOccupiedSpacesMap,
|
|
collidingSpaces,
|
|
globalDirection,
|
|
globalIsHorizontal,
|
|
collidingSpaceMap,
|
|
gridProps,
|
|
primaryMovementMap || prevReflowState.prevMovementMap,
|
|
prevReflowState,
|
|
!!primaryMovementMap,
|
|
primarySecondOrderCollisionMap,
|
|
);
|
|
|
|
if (!collisionTrees || collisionTrees.length <= 0) {
|
|
return {};
|
|
}
|
|
|
|
const directionalVariables: DirectionalVariables = {};
|
|
|
|
//globalProcessedNodes are the global cache to not generate the collision tree for spaces processed unnecessarily
|
|
//this is done for the sake of performance
|
|
const globalProcessedNodes: CollisionTreeCache = {};
|
|
|
|
//solve the tree structure to get the movement values
|
|
for (let i = 0; i < collisionTrees.length; i++) {
|
|
const childNode = collisionTrees[i];
|
|
const childDirection = childNode.direction;
|
|
const directionalAccessors = getAccessor(childDirection);
|
|
|
|
const distanceBeforeCollision =
|
|
childNode[directionalAccessors.oppositeDirection] -
|
|
childNode.collidingValue;
|
|
|
|
const { occupiedLength, occupiedSpace } = getMovementMapHelper(
|
|
childNode,
|
|
movementMap,
|
|
delta,
|
|
gridProps,
|
|
childDirection,
|
|
directionalAccessors,
|
|
childNode[directionalAccessors.direction],
|
|
distanceBeforeCollision,
|
|
collisionTrees,
|
|
i,
|
|
0,
|
|
shouldResize,
|
|
globalProcessedNodes,
|
|
);
|
|
|
|
let staticOccupiedLength = 0,
|
|
maxOccupiedSpace = 0;
|
|
if (!directionalVariables[childNode.collidingId]) {
|
|
directionalVariables[childNode.collidingId] = {};
|
|
}
|
|
if (directionalVariables[childNode.collidingId][childDirection]) {
|
|
[staticOccupiedLength, maxOccupiedSpace] =
|
|
directionalVariables[childNode.collidingId][childDirection];
|
|
}
|
|
staticOccupiedLength = Math.max(staticOccupiedLength, occupiedLength);
|
|
maxOccupiedSpace = Math.max(maxOccupiedSpace, occupiedSpace);
|
|
directionalVariables[childNode.collidingId][childDirection] = [
|
|
staticOccupiedLength,
|
|
maxOccupiedSpace,
|
|
directionalAccessors,
|
|
childDirection,
|
|
];
|
|
}
|
|
|
|
//based on the movement values and the occupiedLength of the dragging space from the borders, movement limits are calculated
|
|
const movementVariablesMap = getMovementVariables(
|
|
newSpacePositionsMap,
|
|
directionalVariables,
|
|
delta,
|
|
gridProps,
|
|
shouldResize,
|
|
);
|
|
|
|
return {
|
|
movementVariablesMap,
|
|
movementMap,
|
|
secondOrderCollisionMap,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Collision tree is an Object containing the spaces and it's colliding spaces in a tree form
|
|
* eg, if a spaces are colliding with root space, then they are added as children of root space.
|
|
* children spaces further check the spaces colliding with them and then add them as their children, so on
|
|
*
|
|
* @param newSpacePositions new/current positions array of the space/block
|
|
* @param occupiedSpaces array of all the occupied spaces on the canvas
|
|
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
|
|
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
|
|
* @param collidingSpaces array of Colliding spaces of the dragging/resizing space
|
|
* @param globalDirection ReflowDirection, direction of reflow
|
|
* @param globalIsHorizontal boolean to identify if the current orientation is horizontal
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param prevMovementMap is previous run's movement map if this is the first orientation, or is primary orientation's movement map if it is the second orientation run
|
|
* @param prevReflowState this contains a map of reference to the key values of previous reflow method call to back trace widget movements
|
|
* @param isSecondRun boolean to indicate if it is being run for the second time
|
|
* @returns array of collision Tree
|
|
*/
|
|
export function getCollisionTree(
|
|
newSpacePositions: OccupiedSpace[],
|
|
occupiedSpaces: OccupiedSpace[],
|
|
occupiedSpacesMap: SpaceMap,
|
|
OGOccupiedSpacesMap: SpaceMap,
|
|
collidingSpaces: CollidingSpace[],
|
|
globalDirection: ReflowDirection,
|
|
globalIsHorizontal: boolean,
|
|
collidingSpaceMap: CollisionMap,
|
|
gridProps: GridProps,
|
|
prevMovementMap: ReflowedSpaceMap,
|
|
prevReflowState: PrevReflowState,
|
|
isSecondRun: boolean,
|
|
primarySecondOrderCollisionMap?: SecondOrderCollisionMap,
|
|
) {
|
|
//To calculate the tree, you iterate over the directly colliding spaces
|
|
const collisionTrees: CollisionTree[] = [];
|
|
const secondOrderCollisionMap: SecondOrderCollisionMap = {
|
|
...primarySecondOrderCollisionMap,
|
|
};
|
|
|
|
//globalProcessedNodes are the global cache to not generate the collision tree for spaces processed unnecessarily
|
|
//this is done for the sake of performance
|
|
const globalProcessedNodes: CollisionTreeCache = {};
|
|
|
|
for (let i = 0; i < collidingSpaces.length; i++) {
|
|
const collidingSpace = collidingSpaces[i];
|
|
if (
|
|
checkProcessNodeForTree(collidingSpace, globalProcessedNodes)
|
|
.shouldProcessNode
|
|
) {
|
|
// This is required if we suddenly switch orientations, like from horizontal to vertical or vice versa
|
|
const {
|
|
currentAccessors,
|
|
currentCollidingSpace,
|
|
currentDirection,
|
|
currentOccSpaces,
|
|
currentOccSpacesMap,
|
|
} = getModifiedArgumentsForCollisionTree(
|
|
collidingSpace,
|
|
occupiedSpaces,
|
|
occupiedSpacesMap,
|
|
OGOccupiedSpacesMap,
|
|
ReflowDirection.UNSET,
|
|
globalIsHorizontal,
|
|
prevMovementMap,
|
|
gridProps,
|
|
);
|
|
|
|
// this method recursively builds the tree structure
|
|
const { collisionTree: currentCollisionTree, occupiedLength } =
|
|
getCollisionTreeHelper(
|
|
newSpacePositions,
|
|
currentOccSpaces,
|
|
currentOccSpacesMap,
|
|
OGOccupiedSpacesMap,
|
|
currentCollidingSpace,
|
|
globalDirection,
|
|
currentDirection,
|
|
currentAccessors,
|
|
collidingSpaces,
|
|
collidingSpaceMap,
|
|
gridProps,
|
|
i,
|
|
prevMovementMap,
|
|
prevReflowState,
|
|
true,
|
|
isSecondRun,
|
|
globalProcessedNodes,
|
|
secondOrderCollisionMap,
|
|
);
|
|
//To get colliding Value of the space relative to the Canvas edges
|
|
const relativeCollidingValue = getRelativeCollidingValue(
|
|
currentAccessors,
|
|
currentCollidingSpace.collidingValue,
|
|
currentDirection,
|
|
gridProps,
|
|
occupiedLength,
|
|
);
|
|
if (currentCollisionTree) {
|
|
collisionTrees.push({
|
|
...currentCollisionTree,
|
|
collidingValue: relativeCollidingValue,
|
|
collidingId: currentCollidingSpace.collidingId,
|
|
});
|
|
|
|
//initialize if undefined
|
|
if (!globalProcessedNodes[currentCollidingSpace.id]) {
|
|
globalProcessedNodes[currentCollidingSpace.id] = {};
|
|
}
|
|
//add value to cache
|
|
globalProcessedNodes[currentCollidingSpace.id][
|
|
currentCollidingSpace.direction
|
|
] = {
|
|
value: relativeCollidingValue,
|
|
childNode: {
|
|
...currentCollisionTree,
|
|
collidingValue: relativeCollidingValue,
|
|
collidingId: currentCollidingSpace.collidingId,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
return { collisionTrees, secondOrderCollisionMap };
|
|
}
|
|
|
|
/**
|
|
* generates a collision tree recursively
|
|
* if a occupiedSpaces are colliding with collidingSpace, then they are added as children of collidingSpace space.
|
|
* children spaces further check the spaces colliding with them and then add them as their children, so on
|
|
* This is done recursively
|
|
*
|
|
* @param newSpacePositions new/current positions array of the space/block
|
|
* @param occupiedSpaces array of all the occupied spaces on the canvas
|
|
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
|
|
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
|
|
* @param collidingSpace current colliding space of which collision tree is returned
|
|
* @param globalDirection ReflowDirection, global direction of reflow
|
|
* @param direction ReflowDirection, direction of reflow of the colliding space
|
|
* @param accessors accessors to access dimensions of spaces in a direction
|
|
* @param globalCollidingSpaces array of initial colliding widgets
|
|
* @param collidingSpaceMap Map of Colliding spaces of the dragging/resizing space
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param insertionIndex current index at which any new direct collision of new space positions will be added
|
|
* @param prevMovementMap is previous run's movement map if this is the first orientation, or is primary orientation's movement map if it is the second orientation run
|
|
* @param prevReflowState this contains a map of reference to the key values of previous reflow method call to back trace widget movements
|
|
* @param isDirectCollidingSpace boolean if the space is direct collision of the new space positions
|
|
* @param isSecondRun boolean to indicate if it is being run for the second time
|
|
* @param globalProcessedNodes cache to make sure to not generate a tree for the same space in the getCollision Tree
|
|
* @param secondOrderCollisionMap collision map of the direct collisions of the initial direct collisions
|
|
* @param processedNodes cache to make sure to not generate a tree for the same space of all the widgets below the colliding space
|
|
* @returns collision tree of a particular in a direction
|
|
*/
|
|
function getCollisionTreeHelper(
|
|
newSpacePositions: OccupiedSpace[],
|
|
occupiedSpaces: OccupiedSpace[],
|
|
occupiedSpacesMap: SpaceMap,
|
|
OGOccupiedSpacesMap: SpaceMap,
|
|
collidingSpace: CollidingSpace,
|
|
globalDirection: ReflowDirection,
|
|
direction: ReflowDirection,
|
|
accessors: CollisionAccessors,
|
|
globalCollidingSpaces: CollidingSpace[],
|
|
collidingSpaceMap: CollisionMap,
|
|
gridProps: GridProps,
|
|
insertionIndex: number,
|
|
prevMovementMap: ReflowedSpaceMap,
|
|
prevReflowState: PrevReflowState,
|
|
isDirectCollidingSpace: boolean,
|
|
isSecondRun: boolean,
|
|
globalProcessedNodes: CollisionTreeCache,
|
|
secondOrderCollisionMap?: SecondOrderCollisionMap,
|
|
) {
|
|
if (!collidingSpace) return {};
|
|
let occupiedLength = 0;
|
|
const collisionTree: CollisionTree = { ...collidingSpace, children: {} };
|
|
|
|
// we resize the space to either increase the width or height based on movement
|
|
// for the sake of finding all the colliding spaces
|
|
//for example, if a space is moved by 2 rows in the BOTTOM direction,
|
|
//then the space's bottom dimension is increased by 2 rows
|
|
const resizedDimensions = getResizedDimensions(collisionTree, accessors);
|
|
|
|
const filteredNewSpacePositions = newSpacePositions.filter(
|
|
(a) => a.id !== collidingSpace.collidingId,
|
|
);
|
|
|
|
// check if the space again collides with other dragging spaces in group widget scenario
|
|
if (
|
|
checkReCollisionWithOtherNewSpacePositions(
|
|
resizedDimensions,
|
|
collidingSpace,
|
|
globalDirection,
|
|
direction,
|
|
filteredNewSpacePositions,
|
|
globalCollidingSpaces,
|
|
insertionIndex,
|
|
globalProcessedNodes,
|
|
collidingSpaceMap,
|
|
prevReflowState,
|
|
isSecondRun,
|
|
)
|
|
)
|
|
return {};
|
|
|
|
// to get it's colliding spaces
|
|
const { collidingSpaces, occupiedSpacesInDirection } =
|
|
getCollidingSpacesInDirection(
|
|
resizedDimensions,
|
|
collidingSpace,
|
|
globalDirection,
|
|
direction,
|
|
gridProps,
|
|
prevReflowState,
|
|
collidingSpaceMap,
|
|
occupiedSpaces,
|
|
isDirectCollidingSpace,
|
|
);
|
|
|
|
if (isDirectCollidingSpace && secondOrderCollisionMap) {
|
|
//initialize if undefined
|
|
if (!secondOrderCollisionMap[collidingSpace.id]) {
|
|
secondOrderCollisionMap[collidingSpace.id] = {
|
|
...occupiedSpacesMap[collidingSpace.id],
|
|
children: {},
|
|
};
|
|
}
|
|
|
|
secondOrderCollisionMap[collidingSpace.id].children = {
|
|
...secondOrderCollisionMap[collidingSpace.id].children,
|
|
...buildArrayToCollisionMap(collidingSpaces),
|
|
};
|
|
}
|
|
|
|
sortCollidingSpacesByDistance(collidingSpaces);
|
|
|
|
for (const currentCollidingSpace of collidingSpaces) {
|
|
// If in case it changes orientation
|
|
const {
|
|
currentAccessors,
|
|
currentCollidingSpace: modifiedCollidingSpace,
|
|
currentDirection,
|
|
currentOccSpaces,
|
|
currentOccSpacesMap,
|
|
} = getModifiedArgumentsForCollisionTree(
|
|
currentCollidingSpace,
|
|
occupiedSpacesInDirection,
|
|
occupiedSpacesMap,
|
|
OGOccupiedSpacesMap,
|
|
direction,
|
|
accessors.isHorizontal,
|
|
prevMovementMap,
|
|
gridProps,
|
|
);
|
|
const { currentChildNode, shouldProcessNode } = checkProcessNodeForTree(
|
|
modifiedCollidingSpace,
|
|
globalProcessedNodes,
|
|
);
|
|
if (shouldProcessNode) {
|
|
//Recursively call to build the tree
|
|
const {
|
|
collisionTree: currentCollisionTree,
|
|
occupiedLength: currentOccupiedLength,
|
|
} = getCollisionTreeHelper(
|
|
filteredNewSpacePositions,
|
|
currentOccSpaces,
|
|
currentOccSpacesMap,
|
|
OGOccupiedSpacesMap,
|
|
modifiedCollidingSpace,
|
|
globalDirection,
|
|
currentDirection,
|
|
currentAccessors,
|
|
globalCollidingSpaces,
|
|
collidingSpaceMap,
|
|
gridProps,
|
|
insertionIndex,
|
|
prevMovementMap,
|
|
prevReflowState,
|
|
false,
|
|
isSecondRun,
|
|
globalProcessedNodes,
|
|
secondOrderCollisionMap,
|
|
);
|
|
|
|
//initialize if undefined
|
|
if (!globalProcessedNodes[modifiedCollidingSpace.id]) {
|
|
globalProcessedNodes[modifiedCollidingSpace.id] = {};
|
|
}
|
|
|
|
if (currentCollisionTree) {
|
|
//To get colliding Value of the space relative to the Canvas edges
|
|
const relativeCollidingValue = getRelativeCollidingValue(
|
|
currentAccessors,
|
|
modifiedCollidingSpace.collidingValue,
|
|
currentDirection,
|
|
gridProps,
|
|
currentOccupiedLength,
|
|
);
|
|
//add value to cache
|
|
globalProcessedNodes[modifiedCollidingSpace.id][
|
|
modifiedCollidingSpace.direction
|
|
] = {
|
|
value: relativeCollidingValue,
|
|
childNode: {
|
|
...currentCollisionTree,
|
|
},
|
|
};
|
|
|
|
if (collisionTree.children) {
|
|
collisionTree.children[currentCollidingSpace.id] = {
|
|
...currentCollisionTree,
|
|
};
|
|
}
|
|
}
|
|
//store overall maximum travel
|
|
if (currentOccupiedLength)
|
|
occupiedLength = Math.max(occupiedLength, currentOccupiedLength);
|
|
} else if (currentChildNode && collisionTree.children) {
|
|
collisionTree.children[currentChildNode.id] = {
|
|
...currentChildNode,
|
|
};
|
|
}
|
|
}
|
|
return {
|
|
collisionTree,
|
|
occupiedLength:
|
|
occupiedLength +
|
|
(accessors.isHorizontal
|
|
? HORIZONTAL_RESIZE_MIN_LIMIT
|
|
: collidingSpace.fixedHeight && accessors.directionIndicator < 0
|
|
? collidingSpace.fixedHeight
|
|
: VERTICAL_RESIZE_MIN_LIMIT),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* to get modified arguments of spaces is opposite orientation than the current orientation
|
|
* If while recursing through the collision tree and children is of different direction or orientation than the parent node,
|
|
* then the occupied spaces need to be modified to suit that orientation
|
|
*
|
|
* @param collidingSpace current colliding space of which collision tree is returned
|
|
* @param occupiedSpaces array of all the occupied spaces on the canvas
|
|
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
|
|
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
|
|
* @param direction ReflowDirection, direction of reflow of the colliding space
|
|
* @param isHorizontal boolean indicating if the current orientation is horizontal
|
|
* @param prevMovementMap movement map generate during the previous run
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param globalProcessedNodes cache to make sure to not generate a tree for the same space in the getCollision Tree
|
|
* @param currentProcessedNodes cache to make sure to not generate a tree for the same space of all the widgets below the colliding space
|
|
* @returns collision tree of a particular in a direction
|
|
*/
|
|
export function getModifiedArgumentsForCollisionTree(
|
|
collidingSpace: CollidingSpace,
|
|
occupiedSpaces: OccupiedSpace[],
|
|
occupiedSpacesMap: SpaceMap,
|
|
OGOccupiedSpacesMap: SpaceMap,
|
|
direction: ReflowDirection,
|
|
isHorizontal: boolean,
|
|
prevMovementMap: ReflowedSpaceMap,
|
|
gridProps: GridProps,
|
|
) {
|
|
const currentDirection = collidingSpace.direction;
|
|
const currentAccessors = getAccessor(currentDirection);
|
|
let currentOccSpaces = occupiedSpaces;
|
|
let currentOccSpacesMap = { ...occupiedSpacesMap };
|
|
// modify the collidingSpace position's values to be in the other orientation
|
|
let currentCollidingSpace = getModifiedCollidingSpace(
|
|
collidingSpace,
|
|
OGOccupiedSpacesMap,
|
|
prevMovementMap,
|
|
currentAccessors.isHorizontal,
|
|
gridProps,
|
|
currentAccessors.perpendicularMax,
|
|
currentAccessors.perpendicularMin,
|
|
);
|
|
|
|
//modify the occupied spaces to be in the other orientation,
|
|
// if the current orientation of the colliding space is different from the parent space
|
|
if (collidingSpace.direction !== direction) {
|
|
if (currentAccessors.isHorizontal !== isHorizontal) {
|
|
currentOccSpacesMap = getModifiedOccupiedSpacesMap(
|
|
OGOccupiedSpacesMap,
|
|
prevMovementMap,
|
|
currentAccessors.isHorizontal,
|
|
gridProps,
|
|
currentAccessors.perpendicularMax,
|
|
currentAccessors.perpendicularMin,
|
|
);
|
|
currentCollidingSpace = {
|
|
...currentCollidingSpace,
|
|
...currentOccSpacesMap[collidingSpace.id],
|
|
};
|
|
}
|
|
|
|
filterCommonSpaces(
|
|
{
|
|
[collidingSpace.collidingId]: true,
|
|
},
|
|
currentOccSpacesMap,
|
|
);
|
|
|
|
currentOccSpaces = Object.values(currentOccSpacesMap);
|
|
currentOccSpaces.sort((a, b) => {
|
|
return a[currentAccessors.direction] - b[currentAccessors.direction];
|
|
});
|
|
}
|
|
return {
|
|
currentOccSpacesMap,
|
|
currentAccessors,
|
|
currentDirection,
|
|
currentOccSpaces,
|
|
currentCollidingSpace,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper method to generate movement map by recursively going over the collision tree
|
|
* This method recursively traverses the collision tree, to calculate from the ends of the tree making it's way to the roots
|
|
* This is calculated in that way to check if the ends of branches are colliding with the boundaries of the canvas.
|
|
*
|
|
* @param collisionTree space and it's colliding spaces in a tree structure
|
|
* @param movementMap map containing reflowed X, Y, width and height of spaces
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param direction ReflowDirection, direction of reflow of the colliding space
|
|
* @param accessors accessors to access dimensions of spaces in a direction
|
|
* @param prevWidgetDistance dimension of the previous colliding widget in the direction
|
|
* @param distanceBeforeCollision point of collision from the previous widget
|
|
* @param globalCollisionTrees Array of collision trees of direct colliding spaces
|
|
* @param index index of insertion if a collision node is of different direction from it's parent
|
|
* @param emptySpaces current number of emptySpaces it's parent ancestors encountered while reflowed
|
|
* @param shouldResize if the reflowed widgets can be reflowed
|
|
* @param globalProcessedNodes cache to make sure to not generate a tree for the same space in the getCollision Tree
|
|
* @returns movement map of current collision tree node
|
|
*/
|
|
function getMovementMapHelper(
|
|
collisionTree: CollisionTree,
|
|
movementMap: ReflowedSpaceMap,
|
|
dimensions = { X: 0, Y: 0 },
|
|
gridProps: GridProps,
|
|
direction: ReflowDirection,
|
|
accessors: CollisionAccessors,
|
|
prevWidgetDistance: number,
|
|
distanceBeforeCollision = 0,
|
|
globalCollisionTrees: CollisionTree[],
|
|
index: number,
|
|
emptySpaces = 0,
|
|
shouldResize: boolean,
|
|
globalProcessedNodes: CollisionTreeCache,
|
|
) {
|
|
let maxOccupiedSpace = 0,
|
|
occupiedLength = 0,
|
|
currentEmptySpaces = emptySpaces;
|
|
|
|
if (collisionTree.children && !isEmpty(collisionTree.children)) {
|
|
const childNodes = Object.values(collisionTree.children);
|
|
|
|
for (const childNode of childNodes) {
|
|
if (childNode.direction !== direction) {
|
|
globalCollisionTrees.splice(index + 1, 0, childNode);
|
|
continue;
|
|
}
|
|
const nextEmptySpaces =
|
|
emptySpaces +
|
|
Math.abs(prevWidgetDistance - childNode[accessors.oppositeDirection]);
|
|
|
|
let {
|
|
currentEmptySpaces: childEmptySpaces,
|
|
occupiedLength: currentOccupiedLength,
|
|
occupiedSpace,
|
|
shouldProcessNode,
|
|
} = checkProcessNodeForTree(childNode, globalProcessedNodes);
|
|
//process the nodes if either one is undefined
|
|
if (
|
|
shouldProcessNode ||
|
|
currentOccupiedLength === undefined ||
|
|
occupiedSpace === undefined ||
|
|
childEmptySpaces === undefined
|
|
) {
|
|
const movementVariables = getMovementMapHelper(
|
|
childNode,
|
|
movementMap,
|
|
dimensions,
|
|
gridProps,
|
|
direction,
|
|
accessors,
|
|
childNode[accessors.direction],
|
|
distanceBeforeCollision,
|
|
globalCollisionTrees,
|
|
index,
|
|
nextEmptySpaces,
|
|
shouldResize,
|
|
globalProcessedNodes,
|
|
);
|
|
//initialize if undefined
|
|
if (!globalProcessedNodes[childNode.id]) {
|
|
globalProcessedNodes[childNode.id] = {};
|
|
}
|
|
//add value to cache
|
|
globalProcessedNodes[childNode.id][childNode.direction] = {
|
|
value: childNode.collidingValue,
|
|
occupiedLength: movementVariables.occupiedLength,
|
|
occupiedSpace: movementVariables.occupiedSpace,
|
|
currentEmptySpaces: movementVariables.currentEmptySpaces,
|
|
};
|
|
|
|
//set current values
|
|
shouldProcessNode = false;
|
|
currentOccupiedLength = movementVariables.occupiedLength;
|
|
occupiedSpace = movementVariables.occupiedSpace;
|
|
childEmptySpaces = movementVariables.currentEmptySpaces;
|
|
}
|
|
|
|
if (maxOccupiedSpace < occupiedSpace) {
|
|
currentEmptySpaces = childEmptySpaces;
|
|
}
|
|
|
|
//maxOccupiedSpace is the maximum dimension that is occupied by all the spaces above it in the tree
|
|
maxOccupiedSpace = Math.max(maxOccupiedSpace, occupiedSpace || 0);
|
|
// occupiedLength is the sum of minimum occupied lengths of all spaces between collidingSpace and the edge of canvas,
|
|
//useful to calculate resized dimensions for spaces colliding with boundaries
|
|
occupiedLength = Math.max(occupiedLength, currentOccupiedLength);
|
|
}
|
|
} else {
|
|
if (direction === ReflowDirection.RIGHT) {
|
|
currentEmptySpaces +=
|
|
GridDefaults.DEFAULT_GRID_COLUMNS - collisionTree.right;
|
|
} else if (direction !== ReflowDirection.BOTTOM) {
|
|
currentEmptySpaces += collisionTree[accessors.direction];
|
|
}
|
|
}
|
|
|
|
//It calculates mainly the X, Y, width, height of the spaces to transform the existing spaces
|
|
const getSpaceMovement = accessors.isHorizontal
|
|
? getHorizontalSpaceMovement
|
|
: getVerticalSpaceMovement;
|
|
const movementObj = getSpaceMovement(
|
|
collisionTree,
|
|
gridProps,
|
|
direction,
|
|
maxOccupiedSpace,
|
|
occupiedLength,
|
|
distanceBeforeCollision,
|
|
emptySpaces,
|
|
currentEmptySpaces,
|
|
dimensions,
|
|
shouldResize,
|
|
);
|
|
|
|
if (
|
|
// If a value already exists in the movement map,
|
|
// this method is used to check if the current one should override the existing movement values of the space
|
|
!shouldReplaceOldMovement(
|
|
movementMap[collisionTree.id],
|
|
movementObj,
|
|
direction,
|
|
)
|
|
) {
|
|
const { isHorizontal } = getAccessor(direction);
|
|
return isHorizontal
|
|
? {
|
|
occupiedSpace:
|
|
(movementMap[collisionTree.id].horizontalMaxOccupiedSpace || 0) +
|
|
collisionTree[accessors.parallelMax] -
|
|
collisionTree[accessors.parallelMin],
|
|
occupiedLength:
|
|
(movementMap[collisionTree.id].horizontalOccupiedLength || 0) +
|
|
HORIZONTAL_RESIZE_MIN_LIMIT,
|
|
currentEmptySpaces:
|
|
(movementMap[collisionTree.id].horizontalEmptySpaces as number) ||
|
|
0,
|
|
}
|
|
: {
|
|
occupiedSpace:
|
|
(movementMap[collisionTree.id].verticalMaxOccupiedSpace || 0) +
|
|
collisionTree[accessors.parallelMax] -
|
|
collisionTree[accessors.parallelMin],
|
|
occupiedLength:
|
|
(movementMap[collisionTree.id].verticalOccupiedLength || 0) +
|
|
(collisionTree.fixedHeight && accessors.directionIndicator < 0
|
|
? collisionTree.fixedHeight
|
|
: VERTICAL_RESIZE_MIN_LIMIT),
|
|
currentEmptySpaces:
|
|
(movementMap[collisionTree.id].verticalEmptySpaces as number) || 0,
|
|
};
|
|
}
|
|
|
|
movementMap[collisionTree.id] = {
|
|
...movementMap[collisionTree.id],
|
|
...movementObj,
|
|
};
|
|
|
|
return {
|
|
occupiedSpace:
|
|
maxOccupiedSpace +
|
|
collisionTree[accessors.parallelMax] -
|
|
collisionTree[accessors.parallelMin],
|
|
occupiedLength:
|
|
occupiedLength +
|
|
(accessors.isHorizontal
|
|
? HORIZONTAL_RESIZE_MIN_LIMIT
|
|
: collisionTree.fixedHeight && accessors.directionIndicator < 0
|
|
? collisionTree.fixedHeight
|
|
: VERTICAL_RESIZE_MIN_LIMIT),
|
|
currentEmptySpaces,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* get movement of the collision tree node in horizontal orientation
|
|
* @param collisionTree space and it's colliding spaces in a tree structure
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param direction ReflowDirection, direction of reflow of the colliding space
|
|
* @param maxOccupiedSpace dimension of all the spaces that were occupied
|
|
* @param occupiedLength is the sum of minimum occupied lengths of all spaces between collidingSpace and the edge of canvas
|
|
* @param distanceBeforeCollision point of collision from the previous widget
|
|
* @param emptySpaces total number of emptySpaces it's parent ancestors encountered while reflowed
|
|
* @param currentEmptySpaces current number of emptySpaces this node encountered
|
|
* @param shouldResize if the reflowed widgets can be reflowed
|
|
* @param delta X and Y distance from the original new space positions
|
|
* @param shouldResize if the reflowed widgets can be reflowed
|
|
* @returns movement of the collision tree node in horizontal orientation
|
|
*/
|
|
export function getHorizontalSpaceMovement(
|
|
collisionTree: CollisionTree,
|
|
gridProps: GridProps,
|
|
direction: ReflowDirection,
|
|
maxOccupiedSpace: number,
|
|
occupiedLength: number,
|
|
distanceBeforeCollision: number,
|
|
emptySpaces: number,
|
|
currentEmptySpaces: number,
|
|
{ X }: Delta,
|
|
shouldResize: boolean,
|
|
) {
|
|
//maxX is the maximum leeway left for the space before it cannot move anymore in the X axis
|
|
const maxX = getMaxX(
|
|
collisionTree,
|
|
gridProps,
|
|
direction,
|
|
occupiedLength,
|
|
maxOccupiedSpace,
|
|
shouldResize,
|
|
);
|
|
const width = getReflowedDimension(
|
|
collisionTree,
|
|
direction,
|
|
X,
|
|
maxX,
|
|
distanceBeforeCollision,
|
|
gridProps.parentColumnSpace,
|
|
emptySpaces,
|
|
HORIZONTAL_RESIZE_MIN_LIMIT,
|
|
shouldResize,
|
|
);
|
|
const spaceMovement = {
|
|
X: getReflowDistance(
|
|
collisionTree,
|
|
direction,
|
|
maxX,
|
|
distanceBeforeCollision,
|
|
width,
|
|
emptySpaces,
|
|
gridProps.parentColumnSpace,
|
|
),
|
|
dimensionXBeforeCollision: distanceBeforeCollision,
|
|
directionX: direction,
|
|
maxX,
|
|
width,
|
|
horizontalEmptySpaces: currentEmptySpaces,
|
|
horizontalOccupiedLength: occupiedLength,
|
|
horizontalMaxOccupiedSpace: maxOccupiedSpace,
|
|
};
|
|
|
|
return spaceMovement;
|
|
}
|
|
|
|
/**
|
|
* get movement of the collision tree node in vertical orientation
|
|
* @param collisionTree space and it's colliding spaces in a tree structure
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param direction ReflowDirection, direction of reflow of the colliding space
|
|
* @param maxOccupiedSpace dimension of all the spaces that were occupied
|
|
* @param occupiedLength is the sum of minimum occupied lengths of all spaces between collidingSpace and the edge of canvas
|
|
* @param distanceBeforeCollision point of collision from the previous widget
|
|
* @param emptySpaces total number of emptySpaces it's parent ancestors encountered while reflowed
|
|
* @param currentEmptySpaces current number of emptySpaces this node encountered
|
|
* @param shouldResize if the reflowed widgets can be reflowed
|
|
* @param delta X and Y distance from the original new space positions
|
|
* @param shouldResize if the reflowed widgets can be reflowed
|
|
* @returns movement of the collision tree node in vertical orientation
|
|
*/
|
|
export function getVerticalSpaceMovement(
|
|
collisionTree: CollisionTree,
|
|
gridProps: GridProps,
|
|
direction: ReflowDirection,
|
|
maxOccupiedSpace: number,
|
|
occupiedLength: number,
|
|
distanceBeforeCollision: number,
|
|
emptySpaces: number,
|
|
currentEmptySpaces: number,
|
|
{ Y }: Delta,
|
|
shouldResize: boolean,
|
|
) {
|
|
//maxY is the maximum leeway left for the space before it cannot move anymore in the Y axis
|
|
const maxY = getMaxY(
|
|
collisionTree,
|
|
gridProps,
|
|
direction,
|
|
occupiedLength,
|
|
maxOccupiedSpace,
|
|
shouldResize,
|
|
);
|
|
const height = getReflowedDimension(
|
|
collisionTree,
|
|
direction,
|
|
Y,
|
|
maxY,
|
|
distanceBeforeCollision,
|
|
gridProps.parentRowSpace,
|
|
emptySpaces,
|
|
VERTICAL_RESIZE_MIN_LIMIT,
|
|
shouldResize,
|
|
);
|
|
const spaceMovement = {
|
|
Y: getReflowDistance(
|
|
collisionTree,
|
|
direction,
|
|
maxY,
|
|
distanceBeforeCollision,
|
|
height,
|
|
emptySpaces,
|
|
gridProps.parentRowSpace,
|
|
true,
|
|
),
|
|
dimensionYBeforeCollision: distanceBeforeCollision,
|
|
directionY: direction,
|
|
maxY,
|
|
height,
|
|
verticalEmptySpaces: currentEmptySpaces,
|
|
verticalOccupiedLength: occupiedLength,
|
|
verticalMaxOccupiedSpace: maxOccupiedSpace,
|
|
};
|
|
|
|
return spaceMovement;
|
|
}
|
|
|
|
/**
|
|
* to get movement variable to determine the limit of all the new Space Positions,
|
|
* MovementVariables are intermediatory variables to calculate the actual movement Limits of each dragging/resizing space
|
|
*
|
|
* @param newSpacePositionsMap new/current positions map of the space/block
|
|
* @param directionalVariables information required to calculate limits such as occupiedLength, emptySpaces of new space positions
|
|
* @param delta X and Y distance from original positions
|
|
* @param gridProps properties of the canvas's grid
|
|
* @param shouldResize boolean to indicate if colliding spaces should resize
|
|
* @returns movement variable to determine the limit of all the new Space Positions
|
|
*/
|
|
function getMovementVariables(
|
|
newSpacePositionsMap: SpaceMap,
|
|
directionalVariables: DirectionalVariables,
|
|
delta: Delta,
|
|
gridProps: GridProps,
|
|
shouldResize: boolean,
|
|
) {
|
|
const newSpacePositionIds = Object.keys(directionalVariables);
|
|
const movementVariablesMap: SpaceMovementMap = {};
|
|
|
|
for (const newSpacePositionId of newSpacePositionIds) {
|
|
if (!newSpacePositionsMap[newSpacePositionId]) continue;
|
|
|
|
const movementVariables = directionalVariables[newSpacePositionId];
|
|
const directionalKeys = Object.keys(movementVariables);
|
|
const directionalMovements: DirectionalMovement[] = [];
|
|
|
|
for (const directionKey of directionalKeys) {
|
|
const [
|
|
staticOccupiedLength,
|
|
maxOccupiedSpace,
|
|
accessors,
|
|
reflowDirection,
|
|
] = movementVariables[directionKey];
|
|
|
|
const maxMethod = accessors.isHorizontal ? getMaxX : getMaxY;
|
|
const gridDistance = accessors.isHorizontal
|
|
? gridProps.parentColumnSpace
|
|
: gridProps.parentRowSpace;
|
|
const coordinateKey = accessors.isHorizontal ? "X" : "Y";
|
|
const maxMovement =
|
|
maxMethod(
|
|
newSpacePositionsMap[newSpacePositionId] as CollisionTree,
|
|
gridProps,
|
|
reflowDirection,
|
|
staticOccupiedLength,
|
|
maxOccupiedSpace,
|
|
shouldResize,
|
|
) +
|
|
delta[coordinateKey] +
|
|
accessors.directionIndicator * gridDistance;
|
|
|
|
directionalMovements.push({
|
|
maxMovement,
|
|
directionalIndicator: accessors.directionIndicator,
|
|
coordinateKey,
|
|
isHorizontal: accessors.isHorizontal,
|
|
});
|
|
}
|
|
|
|
movementVariablesMap[newSpacePositionId] = directionalMovements;
|
|
}
|
|
return movementVariablesMap;
|
|
}
|