@dxos/react-ui-mosaic 0.6.4 → 0.6.5-staging.42fccfe
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/dist/lib/browser/index.mjs +63 -22
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/mosaic/Container.d.ts +25 -11
- package/dist/types/src/mosaic/Container.d.ts.map +1 -1
- package/dist/types/src/mosaic/DragOverlay.d.ts.map +1 -1
- package/dist/types/src/mosaic/Mosaic.d.ts +4 -3
- package/dist/types/src/mosaic/Mosaic.d.ts.map +1 -1
- package/dist/types/src/mosaic/Root.d.ts +4 -0
- package/dist/types/src/mosaic/Root.d.ts.map +1 -1
- package/dist/types/src/mosaic/SortableContext.d.ts.map +1 -1
- package/dist/types/src/mosaic/Tile.d.ts.map +1 -1
- package/package.json +29 -23
- package/src/mosaic/Container.tsx +41 -11
- package/src/mosaic/DragOverlay.tsx +8 -3
- package/src/mosaic/Mosaic.ts +14 -3
- package/src/mosaic/Root.tsx +86 -21
- package/src/mosaic/SortableContext.tsx +1 -0
- package/src/mosaic/Tile.tsx +3 -1
- package/src/mosaic/hooks/useItemsWithPreview.ts +1 -1
- package/src/mosaic/util/path.ts +1 -1
package/src/mosaic/Root.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type DragStartEvent,
|
|
13
13
|
KeyboardSensor,
|
|
14
14
|
type Modifier,
|
|
15
|
+
type DropAnimation,
|
|
15
16
|
MouseSensor,
|
|
16
17
|
pointerWithin,
|
|
17
18
|
rectIntersection,
|
|
@@ -24,7 +25,12 @@ import pick from 'lodash.pick';
|
|
|
24
25
|
import React, { createContext, type FC, type PropsWithChildren, useState } from 'react';
|
|
25
26
|
import { createPortal } from 'react-dom';
|
|
26
27
|
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_TRANSITION,
|
|
30
|
+
type DefaultMoveDetails,
|
|
31
|
+
type MosaicContainerProps,
|
|
32
|
+
type MosaicOperation,
|
|
33
|
+
} from './Container';
|
|
28
34
|
import { Debug } from './Debug';
|
|
29
35
|
import { DefaultComponent } from './DefaultComponent';
|
|
30
36
|
import { type MosaicTileComponent } from './Tile';
|
|
@@ -33,12 +39,16 @@ import { Path } from './util';
|
|
|
33
39
|
|
|
34
40
|
const DEFAULT_COMPONENT_ID = '__default';
|
|
35
41
|
|
|
42
|
+
export type MosaicDropAnimation = DropAnimation | null | undefined | void;
|
|
43
|
+
|
|
36
44
|
export type MosaicContextType = {
|
|
37
45
|
containers: Record<string, MosaicContainerProps<any> | undefined>;
|
|
38
46
|
setContainer: (id: string, container?: MosaicContainerProps<any>) => void;
|
|
39
47
|
activeItem: MosaicDraggedItem | undefined;
|
|
40
48
|
overItem: MosaicDraggedItem | undefined;
|
|
41
49
|
operation: MosaicOperation;
|
|
50
|
+
moveDetails?: Record<string, unknown>;
|
|
51
|
+
dropAnimation?: MosaicDropAnimation;
|
|
42
52
|
};
|
|
43
53
|
|
|
44
54
|
export const MosaicContext = createContext<MosaicContextType | undefined>(undefined);
|
|
@@ -51,6 +61,8 @@ export type MosaicRootProps = PropsWithChildren<{
|
|
|
51
61
|
debug?: boolean;
|
|
52
62
|
}>;
|
|
53
63
|
|
|
64
|
+
const defaultDropAnimation: MosaicDropAnimation = { duration: 0 };
|
|
65
|
+
|
|
54
66
|
/**
|
|
55
67
|
* Root context provider.
|
|
56
68
|
*/
|
|
@@ -73,6 +85,8 @@ export const MosaicRoot: FC<MosaicRootProps> = ({ Component = DefaultComponent,
|
|
|
73
85
|
const [activeItem, setActiveItem] = useState<MosaicDraggedItem>();
|
|
74
86
|
const [overItem, setOverItem] = useState<MosaicDraggedItem>();
|
|
75
87
|
const [operation, setOperation] = useState<MosaicOperation>('reject');
|
|
88
|
+
const [moveDetails, setMoveDetails] = useState<(DefaultMoveDetails & Record<string, any>) | undefined>();
|
|
89
|
+
const [dropAnimation, setDropAnimation] = useState<MosaicDropAnimation>(defaultDropAnimation);
|
|
76
90
|
|
|
77
91
|
//
|
|
78
92
|
// DndKit Defaults
|
|
@@ -136,20 +150,50 @@ export const MosaicRoot: FC<MosaicRootProps> = ({ Component = DefaultComponent,
|
|
|
136
150
|
setActiveItem(pick(event.active.data.current as MosaicDraggedItem, 'path', 'type', 'item', 'position'));
|
|
137
151
|
};
|
|
138
152
|
|
|
139
|
-
const handleDragMove = (event: DragMoveEvent) => {
|
|
153
|
+
const handleDragMove = (event: DragMoveEvent) => {
|
|
154
|
+
const nextOverItem = pick(event.over?.data.current as MosaicDraggedItem, 'path', 'type', 'item', 'position');
|
|
155
|
+
const nextOverContainer =
|
|
156
|
+
nextOverItem && nextOverItem?.path ? containers[Path.first(nextOverItem.path)] : undefined;
|
|
157
|
+
if (!event.over || !nextOverItem || !nextOverContainer) {
|
|
158
|
+
setOperation('reject');
|
|
159
|
+
setOverItem(undefined);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (overItem !== nextOverItem) {
|
|
164
|
+
setOverItem(nextOverItem);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (activeItem && nextOverItem && nextOverContainer?.onMove) {
|
|
168
|
+
const { operation: nextOperation, details: nextDetails } = nextOverContainer.onMove({
|
|
169
|
+
active: activeItem,
|
|
170
|
+
over: nextOverItem,
|
|
171
|
+
details: { delta: event.delta },
|
|
172
|
+
});
|
|
173
|
+
setOperation(nextOperation);
|
|
174
|
+
if (
|
|
175
|
+
// always set falsy symbols
|
|
176
|
+
!nextDetails ||
|
|
177
|
+
// always set if details become truthy
|
|
178
|
+
(!moveDetails && nextDetails) ||
|
|
179
|
+
// only update if details change
|
|
180
|
+
Object.entries(nextDetails).find(([key, value]) => !(key in moveDetails!) || moveDetails![key] !== value)
|
|
181
|
+
) {
|
|
182
|
+
setMoveDetails(nextDetails);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
140
186
|
|
|
141
187
|
const handleDragOver = (event: DragOverEvent) => {
|
|
142
188
|
const overItem = pick(event.over?.data.current as MosaicDraggedItem, 'path', 'type', 'item', 'position');
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
setOverItem(overItem);
|
|
189
|
+
const overContainer = overItem && overItem?.path ? containers[Path.first(overItem.path)] : undefined;
|
|
190
|
+
// TODO(wittjosiah): This is over-prescriptive.
|
|
191
|
+
if (overContainer?.onMove) {
|
|
192
|
+
// The over container will have handled this in its move handler.
|
|
148
193
|
return;
|
|
149
194
|
}
|
|
150
195
|
|
|
151
196
|
const activeContainer = activeItem && containers[Path.first(activeItem.path)];
|
|
152
|
-
const overContainer = overItem?.path && containers[Path.first(overItem.path)];
|
|
153
197
|
if (!event.over || !overItem || !overContainer || !activeItem || !activeContainer) {
|
|
154
198
|
setOperation('reject');
|
|
155
199
|
setOverItem(undefined);
|
|
@@ -157,10 +201,12 @@ export const MosaicRoot: FC<MosaicRootProps> = ({ Component = DefaultComponent,
|
|
|
157
201
|
}
|
|
158
202
|
|
|
159
203
|
let operation: MosaicOperation = 'reject' as const;
|
|
160
|
-
if (
|
|
161
|
-
operation =
|
|
162
|
-
|
|
163
|
-
|
|
204
|
+
if (overContainer.onOver) {
|
|
205
|
+
operation = overContainer.onOver({
|
|
206
|
+
active: activeItem,
|
|
207
|
+
over: overItem,
|
|
208
|
+
details: moveDetails,
|
|
209
|
+
});
|
|
164
210
|
}
|
|
165
211
|
|
|
166
212
|
setOperation(operation);
|
|
@@ -176,22 +222,31 @@ export const MosaicRoot: FC<MosaicRootProps> = ({ Component = DefaultComponent,
|
|
|
176
222
|
const handleDragEnd = (event: DragEndEvent) => {
|
|
177
223
|
const overContainer = overItem && containers[Path.first(overItem.path)];
|
|
178
224
|
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
activeItem &&
|
|
182
|
-
overItem &&
|
|
183
|
-
(activeItem.path !== overItem.path || activeItem.position !== overItem.position)
|
|
184
|
-
) {
|
|
225
|
+
if (operation !== 'reject' && activeItem && overItem) {
|
|
226
|
+
let nextDropAnimation: MosaicDropAnimation = defaultDropAnimation;
|
|
185
227
|
const activeContainer = containers[Path.first(activeItem.path)];
|
|
186
228
|
if (activeContainer) {
|
|
187
|
-
activeContainer.onDrop?.({
|
|
229
|
+
nextDropAnimation = activeContainer.onDrop?.({
|
|
230
|
+
operation,
|
|
231
|
+
active: activeItem,
|
|
232
|
+
over: overItem,
|
|
233
|
+
details: moveDetails,
|
|
234
|
+
});
|
|
188
235
|
|
|
189
236
|
if (overContainer && overContainer !== activeContainer) {
|
|
190
|
-
overContainer.onDrop?.({
|
|
237
|
+
nextDropAnimation = overContainer.onDrop?.({
|
|
238
|
+
operation,
|
|
239
|
+
active: activeItem,
|
|
240
|
+
over: overItem,
|
|
241
|
+
details: moveDetails,
|
|
242
|
+
});
|
|
191
243
|
}
|
|
192
244
|
}
|
|
245
|
+
setDropAnimation(nextDropAnimation);
|
|
193
246
|
}
|
|
194
247
|
|
|
248
|
+
Object.values(containers).forEach((container) => container?.onDragEnd?.(event));
|
|
249
|
+
|
|
195
250
|
setTimeout(() => {
|
|
196
251
|
setOperation('reject');
|
|
197
252
|
setOverItem(undefined);
|
|
@@ -200,7 +255,17 @@ export const MosaicRoot: FC<MosaicRootProps> = ({ Component = DefaultComponent,
|
|
|
200
255
|
};
|
|
201
256
|
|
|
202
257
|
return (
|
|
203
|
-
<MosaicContext.Provider
|
|
258
|
+
<MosaicContext.Provider
|
|
259
|
+
value={{
|
|
260
|
+
containers,
|
|
261
|
+
setContainer: handleSetContainer,
|
|
262
|
+
activeItem,
|
|
263
|
+
overItem,
|
|
264
|
+
operation,
|
|
265
|
+
moveDetails,
|
|
266
|
+
dropAnimation,
|
|
267
|
+
}}
|
|
268
|
+
>
|
|
204
269
|
<DndContext
|
|
205
270
|
collisionDetection={collisionDetection}
|
|
206
271
|
modifiers={[modifiers]}
|
|
@@ -35,6 +35,7 @@ export const MosaicSortableContext = ({ id, items = [], direction = 'vertical',
|
|
|
35
35
|
activeItem &&
|
|
36
36
|
activeItem.item.id === item.id &&
|
|
37
37
|
overItem &&
|
|
38
|
+
overItem.path &&
|
|
38
39
|
(Path.siblings(overItem.path, path) || Path.hasChild(overItem.path, path)) &&
|
|
39
40
|
operation !== 'reject';
|
|
40
41
|
return isPreview ? activeItem.path : path;
|
package/src/mosaic/Tile.tsx
CHANGED
|
@@ -158,6 +158,8 @@ export const SortableTile = ({
|
|
|
158
158
|
activeItem &&
|
|
159
159
|
activeItem.item.id === item.id &&
|
|
160
160
|
overItem &&
|
|
161
|
+
activeItem.path &&
|
|
162
|
+
overItem.path &&
|
|
161
163
|
!Path.siblings(activeItem.path, overItem.path) &&
|
|
162
164
|
(Path.siblings(overItem.path, path) || Path.hasChild(overItem.path, path)) &&
|
|
163
165
|
operation !== 'reject';
|
|
@@ -173,7 +175,7 @@ export const SortableTile = ({
|
|
|
173
175
|
if (isDragging) {
|
|
174
176
|
if (operation === 'rearrange' || operation === 'reject') {
|
|
175
177
|
active = 'rearrange';
|
|
176
|
-
} else if (activeItem && Path.parent(activeItem.path) !== parentPath) {
|
|
178
|
+
} else if (activeItem && activeItem.path && Path.parent(activeItem.path) !== parentPath) {
|
|
177
179
|
active = 'destination';
|
|
178
180
|
} else {
|
|
179
181
|
active = 'origin';
|
|
@@ -20,7 +20,7 @@ export const useItemsWithPreview = <T extends MosaicDataItem>({ path, items }: {
|
|
|
20
20
|
// This allows items to be appended to the end of the sortable.
|
|
21
21
|
overItem?.path === path && activeItem && Path.parent(activeItem.path) !== overItem.path
|
|
22
22
|
? path
|
|
23
|
-
: overItem && Path.parent(overItem.path);
|
|
23
|
+
: overItem && !!overItem.path && Path.parent(overItem.path);
|
|
24
24
|
const [lastOverParent, setLastOverParent] = useState(overParent);
|
|
25
25
|
|
|
26
26
|
useEffect(() => {
|