@cloudscape-design/board-components 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Config +23 -0
- package/LICENSE +201 -0
- package/NOTICE +2 -0
- package/README-cloudscape.md +20 -0
- package/README-open-source.md +0 -0
- package/README.md +20 -0
- package/board/index.d.ts +4 -0
- package/board/index.js +11 -0
- package/board/interfaces.d.ts +167 -0
- package/board/interfaces.js +1 -0
- package/board/internal.d.ts +4 -0
- package/board/internal.js +199 -0
- package/board/placeholder.d.ts +10 -0
- package/board/placeholder.js +24 -0
- package/board/styles.css.js +10 -0
- package/board/styles.scoped.css +23 -0
- package/board/styles.selectors.js +11 -0
- package/board/transition.d.ts +50 -0
- package/board/transition.js +211 -0
- package/board/utils/announcements.d.ts +10 -0
- package/board/utils/announcements.js +98 -0
- package/board/utils/create-transforms.d.ts +7 -0
- package/board/utils/create-transforms.js +22 -0
- package/board/utils/events.d.ts +3 -0
- package/board/utils/events.js +14 -0
- package/board/utils/get-hovered-rect.d.ts +10 -0
- package/board/utils/get-hovered-rect.js +19 -0
- package/board/utils/layout.d.ts +19 -0
- package/board/utils/layout.js +77 -0
- package/board/utils/path.d.ts +10 -0
- package/board/utils/path.js +73 -0
- package/board-item/header.d.ts +7 -0
- package/board-item/header.js +8 -0
- package/board-item/index.d.ts +4 -0
- package/board-item/index.js +11 -0
- package/board-item/interfaces.d.ts +42 -0
- package/board-item/interfaces.js +1 -0
- package/board-item/internal.d.ts +4 -0
- package/board-item/internal.js +20 -0
- package/board-item/styles.css.js +15 -0
- package/board-item/styles.scoped.css +41 -0
- package/board-item/styles.selectors.js +16 -0
- package/index.d.ts +6 -0
- package/index.js +5 -0
- package/internal/api-docs/components/board-item.js +68 -0
- package/internal/api-docs/components/board.js +117 -0
- package/internal/api-docs/components/index.js +5 -0
- package/internal/api-docs/components/items-palette.js +58 -0
- package/internal/api-docs/test-utils-doc/dom.js +4 -0
- package/internal/api-docs/test-utils-doc/selectors.js +4 -0
- package/internal/base-component/use-base-component.d.ts +12 -0
- package/internal/base-component/use-base-component.js +16 -0
- package/internal/base-component/use-telemetry.d.ts +1 -0
- package/internal/base-component/use-telemetry.js +9 -0
- package/internal/base-component/use-visual-refresh.d.ts +1 -0
- package/internal/base-component/use-visual-refresh.js +24 -0
- package/internal/breakpoints.d.ts +2 -0
- package/internal/breakpoints.js +18 -0
- package/internal/constants.d.ts +3 -0
- package/internal/constants.js +5 -0
- package/internal/debug-tools/converters.d.ts +4 -0
- package/internal/debug-tools/converters.js +21 -0
- package/internal/debug-tools/generators.d.ts +25 -0
- package/internal/debug-tools/generators.js +258 -0
- package/internal/debug-tools/index.d.ts +8 -0
- package/internal/debug-tools/index.js +10 -0
- package/internal/debug-tools/interfaces.d.ts +2 -0
- package/internal/debug-tools/interfaces.js +3 -0
- package/internal/debug-tools/parsers.d.ts +8 -0
- package/internal/debug-tools/parsers.js +66 -0
- package/internal/dnd-controller/__mocks__/controller.d.ts +13 -0
- package/internal/dnd-controller/__mocks__/controller.js +33 -0
- package/internal/dnd-controller/collision.d.ts +17 -0
- package/internal/dnd-controller/collision.js +32 -0
- package/internal/dnd-controller/controller.d.ts +64 -0
- package/internal/dnd-controller/controller.js +131 -0
- package/internal/dnd-controller/event-emitter.d.ts +9 -0
- package/internal/dnd-controller/event-emitter.js +22 -0
- package/internal/drag-handle/icon.d.ts +4 -0
- package/internal/drag-handle/icon.js +12 -0
- package/internal/drag-handle/index.d.ts +9 -0
- package/internal/drag-handle/index.js +11 -0
- package/internal/drag-handle/styles.css.js +6 -0
- package/internal/drag-handle/styles.scoped.css +7 -0
- package/internal/drag-handle/styles.selectors.js +7 -0
- package/internal/environment.d.ts +4 -0
- package/internal/environment.js +4 -0
- package/internal/generated/styles/tokens.d.ts +0 -0
- package/internal/generated/styles/tokens.js +0 -0
- package/internal/grid/grid.d.ts +3 -0
- package/internal/grid/grid.js +28 -0
- package/internal/grid/index.d.ts +2 -0
- package/internal/grid/index.js +3 -0
- package/internal/grid/interfaces.d.ts +14 -0
- package/internal/grid/interfaces.js +3 -0
- package/internal/grid/item.d.ts +8 -0
- package/internal/grid/item.js +10 -0
- package/internal/grid/styles.css.js +7 -0
- package/internal/grid/styles.scoped.css +221 -0
- package/internal/grid/styles.selectors.js +8 -0
- package/internal/handle/index.d.ts +3 -0
- package/internal/handle/index.js +17 -0
- package/internal/handle/styles.css.js +6 -0
- package/internal/handle/styles.scoped.css +32 -0
- package/internal/handle/styles.selectors.js +7 -0
- package/internal/interfaces.d.ts +50 -0
- package/internal/interfaces.js +3 -0
- package/internal/is-development.d.ts +6 -0
- package/internal/is-development.js +8 -0
- package/internal/item-container/get-next-droppable.d.ts +7 -0
- package/internal/item-container/get-next-droppable.js +15 -0
- package/internal/item-container/index.d.ts +48 -0
- package/internal/item-container/index.js +262 -0
- package/internal/item-container/styles.css.js +12 -0
- package/internal/item-container/styles.scoped.css +33 -0
- package/internal/item-container/styles.selectors.js +13 -0
- package/internal/layout-engine/engine-conflicts.d.ts +4 -0
- package/internal/layout-engine/engine-conflicts.js +53 -0
- package/internal/layout-engine/engine-step.d.ts +5 -0
- package/internal/layout-engine/engine-step.js +307 -0
- package/internal/layout-engine/engine.d.ts +19 -0
- package/internal/layout-engine/engine.js +105 -0
- package/internal/layout-engine/grid.d.ts +28 -0
- package/internal/layout-engine/grid.js +140 -0
- package/internal/layout-engine/interfaces.d.ts +30 -0
- package/internal/layout-engine/interfaces.js +3 -0
- package/internal/layout-engine/utils.d.ts +12 -0
- package/internal/layout-engine/utils.js +79 -0
- package/internal/live-region/index.d.ts +44 -0
- package/internal/live-region/index.js +85 -0
- package/internal/logging.d.ts +1 -0
- package/internal/logging.js +10 -0
- package/internal/manifest.json +3 -0
- package/internal/resize-handle/icon.d.ts +4 -0
- package/internal/resize-handle/icon.js +12 -0
- package/internal/resize-handle/index.d.ts +8 -0
- package/internal/resize-handle/index.js +7 -0
- package/internal/resize-handle/styles.css.js +6 -0
- package/internal/resize-handle/styles.scoped.css +3 -0
- package/internal/resize-handle/styles.selectors.js +7 -0
- package/internal/screenreader-grid-navigation/index.d.ts +22 -0
- package/internal/screenreader-grid-navigation/index.js +42 -0
- package/internal/screenreader-grid-navigation/styles.css.js +7 -0
- package/internal/screenreader-grid-navigation/styles.scoped.css +12 -0
- package/internal/screenreader-grid-navigation/styles.selectors.js +8 -0
- package/internal/screenreader-only/index.d.ts +21 -0
- package/internal/screenreader-only/index.js +23 -0
- package/internal/screenreader-only/styles.css.js +6 -0
- package/internal/screenreader-only/styles.scoped.css +9 -0
- package/internal/screenreader-only/styles.selectors.js +7 -0
- package/internal/utils/apply-display-name.d.ts +1 -0
- package/internal/utils/apply-display-name.js +5 -0
- package/internal/utils/coordinates.d.ts +14 -0
- package/internal/utils/coordinates.js +20 -0
- package/internal/utils/events.d.ts +2 -0
- package/internal/utils/events.js +18 -0
- package/internal/utils/layout.d.ts +18 -0
- package/internal/utils/layout.js +132 -0
- package/internal/utils/position.d.ts +9 -0
- package/internal/utils/position.js +9 -0
- package/internal/utils/rects.d.ts +6 -0
- package/internal/utils/rects.js +76 -0
- package/internal/utils/screen.d.ts +1 -0
- package/internal/utils/screen.js +17 -0
- package/internal/utils/stack-set.d.ts +8 -0
- package/internal/utils/stack-set.js +23 -0
- package/internal/utils/throttle.d.ts +5 -0
- package/internal/utils/throttle.js +51 -0
- package/internal/utils/use-auto-scroll.d.ts +5 -0
- package/internal/utils/use-auto-scroll.js +62 -0
- package/internal/utils/use-last-interaction.d.ts +1 -0
- package/internal/utils/use-last-interaction.js +22 -0
- package/internal/utils/use-merge-refs.d.ts +9 -0
- package/internal/utils/use-merge-refs.js +29 -0
- package/internal/utils/use-stable-event-handler.d.ts +9 -0
- package/internal/utils/use-stable-event-handler.js +19 -0
- package/internal/utils/use-throttled-event-handler.d.ts +1 -0
- package/internal/utils/use-throttled-event-handler.js +11 -0
- package/internal/utils/zip-arrays.d.ts +1 -0
- package/internal/utils/zip-arrays.js +8 -0
- package/items-palette/index.d.ts +4 -0
- package/items-palette/index.js +11 -0
- package/items-palette/interfaces.d.ts +49 -0
- package/items-palette/interfaces.js +3 -0
- package/items-palette/internal.d.ts +4 -0
- package/items-palette/internal.js +73 -0
- package/items-palette/styles.css.js +6 -0
- package/items-palette/styles.scoped.css +3 -0
- package/items-palette/styles.selectors.js +7 -0
- package/package.json +43 -0
- package/test-utils/dom/board/index.d.ts +6 -0
- package/test-utils/dom/board/index.js +15 -0
- package/test-utils/dom/board/index.js.map +1 -0
- package/test-utils/dom/board-item/index.d.ts +10 -0
- package/test-utils/dom/board-item/index.js +33 -0
- package/test-utils/dom/board-item/index.js.map +1 -0
- package/test-utils/dom/index.d.ts +19 -0
- package/test-utils/dom/index.js +41 -0
- package/test-utils/dom/index.js.map +1 -0
- package/test-utils/dom/items-palette/index.d.ts +7 -0
- package/test-utils/dom/items-palette/index.js +18 -0
- package/test-utils/dom/items-palette/index.js.map +1 -0
- package/test-utils/dom/palette-item/index.d.ts +5 -0
- package/test-utils/dom/palette-item/index.js +15 -0
- package/test-utils/dom/palette-item/index.js.map +1 -0
- package/test-utils/selectors/board/index.d.ts +6 -0
- package/test-utils/selectors/board/index.js +15 -0
- package/test-utils/selectors/board/index.js.map +1 -0
- package/test-utils/selectors/board-item/index.d.ts +10 -0
- package/test-utils/selectors/board-item/index.js +33 -0
- package/test-utils/selectors/board-item/index.js.map +1 -0
- package/test-utils/selectors/index.d.ts +19 -0
- package/test-utils/selectors/index.js +41 -0
- package/test-utils/selectors/index.js.map +1 -0
- package/test-utils/selectors/items-palette/index.d.ts +7 -0
- package/test-utils/selectors/items-palette/index.js +18 -0
- package/test-utils/selectors/items-palette/index.js.map +1 -0
- package/test-utils/selectors/palette-item/index.d.ts +5 -0
- package/test-utils/selectors/palette-item/index.js +15 -0
- package/test-utils/selectors/palette-item/index.js.map +1 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { StackSet } from "../utils/stack-set";
|
|
4
|
+
export function resolveOverlaps(userMove, grid, moves, conflicts) {
|
|
5
|
+
new LayoutEngineStep(grid, moves, conflicts).resolveOverlaps(userMove);
|
|
6
|
+
}
|
|
7
|
+
export function refloatGrid(grid, moves, conflicts) {
|
|
8
|
+
new LayoutEngineStep(grid, moves, conflicts).refloatGrid();
|
|
9
|
+
}
|
|
10
|
+
class LayoutEngineStep {
|
|
11
|
+
constructor(grid, moves, conflicts) {
|
|
12
|
+
this.moves = [];
|
|
13
|
+
this.conflicts = new Set();
|
|
14
|
+
this.grid = grid;
|
|
15
|
+
this.moves = moves;
|
|
16
|
+
this.conflicts = conflicts;
|
|
17
|
+
}
|
|
18
|
+
// Issue moves on overlapping items trying to resolve all of them.
|
|
19
|
+
resolveOverlaps(userMove) {
|
|
20
|
+
const priorities = new Map();
|
|
21
|
+
const activeId = userMove.itemId;
|
|
22
|
+
const isResize = userMove.type === "RESIZE";
|
|
23
|
+
let overlaps = new StackSet();
|
|
24
|
+
let priorityOverlaps = new StackSet();
|
|
25
|
+
const addOverlap = (itemId) => {
|
|
26
|
+
if (!this.conflicts.has(itemId)) {
|
|
27
|
+
overlaps.push(itemId);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
this.makeMove(userMove, addOverlap, priorities);
|
|
31
|
+
const tryVacantMoves = () => {
|
|
32
|
+
// Try vacant moves on all overlaps.
|
|
33
|
+
let overlap = overlaps.pop();
|
|
34
|
+
while (overlap) {
|
|
35
|
+
const nextMove = this.tryFindVacantMove(overlap, activeId, isResize);
|
|
36
|
+
if (nextMove) {
|
|
37
|
+
this.makeMove(nextMove, addOverlap, priorities);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
priorityOverlaps.push(overlap);
|
|
41
|
+
}
|
|
42
|
+
overlap = overlaps.pop();
|
|
43
|
+
}
|
|
44
|
+
overlaps = priorityOverlaps;
|
|
45
|
+
priorityOverlaps = new StackSet();
|
|
46
|
+
tryPriorityMoves();
|
|
47
|
+
};
|
|
48
|
+
const tryPriorityMoves = () => {
|
|
49
|
+
// Try priority moves until first success and delegate back to vacant moves check.
|
|
50
|
+
const overlap = overlaps.pop();
|
|
51
|
+
if (overlap) {
|
|
52
|
+
const nextMove = this.findPriorityMove(overlap, priorities, activeId, isResize);
|
|
53
|
+
this.makeMove(nextMove, addOverlap, priorities);
|
|
54
|
+
tryVacantMoves();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
tryVacantMoves();
|
|
58
|
+
this.refloatGrid(activeId);
|
|
59
|
+
}
|
|
60
|
+
// Find items that can "float" to the top and apply the necessary moves.
|
|
61
|
+
refloatGrid(activeId) {
|
|
62
|
+
if (this.conflicts.size > 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
let needAnotherRefloat = false;
|
|
66
|
+
for (const item of this.grid.items) {
|
|
67
|
+
// The active item is skipped until the operation is committed.
|
|
68
|
+
if (item.id === activeId) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const move = {
|
|
72
|
+
itemId: item.id,
|
|
73
|
+
x: item.x,
|
|
74
|
+
y: item.y,
|
|
75
|
+
width: item.width,
|
|
76
|
+
height: item.height,
|
|
77
|
+
type: "FLOAT",
|
|
78
|
+
};
|
|
79
|
+
for (move.y; move.y >= 0; move.y--) {
|
|
80
|
+
if (!this.validateVacantMove({ ...move, y: move.y - 1 }, activeId !== null && activeId !== void 0 ? activeId : "", false)) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (item.y !== move.y) {
|
|
85
|
+
this.grid.move(move.itemId, move.x, move.y);
|
|
86
|
+
this.moves.push(move);
|
|
87
|
+
needAnotherRefloat = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (needAnotherRefloat) {
|
|
91
|
+
this.refloatGrid(activeId);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
makeMove(move, addOverlap, priorities) {
|
|
95
|
+
switch (move.type) {
|
|
96
|
+
case "ESCAPE":
|
|
97
|
+
case "FLOAT":
|
|
98
|
+
case "MOVE":
|
|
99
|
+
case "VACANT":
|
|
100
|
+
case "PRIORITY":
|
|
101
|
+
this.grid.move(move.itemId, move.x, move.y, addOverlap);
|
|
102
|
+
break;
|
|
103
|
+
case "INSERT":
|
|
104
|
+
this.grid.insert({ id: move.itemId, ...move }, addOverlap);
|
|
105
|
+
break;
|
|
106
|
+
case "REMOVE":
|
|
107
|
+
this.grid.remove(move.itemId);
|
|
108
|
+
break;
|
|
109
|
+
case "RESIZE":
|
|
110
|
+
this.grid.resize(move.itemId, move.width, move.height, addOverlap);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
this.moves.push(move);
|
|
114
|
+
priorities.set(move.itemId, this.getMovePriority(move));
|
|
115
|
+
}
|
|
116
|
+
getMovePriority(move) {
|
|
117
|
+
switch (move.type) {
|
|
118
|
+
case "FLOAT":
|
|
119
|
+
return 0;
|
|
120
|
+
case "VACANT":
|
|
121
|
+
return 1;
|
|
122
|
+
case "PRIORITY":
|
|
123
|
+
case "ESCAPE":
|
|
124
|
+
return 5;
|
|
125
|
+
default:
|
|
126
|
+
return 9999;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Retrieves prioritized list of directions to look for a resolution move.
|
|
130
|
+
getMoveDirections(issuer) {
|
|
131
|
+
const diff = this.getLastStepDiff(issuer);
|
|
132
|
+
const firstVertical = diff.y > 0 ? "down" : "up";
|
|
133
|
+
const nextVertical = firstVertical === "down" ? "up" : "down";
|
|
134
|
+
const firstHorizontal = diff.x > 0 ? "right" : "left";
|
|
135
|
+
const nextHorizontal = firstHorizontal === "right" ? "left" : "right";
|
|
136
|
+
return Math.abs(diff.y) > Math.abs(diff.x)
|
|
137
|
+
? [firstVertical, firstHorizontal, nextHorizontal, nextVertical]
|
|
138
|
+
: [firstHorizontal, firstVertical, nextVertical, nextHorizontal];
|
|
139
|
+
}
|
|
140
|
+
getResizeDirections(issuer) {
|
|
141
|
+
const diff = this.getLastStepDiff(issuer);
|
|
142
|
+
const firstVertical = diff.y > 0 ? "up" : "down";
|
|
143
|
+
const nextVertical = firstVertical === "down" ? "up" : "down";
|
|
144
|
+
const firstHorizontal = diff.x > 0 ? "left" : "right";
|
|
145
|
+
const nextHorizontal = firstHorizontal === "right" ? "left" : "right";
|
|
146
|
+
return Math.abs(diff.y) > Math.abs(diff.x)
|
|
147
|
+
? [firstVertical, firstHorizontal, nextHorizontal, nextVertical]
|
|
148
|
+
: [firstHorizontal, firstVertical, nextVertical, nextHorizontal];
|
|
149
|
+
}
|
|
150
|
+
getLastStepDiff(issuer) {
|
|
151
|
+
var _a, _b;
|
|
152
|
+
const issuerMoves = this.moves.filter((move) => move.itemId === issuer.id);
|
|
153
|
+
const originalParams = {
|
|
154
|
+
x: issuer.originalX,
|
|
155
|
+
y: issuer.originalY,
|
|
156
|
+
width: issuer.originalWidth,
|
|
157
|
+
height: issuer.originalHeight,
|
|
158
|
+
};
|
|
159
|
+
const prevIssuerMove = (_a = issuerMoves[issuerMoves.length - 2]) !== null && _a !== void 0 ? _a : originalParams;
|
|
160
|
+
const lastIssuerMove = (_b = issuerMoves[issuerMoves.length - 1]) !== null && _b !== void 0 ? _b : originalParams;
|
|
161
|
+
const diff = {
|
|
162
|
+
x: prevIssuerMove.x - lastIssuerMove.x,
|
|
163
|
+
y: prevIssuerMove.y - lastIssuerMove.y,
|
|
164
|
+
width: prevIssuerMove.width - lastIssuerMove.width,
|
|
165
|
+
height: prevIssuerMove.height - lastIssuerMove.height,
|
|
166
|
+
};
|
|
167
|
+
return diff.x || diff.y ? { x: diff.x, y: diff.y } : { x: diff.width, y: diff.height };
|
|
168
|
+
}
|
|
169
|
+
// Try finding a move that resolves an overlap by moving an item to a vacant space.
|
|
170
|
+
tryFindVacantMove(overlap, activeId, isResize) {
|
|
171
|
+
const overlapItem = this.grid.getItem(overlap);
|
|
172
|
+
const overlapWith = this.getOverlapWith(overlapItem);
|
|
173
|
+
const directions = isResize ? this.getResizeDirections(overlapWith) : this.getMoveDirections(overlapWith);
|
|
174
|
+
for (const direction of directions) {
|
|
175
|
+
const move = this.getMoveForDirection(overlapItem, overlapWith, direction, "VACANT");
|
|
176
|
+
if (this.validateVacantMove(move, activeId, isResize)) {
|
|
177
|
+
return move;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
validateVacantMove(move, activeId, isResize) {
|
|
183
|
+
const moveTarget = this.grid.getItem(move.itemId);
|
|
184
|
+
for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
|
|
185
|
+
for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
|
|
186
|
+
const newY = move.y + (y - moveTarget.y);
|
|
187
|
+
const newX = move.x + (x - moveTarget.x);
|
|
188
|
+
// Outside the grid.
|
|
189
|
+
if (newY < 0 || newX < 0 || newX >= this.grid.width) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
// The probed destination is occupied.
|
|
193
|
+
if (this.grid.getCellOverlap(newX, newY, move.itemId)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (isResize && activeId && !this.validateResizeMove(move, activeId)) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
// Find a move that resolves an overlap by moving an item over another item that has not been disturbed yet.
|
|
204
|
+
findPriorityMove(overlap, priorities, activeId, isResize) {
|
|
205
|
+
const overlapItem = this.grid.getItem(overlap);
|
|
206
|
+
const overlapWith = this.getOverlapWith(overlapItem);
|
|
207
|
+
const directions = isResize ? this.getResizeDirections(overlapWith) : this.getMoveDirections(overlapWith);
|
|
208
|
+
for (const direction of directions) {
|
|
209
|
+
const move = this.getMoveForDirection(overlapItem, overlapWith, direction, "PRIORITY");
|
|
210
|
+
if (this.validatePriorityMove(move, priorities, activeId, isResize)) {
|
|
211
|
+
return move;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// If can't find a good move - "escape" item to the bottom.
|
|
215
|
+
const move = {
|
|
216
|
+
itemId: overlapItem.id,
|
|
217
|
+
y: overlapItem.y + 1,
|
|
218
|
+
x: overlapItem.x,
|
|
219
|
+
width: overlapItem.width,
|
|
220
|
+
height: overlapItem.height,
|
|
221
|
+
type: "ESCAPE",
|
|
222
|
+
};
|
|
223
|
+
for (move.y; move.y < 100; move.y++) {
|
|
224
|
+
if (this.validatePriorityMove(move, priorities, activeId, false)) {
|
|
225
|
+
return move;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
throw new Error("Invariant violation: can't find escape move.");
|
|
229
|
+
}
|
|
230
|
+
validatePriorityMove(move, priorities, activeId, isResize) {
|
|
231
|
+
var _a;
|
|
232
|
+
const moveTarget = this.grid.getItem(move.itemId);
|
|
233
|
+
for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
|
|
234
|
+
for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
|
|
235
|
+
const newY = move.y + (y - moveTarget.y);
|
|
236
|
+
const newX = move.x + (x - moveTarget.x);
|
|
237
|
+
// Outside the grid.
|
|
238
|
+
if (newY < 0 || newX < 0 || newX >= this.grid.width) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
for (const item of this.grid.getCell(newX, newY)) {
|
|
242
|
+
// Can't overlap with the active item.
|
|
243
|
+
if (item.id === activeId) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
// The overlapping item has same or bigger priority.
|
|
247
|
+
if (((_a = priorities.get(item.id)) !== null && _a !== void 0 ? _a : 0) >= this.getMovePriority(move)) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
// The probed destination is currently blocked.
|
|
251
|
+
if (this.conflicts.has(item.id)) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (isResize && activeId && !this.validateResizeMove(move, activeId)) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
validateResizeMove(move, activeId) {
|
|
263
|
+
const resizeTarget = this.grid.getItem(activeId);
|
|
264
|
+
const moveTarget = this.grid.getItem(move.itemId);
|
|
265
|
+
const diff = this.getLastStepDiff(resizeTarget);
|
|
266
|
+
const direction = diff.x ? "horizontal" : "vertical";
|
|
267
|
+
const originalPlacement = {
|
|
268
|
+
isNext: resizeTarget.x + resizeTarget.originalWidth - 1 < moveTarget.x,
|
|
269
|
+
isBelow: resizeTarget.y + resizeTarget.originalHeight - 1 < moveTarget.y,
|
|
270
|
+
};
|
|
271
|
+
const nextPlacement = {
|
|
272
|
+
isNext: resizeTarget.x + resizeTarget.width - 1 < move.x,
|
|
273
|
+
isBelow: resizeTarget.y + resizeTarget.height - 1 < move.y,
|
|
274
|
+
};
|
|
275
|
+
return ((direction === "horizontal" && originalPlacement.isNext === nextPlacement.isNext) ||
|
|
276
|
+
(direction === "vertical" && originalPlacement.isBelow === nextPlacement.isBelow));
|
|
277
|
+
}
|
|
278
|
+
getOverlapWith(targetItem) {
|
|
279
|
+
for (let y = targetItem.y; y < targetItem.y + targetItem.height; y++) {
|
|
280
|
+
for (let x = targetItem.x; x < targetItem.x + targetItem.width; x++) {
|
|
281
|
+
const overlap = this.grid.getCellOverlap(x, y, targetItem.id);
|
|
282
|
+
if (overlap) {
|
|
283
|
+
return overlap;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
throw new Error("Invariant violation - no overlaps found.");
|
|
288
|
+
}
|
|
289
|
+
// Retrieve first possible move for the given direction to resolve the overlap.
|
|
290
|
+
getMoveForDirection(moveTarget, overlap, direction, moveType) {
|
|
291
|
+
const common = { itemId: moveTarget.id, width: moveTarget.width, height: moveTarget.height, type: moveType };
|
|
292
|
+
switch (direction) {
|
|
293
|
+
case "up": {
|
|
294
|
+
return { ...common, y: overlap.top - moveTarget.height, x: moveTarget.x };
|
|
295
|
+
}
|
|
296
|
+
case "down": {
|
|
297
|
+
return { ...common, y: overlap.bottom + 1, x: moveTarget.x };
|
|
298
|
+
}
|
|
299
|
+
case "left": {
|
|
300
|
+
return { ...common, y: moveTarget.y, x: overlap.left - moveTarget.width };
|
|
301
|
+
}
|
|
302
|
+
case "right": {
|
|
303
|
+
return { ...common, y: moveTarget.y, x: overlap.right + 1 };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GridLayout, ItemId } from "../interfaces";
|
|
2
|
+
import { InsertCommand, LayoutShift, MoveCommand, ResizeCommand } from "./interfaces";
|
|
3
|
+
export declare class LayoutEngine {
|
|
4
|
+
private current;
|
|
5
|
+
private grid;
|
|
6
|
+
private moves;
|
|
7
|
+
private conflicts;
|
|
8
|
+
private chained;
|
|
9
|
+
constructor(args: GridLayout | LayoutEngine);
|
|
10
|
+
move(moveCommand: MoveCommand): LayoutEngine;
|
|
11
|
+
resize(resize: ResizeCommand): LayoutEngine;
|
|
12
|
+
insert({ itemId, width, height, path: [position, ...path] }: InsertCommand): LayoutEngine;
|
|
13
|
+
remove(itemId: ItemId): LayoutEngine;
|
|
14
|
+
refloat(): LayoutEngine;
|
|
15
|
+
getLayoutShift(): LayoutShift;
|
|
16
|
+
private cleanup;
|
|
17
|
+
private validateMoveCommand;
|
|
18
|
+
private validateResizeCommand;
|
|
19
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Position } from "../utils/position";
|
|
4
|
+
import { findConflicts } from "./engine-conflicts";
|
|
5
|
+
import { refloatGrid, resolveOverlaps } from "./engine-step";
|
|
6
|
+
import { LayoutEngineGrid } from "./grid";
|
|
7
|
+
import { normalizeMovePath, normalizeResizePath, sortGridItems } from "./utils";
|
|
8
|
+
export class LayoutEngine {
|
|
9
|
+
constructor(args) {
|
|
10
|
+
this.moves = [];
|
|
11
|
+
this.conflicts = new Set();
|
|
12
|
+
this.chained = false;
|
|
13
|
+
if (args instanceof LayoutEngine) {
|
|
14
|
+
this.current = args.current;
|
|
15
|
+
this.grid = args.grid;
|
|
16
|
+
this.moves = args.moves;
|
|
17
|
+
this.conflicts = args.conflicts;
|
|
18
|
+
this.chained = true;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.current = args;
|
|
22
|
+
this.grid = new LayoutEngineGrid(args.items, args.columns);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
move(moveCommand) {
|
|
26
|
+
this.cleanup();
|
|
27
|
+
const { itemId, path } = this.validateMoveCommand(moveCommand);
|
|
28
|
+
for (let stepIndex = 0; stepIndex < path.length; stepIndex++) {
|
|
29
|
+
const step = path[stepIndex];
|
|
30
|
+
const { width, height } = this.grid.getItem(itemId);
|
|
31
|
+
const move = { itemId, x: step.x, y: step.y, width, height, type: "MOVE" };
|
|
32
|
+
this.conflicts = findConflicts(move, this.grid);
|
|
33
|
+
resolveOverlaps(move, this.grid, this.moves, this.conflicts);
|
|
34
|
+
}
|
|
35
|
+
return new LayoutEngine(this);
|
|
36
|
+
}
|
|
37
|
+
resize(resize) {
|
|
38
|
+
this.cleanup();
|
|
39
|
+
const { itemId, path } = this.validateResizeCommand(resize);
|
|
40
|
+
const resizeTarget = this.grid.getItem(itemId);
|
|
41
|
+
for (let stepIndex = 0; stepIndex < path.length; stepIndex++) {
|
|
42
|
+
const width = path[stepIndex].x - resizeTarget.x;
|
|
43
|
+
const height = path[stepIndex].y - resizeTarget.y;
|
|
44
|
+
resolveOverlaps({ itemId, x: resizeTarget.x, y: resizeTarget.y, width, height, type: "RESIZE" }, this.grid, this.moves, this.conflicts);
|
|
45
|
+
}
|
|
46
|
+
return new LayoutEngine(this);
|
|
47
|
+
}
|
|
48
|
+
insert({ itemId, width, height, path: [position, ...path] }) {
|
|
49
|
+
this.cleanup();
|
|
50
|
+
resolveOverlaps({ itemId, ...position, width, height, type: "INSERT" }, this.grid, this.moves, this.conflicts);
|
|
51
|
+
return new LayoutEngine(this).move({ itemId, path });
|
|
52
|
+
}
|
|
53
|
+
remove(itemId) {
|
|
54
|
+
this.cleanup();
|
|
55
|
+
const { x, y, width, height } = this.grid.getItem(itemId);
|
|
56
|
+
resolveOverlaps({ itemId, x, y, width, height, type: "REMOVE" }, this.grid, this.moves, this.conflicts);
|
|
57
|
+
return new LayoutEngine(this);
|
|
58
|
+
}
|
|
59
|
+
refloat() {
|
|
60
|
+
refloatGrid(this.grid, this.moves, this.conflicts);
|
|
61
|
+
return new LayoutEngine(this);
|
|
62
|
+
}
|
|
63
|
+
getLayoutShift() {
|
|
64
|
+
return {
|
|
65
|
+
current: this.current,
|
|
66
|
+
next: {
|
|
67
|
+
items: sortGridItems(this.grid.items.map((item) => ({ ...item }))),
|
|
68
|
+
columns: this.grid.width,
|
|
69
|
+
rows: this.grid.height,
|
|
70
|
+
},
|
|
71
|
+
moves: [...this.moves],
|
|
72
|
+
conflicts: [...this.conflicts],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
cleanup() {
|
|
76
|
+
if (!this.chained) {
|
|
77
|
+
this.grid = new LayoutEngineGrid(this.current.items, this.current.columns);
|
|
78
|
+
this.moves = [];
|
|
79
|
+
this.conflicts = new Set();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
validateMoveCommand({ itemId, path }) {
|
|
83
|
+
const moveTarget = this.grid.getItem(itemId);
|
|
84
|
+
for (const step of path) {
|
|
85
|
+
if (step.x < 0 || step.y < 0 || step.x + moveTarget.width > this.grid.width) {
|
|
86
|
+
throw new Error("Invalid move: outside grid.");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { itemId, path: normalizeMovePath(new Position({ x: moveTarget.x, y: moveTarget.y }), path) };
|
|
90
|
+
}
|
|
91
|
+
validateResizeCommand({ itemId, path }) {
|
|
92
|
+
const resizeTarget = this.grid.getItem(itemId);
|
|
93
|
+
const x = resizeTarget.x + resizeTarget.width;
|
|
94
|
+
const y = resizeTarget.y + resizeTarget.height;
|
|
95
|
+
for (const step of path) {
|
|
96
|
+
if (step.x < 1 || step.y < 1) {
|
|
97
|
+
throw new Error("Invalid resize: can't resize to 0.");
|
|
98
|
+
}
|
|
99
|
+
if (step.x > this.grid.width) {
|
|
100
|
+
throw new Error("Invalid resize: outside grid.");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { itemId, path: normalizeResizePath(new Position({ x, y }), path) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GridLayoutItem, ItemId } from "../interfaces";
|
|
2
|
+
import { Rect } from "./utils";
|
|
3
|
+
export interface LayoutEngineItem extends GridLayoutItem, Rect {
|
|
4
|
+
originalX: number;
|
|
5
|
+
originalY: number;
|
|
6
|
+
originalWidth: number;
|
|
7
|
+
originalHeight: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class LayoutEngineGrid {
|
|
10
|
+
private _width;
|
|
11
|
+
private _height;
|
|
12
|
+
private _items;
|
|
13
|
+
private layout;
|
|
14
|
+
constructor(items: readonly GridLayoutItem[], columns: number);
|
|
15
|
+
get width(): number;
|
|
16
|
+
get height(): number;
|
|
17
|
+
get items(): LayoutEngineItem[];
|
|
18
|
+
getItem(itemId: ItemId): LayoutEngineItem;
|
|
19
|
+
getCell(x: number, y: number): LayoutEngineItem[];
|
|
20
|
+
getCellOverlap(x: number, y: number, itemId: ItemId): null | LayoutEngineItem;
|
|
21
|
+
move(itemId: ItemId, x: number, y: number, onOverlap?: (overlapId: ItemId) => void): void;
|
|
22
|
+
resize(itemId: ItemId, width: number, height: number, onOverlap?: (overlapId: ItemId) => void): void;
|
|
23
|
+
insert(item: GridLayoutItem, onOverlap: (overlapId: ItemId) => void): void;
|
|
24
|
+
remove(itemId: ItemId): void;
|
|
25
|
+
private removeLayoutItem;
|
|
26
|
+
private insertLayoutItem;
|
|
27
|
+
private makeNewRow;
|
|
28
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { getItemRect } from "./utils";
|
|
4
|
+
export class LayoutEngineGrid {
|
|
5
|
+
constructor(items, columns) {
|
|
6
|
+
this._items = new Map();
|
|
7
|
+
this.layout = [];
|
|
8
|
+
this._width = columns;
|
|
9
|
+
this._height = 0;
|
|
10
|
+
for (const item of items) {
|
|
11
|
+
this._items.set(item.id, {
|
|
12
|
+
...item,
|
|
13
|
+
originalY: item.y,
|
|
14
|
+
originalX: item.x,
|
|
15
|
+
originalWidth: item.width,
|
|
16
|
+
originalHeight: item.height,
|
|
17
|
+
...getItemRect(item),
|
|
18
|
+
});
|
|
19
|
+
if (item.x < 0 || item.y < 0 || item.x + item.width > this._width) {
|
|
20
|
+
throw new Error("Invalid grid: items outside the boundaries.");
|
|
21
|
+
}
|
|
22
|
+
if (item.width < 1 || item.height < 1) {
|
|
23
|
+
throw new Error("Invalid grid: items of invalid size.");
|
|
24
|
+
}
|
|
25
|
+
for (let y = item.y; y < item.y + item.height; y++) {
|
|
26
|
+
while (this.layout.length <= y) {
|
|
27
|
+
this.makeNewRow();
|
|
28
|
+
}
|
|
29
|
+
for (let x = item.x; x < item.x + item.width; x++) {
|
|
30
|
+
this.layout[y][x].add(item.id);
|
|
31
|
+
if (this.layout[y][x].size > 1) {
|
|
32
|
+
throw new Error("Invalid grid: items overlap.");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
get width() {
|
|
39
|
+
return this._width;
|
|
40
|
+
}
|
|
41
|
+
get height() {
|
|
42
|
+
return this._height;
|
|
43
|
+
}
|
|
44
|
+
get items() {
|
|
45
|
+
return [...this._items.values()];
|
|
46
|
+
}
|
|
47
|
+
getItem(itemId) {
|
|
48
|
+
const item = this._items.get(itemId);
|
|
49
|
+
if (!item) {
|
|
50
|
+
throw new Error(`Item with id "${itemId}" not found in the grid.`);
|
|
51
|
+
}
|
|
52
|
+
return item;
|
|
53
|
+
}
|
|
54
|
+
getCell(x, y) {
|
|
55
|
+
if (!this.layout[y] || !this.layout[y][x]) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return [...this.layout[y][x]].map((itemId) => this.getItem(itemId));
|
|
59
|
+
}
|
|
60
|
+
getCellOverlap(x, y, itemId) {
|
|
61
|
+
for (const item of this.getCell(x, y)) {
|
|
62
|
+
if (item.id !== itemId) {
|
|
63
|
+
return item;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
move(itemId, x, y, onOverlap) {
|
|
69
|
+
const moveTarget = this.getItem(itemId);
|
|
70
|
+
this.removeLayoutItem(moveTarget);
|
|
71
|
+
moveTarget.x = x;
|
|
72
|
+
moveTarget.y = y;
|
|
73
|
+
Object.assign(moveTarget, getItemRect(moveTarget));
|
|
74
|
+
this.insertLayoutItem(moveTarget, onOverlap);
|
|
75
|
+
}
|
|
76
|
+
resize(itemId, width, height, onOverlap) {
|
|
77
|
+
const resizeTarget = this.getItem(itemId);
|
|
78
|
+
this.removeLayoutItem(resizeTarget);
|
|
79
|
+
resizeTarget.width = width;
|
|
80
|
+
resizeTarget.height = height;
|
|
81
|
+
Object.assign(resizeTarget, getItemRect(resizeTarget));
|
|
82
|
+
this.insertLayoutItem(resizeTarget, onOverlap);
|
|
83
|
+
}
|
|
84
|
+
insert(item, onOverlap) {
|
|
85
|
+
this._items.set(item.id, {
|
|
86
|
+
...item,
|
|
87
|
+
originalY: item.y,
|
|
88
|
+
originalX: item.x,
|
|
89
|
+
originalWidth: item.width,
|
|
90
|
+
originalHeight: item.height,
|
|
91
|
+
...getItemRect(item),
|
|
92
|
+
});
|
|
93
|
+
if (item.x < 0 || item.y < 0 || item.x + item.width > this._width) {
|
|
94
|
+
throw new Error("Inserting item is outside the boundaries.");
|
|
95
|
+
}
|
|
96
|
+
if (item.width < 1 || item.height < 1) {
|
|
97
|
+
throw new Error("Inserting item has invalid size.");
|
|
98
|
+
}
|
|
99
|
+
for (let y = item.y; y < item.y + item.height; y++) {
|
|
100
|
+
while (this.layout.length <= y) {
|
|
101
|
+
this.makeNewRow();
|
|
102
|
+
}
|
|
103
|
+
for (let x = item.x; x < item.x + item.width; x++) {
|
|
104
|
+
for (const overlapId of this.layout[y][x]) {
|
|
105
|
+
onOverlap(overlapId);
|
|
106
|
+
}
|
|
107
|
+
this.layout[y][x].add(item.id);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
remove(itemId) {
|
|
112
|
+
const removeTarget = this.getItem(itemId);
|
|
113
|
+
this._items.delete(itemId);
|
|
114
|
+
this.removeLayoutItem(removeTarget);
|
|
115
|
+
}
|
|
116
|
+
removeLayoutItem(item) {
|
|
117
|
+
for (let y = item.y; y < item.y + item.height; y++) {
|
|
118
|
+
for (let x = item.x; x < item.x + item.width; x++) {
|
|
119
|
+
this.layout[y][x].delete(item.id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
insertLayoutItem(item, onOverlap) {
|
|
124
|
+
for (let y = item.y; y < item.y + item.height; y++) {
|
|
125
|
+
for (let x = item.x; x < item.x + item.width; x++) {
|
|
126
|
+
while (!this.layout[y]) {
|
|
127
|
+
this.makeNewRow();
|
|
128
|
+
}
|
|
129
|
+
for (const overlapId of this.layout[y][x]) {
|
|
130
|
+
onOverlap === null || onOverlap === void 0 ? void 0 : onOverlap(overlapId);
|
|
131
|
+
}
|
|
132
|
+
this.layout[y][x].add(item.id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
makeNewRow() {
|
|
137
|
+
this.layout.push([...Array(this._width)].map(() => new Set()));
|
|
138
|
+
this._height = this.layout.length;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GridLayout, ItemId } from "../interfaces";
|
|
2
|
+
import { Position } from "../utils/position";
|
|
3
|
+
export interface MoveCommand {
|
|
4
|
+
itemId: ItemId;
|
|
5
|
+
path: readonly Position[];
|
|
6
|
+
}
|
|
7
|
+
export interface ResizeCommand {
|
|
8
|
+
itemId: ItemId;
|
|
9
|
+
path: readonly Position[];
|
|
10
|
+
}
|
|
11
|
+
export interface InsertCommand {
|
|
12
|
+
itemId: ItemId;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
path: readonly Position[];
|
|
16
|
+
}
|
|
17
|
+
export interface CommittedMove {
|
|
18
|
+
itemId: ItemId;
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
type: "MOVE" | "VACANT" | "PRIORITY" | "ESCAPE" | "FLOAT" | "RESIZE" | "INSERT" | "REMOVE";
|
|
24
|
+
}
|
|
25
|
+
export interface LayoutShift {
|
|
26
|
+
current: GridLayout;
|
|
27
|
+
next: GridLayout;
|
|
28
|
+
moves: readonly CommittedMove[];
|
|
29
|
+
conflicts: readonly ItemId[];
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GridLayoutItem } from "../interfaces";
|
|
2
|
+
import { Position } from "../utils/position";
|
|
3
|
+
export interface Rect {
|
|
4
|
+
left: number;
|
|
5
|
+
right: number;
|
|
6
|
+
top: number;
|
|
7
|
+
bottom: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function sortGridItems(items: readonly GridLayoutItem[]): readonly GridLayoutItem[];
|
|
10
|
+
export declare function getItemRect(item: GridLayoutItem): Rect;
|
|
11
|
+
export declare function normalizeMovePath(origin: Position, path: readonly Position[]): readonly Position[];
|
|
12
|
+
export declare function normalizeResizePath(origin: Position, path: readonly Position[]): readonly Position[];
|