@cloudscape-design/board-components 3.0.1 → 3.0.3
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/board/interfaces.d.ts +2 -2
- package/board/internal.js +7 -7
- package/board/transition.d.ts +3 -2
- package/board/transition.js +20 -7
- package/internal/dnd-controller/controller.d.ts +4 -5
- package/internal/dnd-controller/controller.js +13 -15
- package/internal/dnd-controller/get-hovered-droppables.d.ts +6 -0
- package/internal/dnd-controller/get-hovered-droppables.js +10 -0
- package/internal/environment.js +1 -1
- package/internal/item-container/get-collision-rect.d.ts +10 -0
- package/internal/item-container/get-collision-rect.js +23 -0
- package/internal/item-container/index.js +8 -1
- package/internal/layout-engine/engine-step.d.ts +9 -3
- package/internal/layout-engine/engine-step.js +79 -10
- package/internal/layout-engine/engine.d.ts +1 -3
- package/internal/layout-engine/engine.js +21 -29
- package/internal/layout-engine/grid.d.ts +9 -6
- package/internal/layout-engine/grid.js +28 -18
- package/internal/manifest.json +1 -1
- package/package.json +1 -1
- package/internal/dnd-controller/collision.d.ts +0 -17
- package/internal/dnd-controller/collision.js +0 -32
- package/internal/layout-engine/engine-conflicts.d.ts +0 -4
- package/internal/layout-engine/engine-conflicts.js +0 -53
package/board/interfaces.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
2
|
import { InteractionType, Operation } from "../internal/dnd-controller/controller";
|
|
3
|
-
import { BoardItemDefinition, BoardItemDefinitionBase, DataFallbackType, Direction, GridLayout, GridLayoutItem, ItemId } from "../internal/interfaces";
|
|
3
|
+
import { BoardItemDefinition, BoardItemDefinitionBase, DataFallbackType, Direction, GridLayout, GridLayoutItem, ItemId, Rect } from "../internal/interfaces";
|
|
4
4
|
import { LayoutShift } from "../internal/layout-engine/interfaces";
|
|
5
5
|
import { NonCancelableEventHandler } from "../internal/utils/events";
|
|
6
6
|
import { Position } from "../internal/utils/position";
|
|
@@ -124,7 +124,7 @@ export interface Transition<D> {
|
|
|
124
124
|
itemsLayout: GridLayout;
|
|
125
125
|
insertionDirection: null | Direction;
|
|
126
126
|
draggableItem: BoardItemDefinitionBase<D>;
|
|
127
|
-
|
|
127
|
+
draggableRect: Rect;
|
|
128
128
|
acquiredItem: null | BoardItemDefinitionBase<D>;
|
|
129
129
|
collisionIds: Set<ItemId>;
|
|
130
130
|
layoutShift: null | LayoutShift;
|
package/board/internal.js
CHANGED
|
@@ -71,16 +71,15 @@ export function InternalBoard({ items, renderItem, onItemsChange, empty, i18nStr
|
|
|
71
71
|
}, [removeTransition, items]);
|
|
72
72
|
const rows = selectTransitionRows(transitionState) || itemsLayout.rows;
|
|
73
73
|
const placeholdersLayout = createPlaceholdersLayout(rows, columns);
|
|
74
|
-
function isElementOverBoard(
|
|
74
|
+
function isElementOverBoard(rect) {
|
|
75
75
|
const board = containerAccessRef.current;
|
|
76
76
|
const boardContains = (target) => board === target || board.contains(target);
|
|
77
|
-
const rect = draggableElement.getBoundingClientRect();
|
|
78
77
|
return (boardContains(document.elementFromPoint(rect.left, rect.top)) ||
|
|
79
78
|
boardContains(document.elementFromPoint(rect.right, rect.top)) ||
|
|
80
79
|
boardContains(document.elementFromPoint(rect.right, rect.bottom)) ||
|
|
81
80
|
boardContains(document.elementFromPoint(rect.left, rect.bottom)));
|
|
82
81
|
}
|
|
83
|
-
useDragSubscription("start", ({ operation, interactionType, draggableItem,
|
|
82
|
+
useDragSubscription("start", ({ operation, interactionType, draggableItem, collisionRect, collisionIds }) => {
|
|
84
83
|
dispatch({
|
|
85
84
|
type: "init",
|
|
86
85
|
operation,
|
|
@@ -90,16 +89,17 @@ export function InternalBoard({ items, renderItem, onItemsChange, empty, i18nStr
|
|
|
90
89
|
// The code only works assuming the board can take any draggable.
|
|
91
90
|
// If draggables can be of different types a check of some sort is required here.
|
|
92
91
|
draggableItem: draggableItem,
|
|
93
|
-
|
|
94
|
-
collisionIds: interactionType === "keyboard" || isElementOverBoard(
|
|
92
|
+
draggableRect: collisionRect,
|
|
93
|
+
collisionIds: interactionType === "keyboard" || isElementOverBoard(collisionRect) ? collisionIds : [],
|
|
95
94
|
});
|
|
96
95
|
autoScrollHandlers.addPointerEventHandlers();
|
|
97
96
|
});
|
|
98
|
-
useDragSubscription("update", ({ interactionType, collisionIds, positionOffset,
|
|
97
|
+
useDragSubscription("update", ({ interactionType, collisionIds, positionOffset, collisionRect }) => {
|
|
99
98
|
dispatch({
|
|
100
99
|
type: "update-with-pointer",
|
|
101
|
-
collisionIds: interactionType === "keyboard" || isElementOverBoard(
|
|
100
|
+
collisionIds: interactionType === "keyboard" || isElementOverBoard(collisionRect) ? collisionIds : [],
|
|
102
101
|
positionOffset,
|
|
102
|
+
draggableRect: collisionRect,
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
useDragSubscription("submit", () => {
|
package/board/transition.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dispatch } from "react";
|
|
2
2
|
import { InteractionType, Operation } from "../internal/dnd-controller/controller";
|
|
3
|
-
import { BoardItemDefinitionBase, Direction, GridLayout, ItemId } from "../internal/interfaces";
|
|
3
|
+
import { BoardItemDefinitionBase, Direction, GridLayout, ItemId, Rect } from "../internal/interfaces";
|
|
4
4
|
import { Coordinates } from "../internal/utils/coordinates";
|
|
5
5
|
import { Position } from "../internal/utils/position";
|
|
6
6
|
import { BoardProps, RemoveTransition, Transition, TransitionAnnouncement } from "./interfaces";
|
|
@@ -16,7 +16,7 @@ interface InitAction<D> {
|
|
|
16
16
|
interactionType: InteractionType;
|
|
17
17
|
itemsLayout: GridLayout;
|
|
18
18
|
draggableItem: BoardItemDefinitionBase<D>;
|
|
19
|
-
|
|
19
|
+
draggableRect: Rect;
|
|
20
20
|
collisionIds: readonly ItemId[];
|
|
21
21
|
}
|
|
22
22
|
interface InitRemoveAction<D> {
|
|
@@ -35,6 +35,7 @@ interface UpdateWithPointerAction {
|
|
|
35
35
|
type: "update-with-pointer";
|
|
36
36
|
collisionIds: readonly ItemId[];
|
|
37
37
|
positionOffset: Coordinates;
|
|
38
|
+
draggableRect: Rect;
|
|
38
39
|
}
|
|
39
40
|
interface UpdateWithKeyboardAction {
|
|
40
41
|
type: "update-with-keyboard";
|
package/board/transition.js
CHANGED
|
@@ -33,14 +33,14 @@ function transitionReducer(state, action) {
|
|
|
33
33
|
return acquireTransitionItem(state, action);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
function initTransition({ operation, interactionType, itemsLayout, draggableItem,
|
|
36
|
+
function initTransition({ operation, interactionType, itemsLayout, draggableItem, draggableRect, collisionIds, }) {
|
|
37
37
|
const transition = {
|
|
38
38
|
operation,
|
|
39
39
|
interactionType,
|
|
40
40
|
itemsLayout,
|
|
41
41
|
insertionDirection: null,
|
|
42
42
|
draggableItem,
|
|
43
|
-
|
|
43
|
+
draggableRect,
|
|
44
44
|
acquiredItem: null,
|
|
45
45
|
collisionIds: new Set(),
|
|
46
46
|
layoutShift: null,
|
|
@@ -107,7 +107,7 @@ function discardTransition(state) {
|
|
|
107
107
|
announcement: itemBelongsToBoard ? { type: "dnd-discarded", item, operation } : null,
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
-
function updateTransitionWithPointerEvent(state, { collisionIds, positionOffset }) {
|
|
110
|
+
function updateTransitionWithPointerEvent(state, { collisionIds, positionOffset, draggableRect }) {
|
|
111
111
|
var _a, _b, _c;
|
|
112
112
|
const { transition } = state;
|
|
113
113
|
if (!transition) {
|
|
@@ -121,7 +121,13 @@ function updateTransitionWithPointerEvent(state, { collisionIds, positionOffset
|
|
|
121
121
|
const isOutOfBoundaries = transition.operation !== "resize" ? collisionIds.length < itemSize : collisionIds.length === 0;
|
|
122
122
|
if (isOutOfBoundaries) {
|
|
123
123
|
return {
|
|
124
|
-
transition: {
|
|
124
|
+
transition: {
|
|
125
|
+
...transition,
|
|
126
|
+
draggableRect,
|
|
127
|
+
collisionIds: new Set(),
|
|
128
|
+
layoutShift: null,
|
|
129
|
+
insertionDirection: null,
|
|
130
|
+
},
|
|
125
131
|
removeTransition: null,
|
|
126
132
|
announcement: null,
|
|
127
133
|
};
|
|
@@ -133,7 +139,14 @@ function updateTransitionWithPointerEvent(state, { collisionIds, positionOffset
|
|
|
133
139
|
const insertionDirection = (_c = transition.insertionDirection) !== null && _c !== void 0 ? _c : getInsertionDirection(positionOffset);
|
|
134
140
|
const layoutShift = getLayoutShift(transition, path, insertionDirection);
|
|
135
141
|
return {
|
|
136
|
-
transition: {
|
|
142
|
+
transition: {
|
|
143
|
+
...transition,
|
|
144
|
+
draggableRect,
|
|
145
|
+
collisionIds: new Set(collisionIds),
|
|
146
|
+
layoutShift,
|
|
147
|
+
path,
|
|
148
|
+
insertionDirection,
|
|
149
|
+
},
|
|
137
150
|
removeTransition: null,
|
|
138
151
|
announcement: null,
|
|
139
152
|
};
|
|
@@ -192,8 +205,8 @@ function acquireTransitionItem(state, { position, layoutElement }) {
|
|
|
192
205
|
}
|
|
193
206
|
const { columns } = transition.itemsLayout;
|
|
194
207
|
const layoutRect = layoutElement.getBoundingClientRect();
|
|
195
|
-
const itemRect = transition.
|
|
196
|
-
const offset = new Coordinates({ x: itemRect.
|
|
208
|
+
const itemRect = transition.draggableRect;
|
|
209
|
+
const offset = new Coordinates({ x: itemRect.left - layoutRect.x, y: itemRect.top - layoutRect.y });
|
|
197
210
|
const insertionDirection = getInsertionDirection(offset);
|
|
198
211
|
// Update original insertion position if the item can't fit into the layout by width.
|
|
199
212
|
const width = getDefaultColumnSpan(transition.draggableItem, columns);
|
|
@@ -22,10 +22,9 @@ export interface DragAndDropData {
|
|
|
22
22
|
operation: Operation;
|
|
23
23
|
interactionType: InteractionType;
|
|
24
24
|
draggableItem: Item;
|
|
25
|
-
|
|
25
|
+
collisionRect: Rect;
|
|
26
26
|
positionOffset: Coordinates;
|
|
27
27
|
coordinates: Coordinates;
|
|
28
|
-
collisionRect: Rect;
|
|
29
28
|
collisionIds: ItemId[];
|
|
30
29
|
dropTarget: null | DropTargetContext;
|
|
31
30
|
}
|
|
@@ -45,9 +44,9 @@ export interface DragAndDropEvents {
|
|
|
45
44
|
acquire: (data: AcquireData) => void;
|
|
46
45
|
}
|
|
47
46
|
export declare function useDragSubscription<K extends keyof DragAndDropEvents>(event: K, handler: DragAndDropEvents[K]): void;
|
|
48
|
-
export declare function useDraggable({
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
export declare function useDraggable({ draggableItem, getCollisionRect, }: {
|
|
48
|
+
draggableItem: Item;
|
|
49
|
+
getCollisionRect: (operation: Operation, coordinates: Coordinates, dropTarget: null | DropTargetContext) => Rect;
|
|
51
50
|
}): {
|
|
52
51
|
start(operation: Operation, interactionType: InteractionType, startCoordinates: Coordinates): void;
|
|
53
52
|
updateTransition(coordinates: Coordinates): void;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import { Coordinates } from "../utils/coordinates";
|
|
5
|
-
import { getCollisionRect, getHoveredDroppables } from "./collision";
|
|
6
5
|
import { EventEmitter } from "./event-emitter";
|
|
6
|
+
import { getHoveredDroppables } from "./get-hovered-droppables";
|
|
7
7
|
class DragAndDropController extends EventEmitter {
|
|
8
8
|
constructor() {
|
|
9
9
|
super(...arguments);
|
|
@@ -15,15 +15,9 @@ class DragAndDropController extends EventEmitter {
|
|
|
15
15
|
*
|
|
16
16
|
* The method overrides the previous transition if exists (w/o a cancellation event)!
|
|
17
17
|
*/
|
|
18
|
-
start(
|
|
19
|
-
this.transition = {
|
|
20
|
-
|
|
21
|
-
interactionType,
|
|
22
|
-
draggableItem,
|
|
23
|
-
draggableElement,
|
|
24
|
-
startCoordinates,
|
|
25
|
-
};
|
|
26
|
-
this.emit("start", this.getDragAndDropData(startCoordinates));
|
|
18
|
+
start(transition) {
|
|
19
|
+
this.transition = { ...transition };
|
|
20
|
+
this.emit("start", this.getDragAndDropData(transition.startCoordinates));
|
|
27
21
|
}
|
|
28
22
|
/**
|
|
29
23
|
* Updates current transition with given coordinates and issues an "update" event.
|
|
@@ -76,12 +70,16 @@ class DragAndDropController extends EventEmitter {
|
|
|
76
70
|
if (!this.transition) {
|
|
77
71
|
throw new Error("Invariant violation: no transition present for interaction.");
|
|
78
72
|
}
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const collisionRect = getCollisionRect(operation, draggableElement, coordinates);
|
|
73
|
+
const positionOffset = Coordinates.cursorOffset(coordinates, this.transition.startCoordinates);
|
|
74
|
+
const collisionRect = this.getCollisionRect(this.transition, coordinates);
|
|
82
75
|
const { collisionIds, dropTarget } = this.getCollisions(collisionRect);
|
|
83
76
|
return { ...this.transition, positionOffset, coordinates, collisionRect, collisionIds, dropTarget };
|
|
84
77
|
}
|
|
78
|
+
getCollisionRect(transition, coordinates) {
|
|
79
|
+
const originalCollisionRect = transition.getCollisionRect(transition.operation, coordinates, null);
|
|
80
|
+
const { dropTarget } = this.getCollisions(originalCollisionRect);
|
|
81
|
+
return transition.getCollisionRect(transition.operation, coordinates, dropTarget);
|
|
82
|
+
}
|
|
85
83
|
getCollisions(collisionRect) {
|
|
86
84
|
const droppableEntries = [...this.droppables.entries()];
|
|
87
85
|
const droppableElements = droppableEntries.map(([id, entry]) => [id, entry.element]);
|
|
@@ -101,10 +99,10 @@ const controller = new DragAndDropController();
|
|
|
101
99
|
export function useDragSubscription(event, handler) {
|
|
102
100
|
useEffect(() => controller.on(event, handler), [event, handler]);
|
|
103
101
|
}
|
|
104
|
-
export function useDraggable({
|
|
102
|
+
export function useDraggable({ draggableItem, getCollisionRect, }) {
|
|
105
103
|
return {
|
|
106
104
|
start(operation, interactionType, startCoordinates) {
|
|
107
|
-
controller.start(operation, interactionType,
|
|
105
|
+
controller.start({ operation, interactionType, draggableItem, getCollisionRect, startCoordinates });
|
|
108
106
|
},
|
|
109
107
|
updateTransition(coordinates) {
|
|
110
108
|
controller.update(coordinates);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ItemId } from "../interfaces";
|
|
2
|
+
import { Rect } from "../interfaces";
|
|
3
|
+
/**
|
|
4
|
+
* Returns IDs of droppables hovered by the draggable rect.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getHoveredDroppables(collisionRect: Rect, droppables: readonly [ItemId, HTMLElement][]): string[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getGridPlacement, isInside } from "../utils/rects";
|
|
2
|
+
import { getNormalizedElementRect } from "../utils/screen";
|
|
3
|
+
/**
|
|
4
|
+
* Returns IDs of droppables hovered by the draggable rect.
|
|
5
|
+
*/
|
|
6
|
+
export function getHoveredDroppables(collisionRect, droppables) {
|
|
7
|
+
const droppableRects = droppables.map(([, element]) => getNormalizedElementRect(element));
|
|
8
|
+
const bounds = getGridPlacement(collisionRect, droppableRects);
|
|
9
|
+
return droppables.filter((_, index) => isInside(droppableRects[index], bounds)).map(([droppableId]) => droppableId);
|
|
10
|
+
}
|
package/internal/environment.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Operation } from "../dnd-controller/controller";
|
|
2
|
+
import { Rect } from "../interfaces";
|
|
3
|
+
import { Coordinates } from "../utils/coordinates";
|
|
4
|
+
/**
|
|
5
|
+
* Produces a rect (in coordinates) to represent the draggable item.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getCollisionRect(operation: Operation, draggableElement: HTMLElement, coordinates: Coordinates, sizeOverride: null | {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}): Rect;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { getNormalizedElementRect } from "../utils/screen";
|
|
4
|
+
/**
|
|
5
|
+
* Produces a rect (in coordinates) to represent the draggable item.
|
|
6
|
+
*/
|
|
7
|
+
export function getCollisionRect(operation, draggableElement, coordinates, sizeOverride) {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
const { left, top, width, height } = getNormalizedElementRect(draggableElement);
|
|
10
|
+
switch (operation) {
|
|
11
|
+
case "reorder":
|
|
12
|
+
return { left: coordinates.x, right: coordinates.x + width, top: coordinates.y, bottom: coordinates.y + height };
|
|
13
|
+
case "resize":
|
|
14
|
+
return { left: left, top: top, right: coordinates.x, bottom: coordinates.y };
|
|
15
|
+
case "insert":
|
|
16
|
+
return {
|
|
17
|
+
left: coordinates.x,
|
|
18
|
+
top: coordinates.y,
|
|
19
|
+
right: coordinates.x + ((_a = sizeOverride === null || sizeOverride === void 0 ? void 0 : sizeOverride.width) !== null && _a !== void 0 ? _a : width),
|
|
20
|
+
bottom: coordinates.y + ((_b = sizeOverride === null || sizeOverride === void 0 ? void 0 : sizeOverride.height) !== null && _b !== void 0 ? _b : height),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -9,6 +9,7 @@ import { Coordinates } from "../utils/coordinates";
|
|
|
9
9
|
import { getNormalizedElementRect } from "../utils/screen";
|
|
10
10
|
import { useStableEventHandler } from "../utils/use-stable-event-handler";
|
|
11
11
|
import { useThrottledEventHandler } from "../utils/use-throttled-event-handler";
|
|
12
|
+
import { getCollisionRect } from "./get-collision-rect";
|
|
12
13
|
import { getNextDroppable } from "./get-next-droppable";
|
|
13
14
|
import styles from "./styles.css.js";
|
|
14
15
|
const Context = createContext(null);
|
|
@@ -27,7 +28,13 @@ function ItemContainerComponent({ item, placed, acquired, inTransition, transfor
|
|
|
27
28
|
const pointerBoundariesRef = useRef(null);
|
|
28
29
|
const [transition, setTransition] = useState(null);
|
|
29
30
|
const itemRef = useRef(null);
|
|
30
|
-
const draggableApi = useDraggable({
|
|
31
|
+
const draggableApi = useDraggable({
|
|
32
|
+
draggableItem: item,
|
|
33
|
+
getCollisionRect: (operation, coordinates, dropTarget) => {
|
|
34
|
+
const sizeOverride = operation === "insert" && dropTarget ? getItemSize(dropTarget) : null;
|
|
35
|
+
return getCollisionRect(operation, itemRef.current, coordinates, sizeOverride);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
31
38
|
const onPointerMove = useThrottledEventHandler((event) => {
|
|
32
39
|
var _a, _b, _c, _d;
|
|
33
40
|
const coordinates = Coordinates.fromEvent(event);
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ItemId } from "../interfaces";
|
|
2
|
-
import { LayoutEngineGrid } from "./grid";
|
|
2
|
+
import { LayoutEngineGrid, ReadonlyLayoutEngineGrid } from "./grid";
|
|
3
3
|
import { CommittedMove } from "./interfaces";
|
|
4
|
-
export declare
|
|
5
|
-
|
|
4
|
+
export declare class LayoutEngineStepState {
|
|
5
|
+
grid: ReadonlyLayoutEngineGrid;
|
|
6
|
+
moves: readonly CommittedMove[];
|
|
7
|
+
conflicts: ReadonlySet<ItemId>;
|
|
8
|
+
constructor(grid: LayoutEngineGrid, moves?: CommittedMove[], conflicts?: Set<string>);
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveOverlaps(userMove: CommittedMove, state: LayoutEngineStepState): LayoutEngineStepState;
|
|
11
|
+
export declare function refloatGrid(state: LayoutEngineStepState): LayoutEngineStepState;
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { StackSet } from "../utils/stack-set";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { LayoutEngineGrid } from "./grid";
|
|
5
|
+
export class LayoutEngineStepState {
|
|
6
|
+
constructor(grid, moves = new Array(), conflicts = new Set()) {
|
|
7
|
+
this.grid = grid;
|
|
8
|
+
this.moves = moves;
|
|
9
|
+
this.conflicts = conflicts;
|
|
10
|
+
}
|
|
6
11
|
}
|
|
7
|
-
export function
|
|
8
|
-
new LayoutEngineStep(
|
|
12
|
+
export function resolveOverlaps(userMove, state) {
|
|
13
|
+
return new LayoutEngineStep(state).resolveOverlaps(userMove).getState();
|
|
14
|
+
}
|
|
15
|
+
export function refloatGrid(state) {
|
|
16
|
+
return new LayoutEngineStep(state).refloatGrid().getState();
|
|
9
17
|
}
|
|
10
18
|
class LayoutEngineStep {
|
|
11
|
-
constructor(
|
|
19
|
+
constructor(state) {
|
|
12
20
|
this.moves = [];
|
|
13
21
|
this.conflicts = new Set();
|
|
14
|
-
this.grid = grid;
|
|
15
|
-
this.moves = moves;
|
|
16
|
-
this.conflicts = conflicts;
|
|
22
|
+
this.grid = LayoutEngineGrid.clone(state.grid);
|
|
23
|
+
this.moves = [...state.moves];
|
|
24
|
+
this.conflicts = new Set([...state.conflicts]);
|
|
25
|
+
}
|
|
26
|
+
getState() {
|
|
27
|
+
return { grid: this.grid, moves: this.moves, conflicts: this.conflicts };
|
|
17
28
|
}
|
|
18
29
|
// Issue moves on overlapping items trying to resolve all of them.
|
|
19
30
|
resolveOverlaps(userMove) {
|
|
@@ -27,6 +38,7 @@ class LayoutEngineStep {
|
|
|
27
38
|
overlaps.push(itemId);
|
|
28
39
|
}
|
|
29
40
|
};
|
|
41
|
+
this.conflicts = this.findConflicts(userMove);
|
|
30
42
|
this.makeMove(userMove, addOverlap, priorities);
|
|
31
43
|
const tryVacantMoves = () => {
|
|
32
44
|
// Try vacant moves on all overlaps.
|
|
@@ -56,11 +68,12 @@ class LayoutEngineStep {
|
|
|
56
68
|
};
|
|
57
69
|
tryVacantMoves();
|
|
58
70
|
this.refloatGrid(activeId);
|
|
71
|
+
return this;
|
|
59
72
|
}
|
|
60
73
|
// Find items that can "float" to the top and apply the necessary moves.
|
|
61
74
|
refloatGrid(activeId) {
|
|
62
75
|
if (this.conflicts.size > 0) {
|
|
63
|
-
return;
|
|
76
|
+
return this;
|
|
64
77
|
}
|
|
65
78
|
let needAnotherRefloat = false;
|
|
66
79
|
for (const item of this.grid.items) {
|
|
@@ -90,6 +103,7 @@ class LayoutEngineStep {
|
|
|
90
103
|
if (needAnotherRefloat) {
|
|
91
104
|
this.refloatGrid(activeId);
|
|
92
105
|
}
|
|
106
|
+
return this;
|
|
93
107
|
}
|
|
94
108
|
makeMove(move, addOverlap, priorities) {
|
|
95
109
|
switch (move.type) {
|
|
@@ -220,7 +234,8 @@ class LayoutEngineStep {
|
|
|
220
234
|
height: overlapItem.height,
|
|
221
235
|
type: "ESCAPE",
|
|
222
236
|
};
|
|
223
|
-
|
|
237
|
+
// If can't find the escape move after 999 steps down it is likely a bug in the validation.
|
|
238
|
+
for (move.y; move.y < 999; move.y++) {
|
|
224
239
|
if (this.validatePriorityMove(move, priorities, activeId, false)) {
|
|
225
240
|
return move;
|
|
226
241
|
}
|
|
@@ -304,4 +319,58 @@ class LayoutEngineStep {
|
|
|
304
319
|
}
|
|
305
320
|
}
|
|
306
321
|
}
|
|
322
|
+
// Find items that the active item cannot be moved over with the current move.
|
|
323
|
+
findConflicts(move) {
|
|
324
|
+
if (move.type !== "MOVE") {
|
|
325
|
+
return new Set();
|
|
326
|
+
}
|
|
327
|
+
const conflicts = new Set();
|
|
328
|
+
const moveTarget = this.grid.getItem(move.itemId);
|
|
329
|
+
const direction = `${move.x - moveTarget.x}:${move.y - moveTarget.y}`;
|
|
330
|
+
switch (direction) {
|
|
331
|
+
case "-1:0": {
|
|
332
|
+
const left = Math.max(0, moveTarget.left - 1);
|
|
333
|
+
for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
|
|
334
|
+
const block = this.grid.getCellOverlap(left, y, moveTarget.id);
|
|
335
|
+
if (block && block.x < left) {
|
|
336
|
+
conflicts.add(block.id);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case "1:0": {
|
|
342
|
+
const right = Math.min(this.grid.width - 1, moveTarget.right + 1);
|
|
343
|
+
for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
|
|
344
|
+
const block = this.grid.getCellOverlap(right, y, moveTarget.id);
|
|
345
|
+
if (block && block.x + block.width - 1 > right) {
|
|
346
|
+
conflicts.add(block.id);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case "0:-1": {
|
|
352
|
+
const top = Math.max(0, moveTarget.top - 1);
|
|
353
|
+
for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
|
|
354
|
+
const block = this.grid.getCellOverlap(x, top, moveTarget.id);
|
|
355
|
+
if (block && block.y < top) {
|
|
356
|
+
conflicts.add(block.id);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
case "0:1": {
|
|
362
|
+
const bottom = moveTarget.bottom + 1;
|
|
363
|
+
for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
|
|
364
|
+
const block = this.grid.getCellOverlap(x, bottom, moveTarget.id);
|
|
365
|
+
if (block && block.y + block.height - 1 > bottom) {
|
|
366
|
+
conflicts.add(block.id);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
default:
|
|
372
|
+
throw new Error(`Invariant violation: unexpected direction ${direction}.`);
|
|
373
|
+
}
|
|
374
|
+
return conflicts;
|
|
375
|
+
}
|
|
307
376
|
}
|
|
@@ -2,9 +2,7 @@ import { GridLayout, ItemId } from "../interfaces";
|
|
|
2
2
|
import { InsertCommand, LayoutShift, MoveCommand, ResizeCommand } from "./interfaces";
|
|
3
3
|
export declare class LayoutEngine {
|
|
4
4
|
private current;
|
|
5
|
-
private
|
|
6
|
-
private moves;
|
|
7
|
-
private conflicts;
|
|
5
|
+
private step;
|
|
8
6
|
private chained;
|
|
9
7
|
constructor(args: GridLayout | LayoutEngine);
|
|
10
8
|
move(moveCommand: MoveCommand): LayoutEngine;
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Position } from "../utils/position";
|
|
4
|
-
import {
|
|
5
|
-
import { refloatGrid, resolveOverlaps } from "./engine-step";
|
|
4
|
+
import { LayoutEngineStepState, refloatGrid, resolveOverlaps } from "./engine-step";
|
|
6
5
|
import { LayoutEngineGrid } from "./grid";
|
|
7
6
|
import { normalizeMovePath, normalizeResizePath, sortGridItems } from "./utils";
|
|
8
7
|
export class LayoutEngine {
|
|
9
8
|
constructor(args) {
|
|
10
|
-
this.moves = [];
|
|
11
|
-
this.conflicts = new Set();
|
|
12
9
|
this.chained = false;
|
|
13
10
|
if (args instanceof LayoutEngine) {
|
|
14
11
|
this.current = args.current;
|
|
15
|
-
this.
|
|
16
|
-
this.moves = args.moves;
|
|
17
|
-
this.conflicts = args.conflicts;
|
|
12
|
+
this.step = args.step;
|
|
18
13
|
this.chained = true;
|
|
19
14
|
}
|
|
20
15
|
else {
|
|
21
16
|
this.current = args;
|
|
22
|
-
this.
|
|
17
|
+
this.step = new LayoutEngineStepState(new LayoutEngineGrid(args.items, args.columns));
|
|
23
18
|
}
|
|
24
19
|
}
|
|
25
20
|
move(moveCommand) {
|
|
@@ -27,76 +22,73 @@ export class LayoutEngine {
|
|
|
27
22
|
const { itemId, path } = this.validateMoveCommand(moveCommand);
|
|
28
23
|
for (let stepIndex = 0; stepIndex < path.length; stepIndex++) {
|
|
29
24
|
const step = path[stepIndex];
|
|
30
|
-
const { width, height } = this.grid.getItem(itemId);
|
|
25
|
+
const { width, height } = this.step.grid.getItem(itemId);
|
|
31
26
|
const move = { itemId, x: step.x, y: step.y, width, height, type: "MOVE" };
|
|
32
|
-
this.
|
|
33
|
-
resolveOverlaps(move, this.grid, this.moves, this.conflicts);
|
|
27
|
+
this.step = resolveOverlaps(move, this.step);
|
|
34
28
|
}
|
|
35
29
|
return new LayoutEngine(this);
|
|
36
30
|
}
|
|
37
31
|
resize(resize) {
|
|
38
32
|
this.cleanup();
|
|
39
33
|
const { itemId, path } = this.validateResizeCommand(resize);
|
|
40
|
-
const resizeTarget = this.grid.getItem(itemId);
|
|
34
|
+
const resizeTarget = this.step.grid.getItem(itemId);
|
|
41
35
|
for (let stepIndex = 0; stepIndex < path.length; stepIndex++) {
|
|
42
36
|
const width = path[stepIndex].x - resizeTarget.x;
|
|
43
37
|
const height = path[stepIndex].y - resizeTarget.y;
|
|
44
|
-
resolveOverlaps({ itemId, x: resizeTarget.x, y: resizeTarget.y, width, height, type: "RESIZE" }, this.
|
|
38
|
+
this.step = resolveOverlaps({ itemId, x: resizeTarget.x, y: resizeTarget.y, width, height, type: "RESIZE" }, this.step);
|
|
45
39
|
}
|
|
46
40
|
return new LayoutEngine(this);
|
|
47
41
|
}
|
|
48
42
|
insert({ itemId, width, height, path: [position, ...path] }) {
|
|
49
43
|
this.cleanup();
|
|
50
|
-
resolveOverlaps({ itemId, ...position, width, height, type: "INSERT" }, this.
|
|
44
|
+
this.step = resolveOverlaps({ itemId, ...position, width, height, type: "INSERT" }, this.step);
|
|
51
45
|
return new LayoutEngine(this).move({ itemId, path });
|
|
52
46
|
}
|
|
53
47
|
remove(itemId) {
|
|
54
48
|
this.cleanup();
|
|
55
|
-
const { x, y, width, height } = this.grid.getItem(itemId);
|
|
56
|
-
resolveOverlaps({ itemId, x, y, width, height, type: "REMOVE" }, this.
|
|
49
|
+
const { x, y, width, height } = this.step.grid.getItem(itemId);
|
|
50
|
+
this.step = resolveOverlaps({ itemId, x, y, width, height, type: "REMOVE" }, this.step);
|
|
57
51
|
return new LayoutEngine(this);
|
|
58
52
|
}
|
|
59
53
|
refloat() {
|
|
60
|
-
|
|
54
|
+
this.step = refloatGrid(this.step);
|
|
61
55
|
return new LayoutEngine(this);
|
|
62
56
|
}
|
|
63
57
|
getLayoutShift() {
|
|
64
58
|
return {
|
|
65
59
|
current: this.current,
|
|
66
60
|
next: {
|
|
67
|
-
items: sortGridItems(this.grid.items.map((item) => ({ ...item }))),
|
|
68
|
-
columns: this.grid.width,
|
|
69
|
-
rows: this.grid.height,
|
|
61
|
+
items: sortGridItems(this.step.grid.items.map((item) => ({ ...item }))),
|
|
62
|
+
columns: this.step.grid.width,
|
|
63
|
+
rows: this.step.grid.height,
|
|
70
64
|
},
|
|
71
|
-
moves: [...this.moves],
|
|
72
|
-
conflicts: [...this.conflicts],
|
|
65
|
+
moves: [...this.step.moves],
|
|
66
|
+
conflicts: [...this.step.conflicts],
|
|
73
67
|
};
|
|
74
68
|
}
|
|
75
69
|
cleanup() {
|
|
76
70
|
if (!this.chained) {
|
|
77
|
-
this.
|
|
78
|
-
this.moves = [];
|
|
79
|
-
this.conflicts = new Set();
|
|
71
|
+
this.step = new LayoutEngineStepState(new LayoutEngineGrid(this.current.items, this.current.columns));
|
|
80
72
|
}
|
|
81
73
|
}
|
|
82
74
|
validateMoveCommand({ itemId, path }) {
|
|
83
|
-
const moveTarget = this.grid.getItem(itemId);
|
|
75
|
+
const moveTarget = this.step.grid.getItem(itemId);
|
|
84
76
|
for (const step of path) {
|
|
85
|
-
if (step.x < 0 || step.y < 0 || step.x + moveTarget.width > this.grid.width) {
|
|
77
|
+
if (step.x < 0 || step.y < 0 || step.x + moveTarget.width > this.step.grid.width) {
|
|
86
78
|
throw new Error("Invalid move: outside grid.");
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
81
|
return { itemId, path: normalizeMovePath(new Position({ x: moveTarget.x, y: moveTarget.y }), path) };
|
|
90
82
|
}
|
|
91
83
|
validateResizeCommand({ itemId, path }) {
|
|
92
|
-
const resizeTarget = this.grid.getItem(itemId);
|
|
84
|
+
const resizeTarget = this.step.grid.getItem(itemId);
|
|
93
85
|
const x = resizeTarget.x + resizeTarget.width;
|
|
94
86
|
const y = resizeTarget.y + resizeTarget.height;
|
|
95
87
|
for (const step of path) {
|
|
96
88
|
if (step.x < 1 || step.y < 1) {
|
|
97
89
|
throw new Error("Invalid resize: can't resize to 0.");
|
|
98
90
|
}
|
|
99
|
-
if (step.x > this.grid.width) {
|
|
91
|
+
if (step.x > this.step.grid.width) {
|
|
100
92
|
throw new Error("Invalid resize: outside grid.");
|
|
101
93
|
}
|
|
102
94
|
}
|
|
@@ -6,11 +6,12 @@ export interface LayoutEngineItem extends GridLayoutItem, Rect {
|
|
|
6
6
|
originalWidth: number;
|
|
7
7
|
originalHeight: number;
|
|
8
8
|
}
|
|
9
|
-
export declare class
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
export declare class ReadonlyLayoutEngineGrid {
|
|
10
|
+
protected _width: number;
|
|
11
|
+
protected _height: number;
|
|
12
|
+
protected _items: Map<string, LayoutEngineItem>;
|
|
13
|
+
protected _layout: Set<ItemId>[][];
|
|
14
|
+
static clone(grid: ReadonlyLayoutEngineGrid): LayoutEngineGrid;
|
|
14
15
|
constructor(items: readonly GridLayoutItem[], columns: number);
|
|
15
16
|
get width(): number;
|
|
16
17
|
get height(): number;
|
|
@@ -18,11 +19,13 @@ export declare class LayoutEngineGrid {
|
|
|
18
19
|
getItem(itemId: ItemId): LayoutEngineItem;
|
|
19
20
|
getCell(x: number, y: number): LayoutEngineItem[];
|
|
20
21
|
getCellOverlap(x: number, y: number, itemId: ItemId): null | LayoutEngineItem;
|
|
22
|
+
protected makeNewRow(): void;
|
|
23
|
+
}
|
|
24
|
+
export declare class LayoutEngineGrid extends ReadonlyLayoutEngineGrid {
|
|
21
25
|
move(itemId: ItemId, x: number, y: number, onOverlap?: (overlapId: ItemId) => void): void;
|
|
22
26
|
resize(itemId: ItemId, width: number, height: number, onOverlap?: (overlapId: ItemId) => void): void;
|
|
23
27
|
insert(item: GridLayoutItem, onOverlap: (overlapId: ItemId) => void): void;
|
|
24
28
|
remove(itemId: ItemId): void;
|
|
25
29
|
private removeLayoutItem;
|
|
26
30
|
private insertLayoutItem;
|
|
27
|
-
private makeNewRow;
|
|
28
31
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { getItemRect } from "./utils";
|
|
4
|
-
export class
|
|
4
|
+
export class ReadonlyLayoutEngineGrid {
|
|
5
|
+
static clone(grid) {
|
|
6
|
+
const clone = new LayoutEngineGrid([], 0);
|
|
7
|
+
clone._width = grid._width;
|
|
8
|
+
clone._height = grid._height;
|
|
9
|
+
clone._items = new Map([...grid._items].map(([k, v]) => [k, { ...v }]));
|
|
10
|
+
clone._layout = grid._layout.map((row) => row.map((set) => new Set([...set])));
|
|
11
|
+
return clone;
|
|
12
|
+
}
|
|
5
13
|
constructor(items, columns) {
|
|
6
14
|
this._items = new Map();
|
|
7
|
-
this.
|
|
15
|
+
this._layout = [];
|
|
8
16
|
this._width = columns;
|
|
9
17
|
this._height = 0;
|
|
10
18
|
for (const item of items) {
|
|
@@ -23,12 +31,12 @@ export class LayoutEngineGrid {
|
|
|
23
31
|
throw new Error("Invalid grid: items of invalid size.");
|
|
24
32
|
}
|
|
25
33
|
for (let y = item.y; y < item.y + item.height; y++) {
|
|
26
|
-
while (this.
|
|
34
|
+
while (this._layout.length <= y) {
|
|
27
35
|
this.makeNewRow();
|
|
28
36
|
}
|
|
29
37
|
for (let x = item.x; x < item.x + item.width; x++) {
|
|
30
|
-
this.
|
|
31
|
-
if (this.
|
|
38
|
+
this._layout[y][x].add(item.id);
|
|
39
|
+
if (this._layout[y][x].size > 1) {
|
|
32
40
|
throw new Error("Invalid grid: items overlap.");
|
|
33
41
|
}
|
|
34
42
|
}
|
|
@@ -52,10 +60,10 @@ export class LayoutEngineGrid {
|
|
|
52
60
|
return item;
|
|
53
61
|
}
|
|
54
62
|
getCell(x, y) {
|
|
55
|
-
if (!this.
|
|
63
|
+
if (!this._layout[y] || !this._layout[y][x]) {
|
|
56
64
|
return [];
|
|
57
65
|
}
|
|
58
|
-
return [...this.
|
|
66
|
+
return [...this._layout[y][x]].map((itemId) => this.getItem(itemId));
|
|
59
67
|
}
|
|
60
68
|
getCellOverlap(x, y, itemId) {
|
|
61
69
|
for (const item of this.getCell(x, y)) {
|
|
@@ -65,6 +73,12 @@ export class LayoutEngineGrid {
|
|
|
65
73
|
}
|
|
66
74
|
return null;
|
|
67
75
|
}
|
|
76
|
+
makeNewRow() {
|
|
77
|
+
this._layout.push([...Array(this._width)].map(() => new Set()));
|
|
78
|
+
this._height = this._layout.length;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export class LayoutEngineGrid extends ReadonlyLayoutEngineGrid {
|
|
68
82
|
move(itemId, x, y, onOverlap) {
|
|
69
83
|
const moveTarget = this.getItem(itemId);
|
|
70
84
|
this.removeLayoutItem(moveTarget);
|
|
@@ -97,14 +111,14 @@ export class LayoutEngineGrid {
|
|
|
97
111
|
throw new Error("Inserting item has invalid size.");
|
|
98
112
|
}
|
|
99
113
|
for (let y = item.y; y < item.y + item.height; y++) {
|
|
100
|
-
while (this.
|
|
114
|
+
while (this._layout.length <= y) {
|
|
101
115
|
this.makeNewRow();
|
|
102
116
|
}
|
|
103
117
|
for (let x = item.x; x < item.x + item.width; x++) {
|
|
104
|
-
for (const overlapId of this.
|
|
118
|
+
for (const overlapId of this._layout[y][x]) {
|
|
105
119
|
onOverlap(overlapId);
|
|
106
120
|
}
|
|
107
|
-
this.
|
|
121
|
+
this._layout[y][x].add(item.id);
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
}
|
|
@@ -116,25 +130,21 @@ export class LayoutEngineGrid {
|
|
|
116
130
|
removeLayoutItem(item) {
|
|
117
131
|
for (let y = item.y; y < item.y + item.height; y++) {
|
|
118
132
|
for (let x = item.x; x < item.x + item.width; x++) {
|
|
119
|
-
this.
|
|
133
|
+
this._layout[y][x].delete(item.id);
|
|
120
134
|
}
|
|
121
135
|
}
|
|
122
136
|
}
|
|
123
137
|
insertLayoutItem(item, onOverlap) {
|
|
124
138
|
for (let y = item.y; y < item.y + item.height; y++) {
|
|
125
139
|
for (let x = item.x; x < item.x + item.width; x++) {
|
|
126
|
-
while (!this.
|
|
140
|
+
while (!this._layout[y]) {
|
|
127
141
|
this.makeNewRow();
|
|
128
142
|
}
|
|
129
|
-
for (const overlapId of this.
|
|
143
|
+
for (const overlapId of this._layout[y][x]) {
|
|
130
144
|
onOverlap === null || onOverlap === void 0 ? void 0 : onOverlap(overlapId);
|
|
131
145
|
}
|
|
132
|
-
this.
|
|
146
|
+
this._layout[y][x].add(item.id);
|
|
133
147
|
}
|
|
134
148
|
}
|
|
135
149
|
}
|
|
136
|
-
makeNewRow() {
|
|
137
|
-
this.layout.push([...Array(this._width)].map(() => new Set()));
|
|
138
|
-
this._height = this.layout.length;
|
|
139
|
-
}
|
|
140
150
|
}
|
package/internal/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ItemId } from "../../internal/interfaces";
|
|
2
|
-
import { Rect } from "../../internal/interfaces";
|
|
3
|
-
import { Coordinates } from "../utils/coordinates";
|
|
4
|
-
import { Operation } from "./controller";
|
|
5
|
-
/**
|
|
6
|
-
* Produces a rect (in coordinates) to represent the draggable item.
|
|
7
|
-
*/
|
|
8
|
-
export declare function getCollisionRect(operation: Operation, draggableElement: HTMLElement, coordinates: Coordinates): {
|
|
9
|
-
left: number;
|
|
10
|
-
top: number;
|
|
11
|
-
right: number;
|
|
12
|
-
bottom: number;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Returns IDs of droppables hovered by the draggable rect.
|
|
16
|
-
*/
|
|
17
|
-
export declare function getHoveredDroppables(collisionRect: Rect, droppables: readonly [ItemId, HTMLElement][]): string[];
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { getGridPlacement, isInside } from "../utils/rects";
|
|
2
|
-
import { getNormalizedElementRect } from "../utils/screen";
|
|
3
|
-
/**
|
|
4
|
-
* Produces a rect (in coordinates) to represent the draggable item.
|
|
5
|
-
*/
|
|
6
|
-
export function getCollisionRect(operation, draggableElement, coordinates) {
|
|
7
|
-
const activeRect = getNormalizedElementRect(draggableElement);
|
|
8
|
-
let collisionRect = { left: 0, top: 0, right: 0, bottom: 0 };
|
|
9
|
-
if (operation === "reorder") {
|
|
10
|
-
collisionRect = activeRect;
|
|
11
|
-
}
|
|
12
|
-
if (operation === "resize") {
|
|
13
|
-
collisionRect = { left: activeRect.left, top: activeRect.top, right: coordinates.x, bottom: coordinates.y };
|
|
14
|
-
}
|
|
15
|
-
if (operation === "insert") {
|
|
16
|
-
collisionRect = {
|
|
17
|
-
left: coordinates.x,
|
|
18
|
-
top: coordinates.y,
|
|
19
|
-
right: coordinates.x + activeRect.width,
|
|
20
|
-
bottom: coordinates.y + activeRect.height,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
return collisionRect;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Returns IDs of droppables hovered by the draggable rect.
|
|
27
|
-
*/
|
|
28
|
-
export function getHoveredDroppables(collisionRect, droppables) {
|
|
29
|
-
const droppableRects = droppables.map(([, element]) => getNormalizedElementRect(element));
|
|
30
|
-
const bounds = getGridPlacement(collisionRect, droppableRects);
|
|
31
|
-
return droppables.filter((_, index) => isInside(droppableRects[index], bounds)).map(([droppableId]) => droppableId);
|
|
32
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
// Find items that the active item cannot be moved over with the current move.
|
|
4
|
-
export function findConflicts(move, grid) {
|
|
5
|
-
const conflicts = new Set();
|
|
6
|
-
const moveTarget = grid.getItem(move.itemId);
|
|
7
|
-
const direction = `${move.x - moveTarget.x}:${move.y - moveTarget.y}`;
|
|
8
|
-
switch (direction) {
|
|
9
|
-
case "-1:0": {
|
|
10
|
-
const left = Math.max(0, moveTarget.left - 1);
|
|
11
|
-
for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
|
|
12
|
-
const block = grid.getCellOverlap(left, y, moveTarget.id);
|
|
13
|
-
if (block && block.x < left) {
|
|
14
|
-
conflicts.add(block.id);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
case "1:0": {
|
|
20
|
-
const right = Math.min(grid.width - 1, moveTarget.right + 1);
|
|
21
|
-
for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
|
|
22
|
-
const block = grid.getCellOverlap(right, y, moveTarget.id);
|
|
23
|
-
if (block && block.x + block.width - 1 > right) {
|
|
24
|
-
conflicts.add(block.id);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
case "0:-1": {
|
|
30
|
-
const top = Math.max(0, moveTarget.top - 1);
|
|
31
|
-
for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
|
|
32
|
-
const block = grid.getCellOverlap(x, top, moveTarget.id);
|
|
33
|
-
if (block && block.y < top) {
|
|
34
|
-
conflicts.add(block.id);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
case "0:1": {
|
|
40
|
-
const bottom = moveTarget.bottom + 1;
|
|
41
|
-
for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
|
|
42
|
-
const block = grid.getCellOverlap(x, bottom, moveTarget.id);
|
|
43
|
-
if (block && block.y + block.height - 1 > bottom) {
|
|
44
|
-
conflicts.add(block.id);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
default:
|
|
50
|
-
throw new Error(`Invariant violation: unexpected direction ${direction}.`);
|
|
51
|
-
}
|
|
52
|
-
return conflicts;
|
|
53
|
-
}
|