PromucFlow_constructor/app/client/src/utils/autoHeight/reflow.ts

205 lines
8.1 KiB
TypeScript
Raw Normal View History

feat: (Internal Change) Add auto height computation functions (#17962) * Add auto height reflow functions libary * Add comments in hard to understand parts * feat: auto height reflow lib (#17978) added 2 tests for boxHelper and 1 simple test for reflow computeChangeInPositionBasedOnDelta Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> * Reduce one loop. Fix typo * Add helper functions and use them in lib * Use helper function * Add types * Fix issue where widgets don't get close to the bottom most above widget if that widget hasn't changed * fix: auto height reflow lib merge release (#18193) * feat: show number of tabs on the header (#18071) * number of tabs displayed alongside label * styling for span removed * feature added and cypress test cases written * code refactoring after review * Update top contributors * feat: Auto-height add reducers and actions (#17953) * Add reducers for auto height feature (Internal Change, No changes reflected to users) Co-authored-by: ankurrsinghal <ankur@appsmith.com> * [Bug] Incorrect count of users in workspace when adding multiple users (#17728) fix: filtering unique users by userId Co-authored-by: Anubhav <anubhav@appsmith.com> * fix: Instrumentation for execution errors (#18093) * fix: Instrumentation for execution errors * added widget editor error event * fix: Sidebar heading fontSize & checkbox alignment (#18104) sidebar heading & checkbox alignment to heading * chore: added type for feature flag (#18152) add: new type for env variable * Update top contributors * feat: [Context Switching]: Change focus target and fix cursor position (#17794) Co-authored-by: rahulramesha <rahul@appsmith.com> * fix: JS Objects save failures due to AST changes (#18018) * fix: update local_testing.sh to build image for external contributor PRs (#18024) * chore: use `typography` and `getTypographyFromKey` from the design-system (#18050) Change typography imports, change function call * dummy Co-authored-by: Rishabh Kashyap <rishabh.kashyap@appsmith.com> Co-authored-by: Appsmith Bot <74705725+appsmith-bot@users.noreply.github.com> Co-authored-by: Abhinav Jha <abhinav@appsmith.com> Co-authored-by: Ankit Srivastava <67647761+ankitsrivas14@users.noreply.github.com> Co-authored-by: Anubhav <anubhav@appsmith.com> Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com> Co-authored-by: Rohit Agarwal <rohit_agarwal@live.in> Co-authored-by: Ayush Pahwa <ayush@appsmith.com> Co-authored-by: Hetu Nandu <hetu@appsmith.com> Co-authored-by: subratadeypappu <subrata@appsmith.com> Co-authored-by: Sumit Kumar <sumit@appsmith.com> Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com> Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> Co-authored-by: ankurrsinghal <ankur@appsmith.com> Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> Co-authored-by: Rishabh Kashyap <rishabh.kashyap@appsmith.com> Co-authored-by: Appsmith Bot <74705725+appsmith-bot@users.noreply.github.com> Co-authored-by: Ankit Srivastava <67647761+ankitsrivas14@users.noreply.github.com> Co-authored-by: Anubhav <anubhav@appsmith.com> Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com> Co-authored-by: Rohit Agarwal <rohit_agarwal@live.in> Co-authored-by: Ayush Pahwa <ayush@appsmith.com> Co-authored-by: Hetu Nandu <hetu@appsmith.com> Co-authored-by: subratadeypappu <subrata@appsmith.com> Co-authored-by: Sumit Kumar <sumit@appsmith.com> Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com>
2022-11-20 06:12:32 +00:00
import { pushToArray } from "utils/helpers";
import { TreeNode } from "./constants";
/**
*
* @param tree : Auto Height Layout Tree
* @param effectedBoxId : Current box in consideration
* @param aboveId : Above box which may or maynot have changed
* @param offsetSoFar : Offset of the above box, or changes to be applied so far
* @returns : The offset expected to be applied to the effectedBoxId. This is how much this box should move
*/
export function getNegativeOffset(
tree: Record<string, TreeNode>,
effectedBoxId: string,
aboveId: string,
offsetSoFar = 0,
): number {
if (offsetSoFar <= 0) {
// Let's take in to account the old spacing between the effected box and bottom most above box
// when the layout was last updated.
const oldSpacing =
tree[effectedBoxId].originalTopRow - tree[aboveId].originalBottomRow;
// Let's compute the spacing between the effected box and bottom most above box
const currentSpacing = tree[effectedBoxId].topRow - tree[aboveId].bottomRow;
// If the old spacing is less than current spacing and the offset of the bottom most above,
// we need to make sure that we're sticking to the original spacing between the bottom most above
// and the current effected box.
// Note: This applies only if the offset is negative, which is to say that the box is to move up
if (oldSpacing < currentSpacing + offsetSoFar) {
return oldSpacing + offsetSoFar - currentSpacing;
}
}
return offsetSoFar;
}
/**
* Gets the nearest above box for the current box. Including the aboves which have changes so far.
*
* @param tree: Auto Height Layout Tree
* @param effectedBoxId: Current box in consideration
* @param repositionedBoxes: Boxes repositioned so far
* @returns An array of boxIds which are above and nearest the effectedBoxId
*/
export function getNearestAbove(
tree: Record<string, TreeNode>,
effectedBoxId: string,
repositionedBoxes: Record<string, { topRow: number; bottomRow: number }>,
) {
// Get all the above boxes
const aboves = tree[effectedBoxId].aboves;
// We're trying to find the nearest boxes above this box
return aboves.reduce((prev: string[], next: string) => {
if (!prev[0]) return [next];
// Get the bottomRow of the above box
let nextBottomRow = tree[next].bottomRow;
let prevBottomRow = tree[prev[0]].bottomRow;
// If we've already repositioned this, use the new bottomRow of the box
if (repositionedBoxes[next]) {
nextBottomRow = repositionedBoxes[next].bottomRow;
}
if (repositionedBoxes[prev[0]]) {
prevBottomRow = repositionedBoxes[prev[0]].bottomRow;
}
// If the current box's (next) bottomRow is larger than the previous
// This (next) box is the bottom most above so far
if (nextBottomRow > prevBottomRow) return [next];
// If this (next) box's bottom row is the same as the previous
// We have two bottom most boxes
else if (nextBottomRow === prevBottomRow) {
if (
repositionedBoxes[prev[0]] &&
repositionedBoxes[prev[0]].bottomRow ===
repositionedBoxes[prev[0]].topRow
) {
return prev;
}
if (
repositionedBoxes[next] &&
repositionedBoxes[next].bottomRow === repositionedBoxes[next].topRow
) {
return [next];
}
return [...prev, next];
}
// This (next) box's bottom row is lower than the boxes selected so far
// so, we ignore it.
else return prev;
}, []);
}
// This function computes the new positions for boxes based on the boxes which have changed height
// delta: a map of boxes with change in heights
// tree: a layout tree which contains the current state of the boxes.
export function computeChangeInPositionBasedOnDelta(
tree: Record<string, TreeNode>,
delta: Record<string, number>,
): Record<string, { topRow: number; bottomRow: number }> {
const repositionedBoxes: Record<
string,
{ topRow: number; bottomRow: number }
> = {};
const effectedBoxMap: Record<string, number[]> = {};
// For each box which has changed height (box delta)
for (const boxId in delta) {
// Create an effectedBoxMap, which contains the changes for each of the boxes effected by the delta of this box
// This is a map, because multiple box deltas can effect one box
// We simply take all the boxes which are below this box from the tree
// and add the delta to the effectedBoxMap where the key is the below boxId from the tree
tree[boxId].belows.forEach((effectedId) => {
effectedBoxMap[effectedId] = pushToArray(
delta[boxId],
effectedBoxMap[effectedId],
) as number[];
});
// Add this box's delta to the repositioning, as this won't show up in the effectedBoxMap
repositionedBoxes[boxId] = {
topRow: tree[boxId].topRow,
bottomRow: tree[boxId].bottomRow + delta[boxId],
};
}
// Sort the effected box ids, this is to make sure we compute from top to bottom.
const sortedEffectedBoxIds = Object.keys(effectedBoxMap).sort(
(a, b) => tree[a].topRow - tree[b].topRow,
);
// For each of the boxes which have been effected
for (const effectedBoxId of sortedEffectedBoxIds) {
let _offset;
const bottomMostAboves = getNearestAbove(
tree,
effectedBoxId,
repositionedBoxes,
);
// for each of the bottom most above boxes.
// Note: There can be more than one if two above widgets have the same bottomrow
for (const aboveId of bottomMostAboves) {
// If the above box has been effected by another box change height
// Or, if this above box itself has changed height
if (Array.isArray(effectedBoxMap[aboveId]) || delta[aboveId]) {
// In case the above box has changed heights
const _aboveOffset = repositionedBoxes[aboveId]
? repositionedBoxes[aboveId].bottomRow - tree[aboveId].bottomRow
: 0;
// If so far, we haven't got any _offset updates
// This can happen if this is the first aboveId we're checking
if (_offset === undefined) _offset = _aboveOffset;
const negativeOffset = getNegativeOffset(
tree,
effectedBoxId,
aboveId,
_aboveOffset,
);
// If the bottom most above (_aboveOffset), has moved down (either by increasing height and/or due to its above)
// Let's take the effected boxs' change to be the max of _offset and _aboveOffset
// The _offset so far will be due to other bottomMostAbove effecting this effected box.
if (_aboveOffset > 0) _offset = Math.max(_aboveOffset, _offset);
// If the bottom most above (_aboveOffset) has moved up (either by decreasing height and/or due to its above)
// Let's take the Min (negative values, so max offset in the upward direction) of the _aboveOffset, _offset, negativeOffset.
else if (_aboveOffset < 0) {
_offset = Math.min(_aboveOffset, _offset, negativeOffset);
}
} else {
// Stick to the widget above if the bottomMost above box hasn't changed
// TODO(abhinav): Here we may want to use the same logic as negativeOffset using originals as done previously.
// Test this.
// Let's take in to account the old spacing between the effected box and bottom most above box
// when the layout was last updated.
const negativeOffset = getNegativeOffset(tree, effectedBoxId, aboveId);
_offset = negativeOffset;
}
}
// If _offset is not defined, this means that this box is the topmost box
if (_offset === undefined) {
// The effectedBoxId is the topmost box, so the _offset will most likely always be 0
_offset = effectedBoxMap[effectedBoxId].reduce(
(prev, next) => prev + next,
0,
);
}
// Finally update the repositioned box with the _offset.
if (repositionedBoxes[effectedBoxId]) {
repositionedBoxes[effectedBoxId].bottomRow += _offset;
repositionedBoxes[effectedBoxId].topRow += _offset;
} else {
repositionedBoxes[effectedBoxId] = {
topRow: tree[effectedBoxId].topRow + _offset,
bottomRow: tree[effectedBoxId].bottomRow + _offset,
};
}
}
return repositionedBoxes;
}