@dxos/react-ui-mosaic 0.10.0 → 0.11.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/dist/lib/chunk-Mosaic.mjs +1024 -0
- package/dist/lib/chunk-Mosaic.mjs.map +1 -0
- package/dist/lib/index.mjs +653 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/playwright.mjs +200 -0
- package/dist/lib/playwright.mjs.map +1 -0
- package/dist/lib/testing.mjs +127 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +11 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/types/src/components/Board/Board.d.ts +7 -27
- package/dist/types/src/components/Board/Board.d.ts.map +1 -1
- package/dist/types/src/components/Board/Board.stories.d.ts.map +1 -1
- package/dist/types/src/components/Board/BoardColumnContext.d.ts +10 -0
- package/dist/types/src/components/Board/BoardColumnContext.d.ts.map +1 -0
- package/dist/types/src/components/Board/BoardContext.d.ts +26 -0
- package/dist/types/src/components/Board/BoardContext.d.ts.map +1 -0
- package/dist/types/src/components/Board/Column.d.ts +2 -7
- package/dist/types/src/components/Board/Column.d.ts.map +1 -1
- package/dist/types/src/components/Board/index.d.ts +2 -0
- package/dist/types/src/components/Board/index.d.ts.map +1 -1
- package/dist/types/src/components/Focus/Focus.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/Container.d.ts +6 -34
- package/dist/types/src/components/Mosaic/Container.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/Mosaic.d.ts +10 -15
- package/dist/types/src/components/Mosaic/Mosaic.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/MosaicContainerContext.d.ts +34 -0
- package/dist/types/src/components/Mosaic/MosaicContainerContext.d.ts.map +1 -0
- package/dist/types/src/components/Mosaic/MosaicTileContext.d.ts +31 -0
- package/dist/types/src/components/Mosaic/MosaicTileContext.d.ts.map +1 -0
- package/dist/types/src/components/Mosaic/Placeholder.d.ts +3 -3
- package/dist/types/src/components/Mosaic/Placeholder.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/Stack.d.ts +8 -2
- package/dist/types/src/components/Mosaic/Stack.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/Stack.stories.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/Tile.d.ts +6 -32
- package/dist/types/src/components/Mosaic/Tile.d.ts.map +1 -1
- package/dist/types/src/components/Mosaic/VirtualStackPagination.stories.d.ts +13 -0
- package/dist/types/src/components/Mosaic/VirtualStackPagination.stories.d.ts.map +1 -0
- package/dist/types/src/components/Mosaic/index.d.ts +0 -1
- package/dist/types/src/components/Mosaic/index.d.ts.map +1 -1
- package/dist/types/src/components/SearchStack/SearchStack.d.ts +1 -1
- package/dist/types/src/components/SearchStack/SearchStack.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useDefaultColumnEventHandler.d.ts +2 -2
- package/dist/types/src/hooks/useDefaultColumnEventHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useEventHandlerAdapter.d.ts +3 -3
- package/dist/types/src/hooks/useEventHandlerAdapter.d.ts.map +1 -1
- package/dist/types/src/hooks/useVirtualizerPagination.d.ts +60 -0
- package/dist/types/src/hooks/useVirtualizerPagination.d.ts.map +1 -0
- package/dist/types/src/hooks/useVirtualizerPagination.test.d.ts +2 -0
- package/dist/types/src/hooks/useVirtualizerPagination.test.d.ts.map +1 -0
- package/dist/types/src/hooks/useVisibleItems.d.ts +2 -2
- package/dist/types/src/hooks/useVisibleItems.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +35 -37
- package/src/components/Board/Board.stories.tsx +7 -6
- package/src/components/Board/Board.tsx +9 -54
- package/src/components/Board/BoardColumnContext.ts +25 -0
- package/src/components/Board/BoardContext.ts +43 -0
- package/src/components/Board/Column.tsx +13 -27
- package/src/components/Board/Item.tsx +2 -2
- package/src/components/Board/index.ts +3 -0
- package/src/components/Mosaic/Container.tsx +31 -53
- package/src/components/Mosaic/DragHandle.tsx +1 -1
- package/src/components/Mosaic/{Mosaic.tsx → Mosaic.ts} +5 -16
- package/src/components/Mosaic/MosaicContainerContext.ts +44 -0
- package/src/components/Mosaic/MosaicTileContext.ts +34 -0
- package/src/components/Mosaic/Placeholder.tsx +7 -8
- package/src/components/Mosaic/ResizeHandle.stories.tsx +4 -4
- package/src/components/Mosaic/ResizeHandle.tsx +2 -2
- package/src/components/Mosaic/Stack.stories.tsx +49 -13
- package/src/components/Mosaic/Stack.tsx +30 -9
- package/src/components/Mosaic/Tile.tsx +33 -33
- package/src/components/Mosaic/VirtualStackPagination.stories.tsx +423 -0
- package/src/components/Mosaic/index.ts +0 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useDefaultColumnEventHandler.ts +2 -2
- package/src/hooks/useEventHandlerAdapter.ts +4 -5
- package/src/hooks/useVirtualizerPagination.test.tsx +105 -0
- package/src/hooks/useVirtualizerPagination.ts +473 -0
- package/src/hooks/useVisibleItems.ts +2 -2
- package/src/playwright/smoke.spec.ts +4 -1
- package/src/testing/DefaultStackTile.tsx +1 -1
- package/src/testing/decorator.tsx +3 -3
- package/dist/lib/browser/chunk-OBVIYZLR.mjs +0 -1119
- package/dist/lib/browser/chunk-OBVIYZLR.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -701
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/playwright/index.mjs +0 -256
- package/dist/lib/browser/playwright/index.mjs.map +0 -7
- package/dist/lib/browser/testing/index.mjs +0 -136
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/browser/translations.mjs +0 -18
- package/dist/lib/browser/translations.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4IEMCMIV.mjs +0 -1121
- package/dist/lib/node-esm/chunk-4IEMCMIV.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -702
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/playwright/index.mjs +0 -258
- package/dist/lib/node-esm/playwright/index.mjs.map +0 -7
- package/dist/lib/node-esm/testing/index.mjs +0 -137
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/translations.mjs +0 -20
- package/dist/lib/node-esm/translations.mjs.map +0 -7
- package/dist/types/src/components/Mosaic/Root.d.ts +0 -35
- package/dist/types/src/components/Mosaic/Root.d.ts.map +0 -1
- package/dist/types/src/components/Mosaic/types.d.ts +0 -86
- package/dist/types/src/components/Mosaic/types.d.ts.map +0 -1
- package/src/components/Mosaic/Root.tsx +0 -288
- package/src/components/Mosaic/types.ts +0 -94
- /package/src/components/Focus/{Focus.tsx → Focus.ts} +0 -0
|
@@ -0,0 +1,1024 @@
|
|
|
1
|
+
import { createContext } from "@radix-ui/react-context";
|
|
2
|
+
import { Fragment, forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Button, Focus, IconButton, composable, composableProps, slottable, useFocus, useTranslation } from "@dxos/react-ui";
|
|
4
|
+
import { ResizeHandle, getSourceData, resizeAttributes, sizeStyle, useDndRootContext } from "@dxos/react-ui-dnd";
|
|
5
|
+
import { mx } from "@dxos/ui-theme";
|
|
6
|
+
import { createPortal } from "react-dom";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { isTruthy } from "@dxos/util";
|
|
9
|
+
import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element";
|
|
10
|
+
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
|
11
|
+
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
12
|
+
import { composeRefs, useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
13
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
14
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
15
|
+
import { bind } from "bind-event-listener";
|
|
16
|
+
import { DropIndicator } from "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box";
|
|
17
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
18
|
+
import { invariant } from "@dxos/invariant";
|
|
19
|
+
import { attachClosestEdge, extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
|
|
20
|
+
import { preserveOffsetOnSource } from "@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source";
|
|
21
|
+
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
|
|
22
|
+
//#region src/hooks/useVirtualizerPagination.ts
|
|
23
|
+
/** Rows from either loaded edge at which the next/previous page is requested. */
|
|
24
|
+
var DEFAULT_THRESHOLD = 12;
|
|
25
|
+
/** Index at/above which `getNext` fires; small windows clamp to the last row only. */
|
|
26
|
+
var nextPageIndexThreshold = (itemCount, threshold) => itemCount > threshold ? itemCount - threshold : itemCount - 1;
|
|
27
|
+
/** True when loaded content exceeds the scroll viewport (user can actually scroll). */
|
|
28
|
+
var isScrollable = (virtualizer) => {
|
|
29
|
+
const viewportHeight = virtualizer.scrollElement?.clientHeight ?? 0;
|
|
30
|
+
return viewportHeight > 0 && virtualizer.getTotalSize() > viewportHeight + 1;
|
|
31
|
+
};
|
|
32
|
+
var readEdgeGeometry = (virtualizer) => {
|
|
33
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
34
|
+
const measurements = virtualizer.measurementsCache;
|
|
35
|
+
return {
|
|
36
|
+
scrollOffset: virtualizer.scrollOffset ?? 0,
|
|
37
|
+
viewportHeight: virtualizer.scrollElement?.clientHeight ?? 0,
|
|
38
|
+
firstVisibleIndex: virtualItems.at(0)?.index,
|
|
39
|
+
lastVisibleIndex: virtualItems.at(-1)?.index,
|
|
40
|
+
windowStart: measurements[0]?.start,
|
|
41
|
+
windowEnd: measurements.at(-1)?.end
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
var isNearBottomEdge = (geometry, itemCount, threshold) => geometry.lastVisibleIndex !== void 0 ? geometry.lastVisibleIndex >= nextPageIndexThreshold(itemCount, threshold) : geometry.windowEnd != null && geometry.scrollOffset + geometry.viewportHeight > geometry.windowEnd;
|
|
45
|
+
var isNearTopEdge = (geometry, threshold) => geometry.firstVisibleIndex !== void 0 ? geometry.firstVisibleIndex < threshold : geometry.windowStart != null && geometry.scrollOffset < geometry.windowStart;
|
|
46
|
+
/** Loaded content doesn't reach the viewport's top edge (e.g. scrolled into the leading spacer). */
|
|
47
|
+
var isBlankAbove = (geometry) => geometry.windowStart != null && geometry.windowStart > geometry.scrollOffset;
|
|
48
|
+
/** Loaded content doesn't reach the viewport's bottom edge. */
|
|
49
|
+
var isBlankBelow = (geometry) => geometry.windowEnd != null && geometry.windowEnd < geometry.scrollOffset + geometry.viewportHeight;
|
|
50
|
+
/** Not fired at this exact scroll offset since the edge was last left, unless blank re-arms it. */
|
|
51
|
+
var canRequestNext = (state, geometry) => state.lastGetNextScrollRef.current === null || geometry.scrollOffset !== state.lastGetNextScrollRef.current || isBlankBelow(geometry);
|
|
52
|
+
var canRequestPrevious = (state, geometry) => state.lastGetPreviousScrollRef.current === null || geometry.scrollOffset !== state.lastGetPreviousScrollRef.current || isBlankAbove(geometry);
|
|
53
|
+
var requestNext = (state, items, scrollOffset) => {
|
|
54
|
+
state.lastNextRequestedItemsRef.current = items;
|
|
55
|
+
state.lastGetNextScrollRef.current = scrollOffset;
|
|
56
|
+
const getNext = state.paginationRef.current?.getNext;
|
|
57
|
+
queueMicrotask(() => getNext?.());
|
|
58
|
+
};
|
|
59
|
+
var requestPrevious = (state, items, scrollOffset) => {
|
|
60
|
+
state.lastPreviousRequestedItemsRef.current = items;
|
|
61
|
+
state.lastGetPreviousScrollRef.current = scrollOffset;
|
|
62
|
+
const getPrevious = state.paginationRef.current?.getPrevious;
|
|
63
|
+
queueMicrotask(() => getPrevious?.());
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Requests `getNext`/`getPrevious` once the loaded window's edge nears the viewport. Deduped per
|
|
67
|
+
* direction and per `items` snapshot; re-armed while the viewport sits in blank space so a chain
|
|
68
|
+
* of pages loads back-to-back without further user scrolling.
|
|
69
|
+
*/
|
|
70
|
+
var evaluateTriggers = ({ virtualizer, items, pagination, itemCount, threshold, state }) => {
|
|
71
|
+
const geometry = readEdgeGeometry(virtualizer);
|
|
72
|
+
const scrollable = isScrollable(virtualizer);
|
|
73
|
+
const smallWindow = itemCount <= threshold;
|
|
74
|
+
const nearBottom = isNearBottomEdge(geometry, itemCount, threshold);
|
|
75
|
+
const nearTop = isNearTopEdge(geometry, threshold);
|
|
76
|
+
if (!nearBottom) state.lastGetNextScrollRef.current = null;
|
|
77
|
+
if (!nearTop) state.lastGetPreviousScrollRef.current = null;
|
|
78
|
+
const triggerNext = !!pagination?.getNext && !pagination.isLoading && scrollable && nearBottom && (!smallWindow || geometry.scrollOffset > 0) && canRequestNext(state, geometry);
|
|
79
|
+
const triggerPrevious = !!pagination?.getPrevious && !pagination.isLoading && scrollable && nearTop && pagination.atHead !== true && canRequestPrevious(state, geometry);
|
|
80
|
+
if (!triggerNext) state.lastNextRequestedItemsRef.current = void 0;
|
|
81
|
+
if (!triggerPrevious) state.lastPreviousRequestedItemsRef.current = void 0;
|
|
82
|
+
if (triggerNext && state.lastNextRequestedItemsRef.current !== items) requestNext(state, items, geometry.scrollOffset);
|
|
83
|
+
else if (triggerPrevious && state.lastPreviousRequestedItemsRef.current !== items) requestPrevious(state, items, geometry.scrollOffset);
|
|
84
|
+
};
|
|
85
|
+
/** Total height (px) of `prevItems[0, oldIndexOfNewFirst)`, per its outgoing measurements. */
|
|
86
|
+
var evictedPrefixHeight = (virtualizer, oldIndexOfNewFirst) => {
|
|
87
|
+
const measurements = virtualizer.measurementsCache;
|
|
88
|
+
return (measurements[oldIndexOfNewFirst]?.start ?? 0) - (measurements[0]?.start ?? 0);
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* True when `items` is `prevItems` sliced from `oldIndexOfNewFirst` (a genuine slide/eviction/append).
|
|
92
|
+
* False for a reorder -- e.g. a conversation bumped to the head by a mid-sync reply -- whose old
|
|
93
|
+
* mid-list offset would otherwise be misread as evicted height and grow the spacer for good.
|
|
94
|
+
*/
|
|
95
|
+
var isContiguousSlide = (prevItems, items, oldIndexOfNewFirst, getId) => {
|
|
96
|
+
const overlap = Math.min(items.length, prevItems.length - oldIndexOfNewFirst);
|
|
97
|
+
for (let index = 0; index < overlap; index++) if (getId(items[index]) !== getId(prevItems[oldIndexOfNewFirst + index])) return false;
|
|
98
|
+
return true;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Finds an item retained across a prepend, anchored on its pre-change offset. Scans from the tail
|
|
102
|
+
* (the common case -- a prepend leaves the rest of the window untouched) and doesn't require the
|
|
103
|
+
* item to be currently rendered, since `measurementsCache` covers the whole array.
|
|
104
|
+
*/
|
|
105
|
+
var findRetainedAnchor = (prevItems, newIds, getId, virtualizer) => {
|
|
106
|
+
for (let index = prevItems.length - 1; index >= 0; index--) {
|
|
107
|
+
const id = getId(prevItems[index]);
|
|
108
|
+
if (newIds.has(id)) {
|
|
109
|
+
const start = virtualizer.measurementsCache[index]?.start;
|
|
110
|
+
return start != null ? {
|
|
111
|
+
id,
|
|
112
|
+
start
|
|
113
|
+
} : null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Classifies how `items` changed at the front, relative to `prevItems`, using only the outgoing
|
|
120
|
+
* window's own measurements (no need to wait for the incoming render). `'evicted'` covers both a
|
|
121
|
+
* prefix eviction (the window slid forward) and a pure append (the window grew) -- either way the
|
|
122
|
+
* item now at the front was already loaded, so its height is already known, *and* the retained
|
|
123
|
+
* items keep their old relative order (`isContiguousSlide`). `'prepended'` covers everything else:
|
|
124
|
+
* a `getPrevious` prepend, a reset to a disjoint range, or an existing item reordered to the head
|
|
125
|
+
* (e.g. conversation grouping bumping a thread on a new reply) -- despite the new head having been
|
|
126
|
+
* loaded before, nothing was actually evicted.
|
|
127
|
+
*/
|
|
128
|
+
var classifyFrontEdgeChange = (prevItems, items, getId, virtualizer) => {
|
|
129
|
+
if (!virtualizer || !prevItems) return { kind: "none" };
|
|
130
|
+
const firstNewId = items?.[0] != null ? getId(items[0]) : void 0;
|
|
131
|
+
const oldIndexOfNewFirst = firstNewId != null ? prevItems.findIndex((item) => getId(item) === firstNewId) : -1;
|
|
132
|
+
if (oldIndexOfNewFirst >= 0 && items && isContiguousSlide(prevItems, items, oldIndexOfNewFirst, getId)) return {
|
|
133
|
+
kind: "evicted",
|
|
134
|
+
evictedHeight: evictedPrefixHeight(virtualizer, oldIndexOfNewFirst)
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
kind: "prepended",
|
|
138
|
+
anchor: findRetainedAnchor(prevItems, new Set(items?.map((item) => getId(item))), getId, virtualizer)
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* `'no-overlap'`: the anchor isn't in the new `items` (e.g. a reset) -- the spacer no longer
|
|
143
|
+
* describes anything. `'pending'`: the incoming render hasn't measured the anchor yet. `'absorbed'`:
|
|
144
|
+
* the shift is fully absorbed by the spacer, no scroll rewrite needed. `'snap'`: growing past an
|
|
145
|
+
* empty spacer (or the `atHead` reset) leaves a remainder that has to move the scroll offset.
|
|
146
|
+
*/
|
|
147
|
+
var computePrependCorrection = (virtualizer, items, getId, anchor, currentSpace, atHead) => {
|
|
148
|
+
const newIndex = items.findIndex((item) => getId(item) === anchor.id);
|
|
149
|
+
if (newIndex < 0) return { kind: "no-overlap" };
|
|
150
|
+
const newStart = virtualizer.measurementsCache[newIndex]?.start;
|
|
151
|
+
if (newStart == null) return { kind: "pending" };
|
|
152
|
+
const shift = newStart - anchor.start;
|
|
153
|
+
const nextSpace = atHead ? 0 : Math.max(0, currentSpace - shift);
|
|
154
|
+
const scrollAdjust = shift + (nextSpace - currentSpace);
|
|
155
|
+
return scrollAdjust === 0 ? {
|
|
156
|
+
kind: "absorbed",
|
|
157
|
+
nextSpace
|
|
158
|
+
} : {
|
|
159
|
+
kind: "snap",
|
|
160
|
+
nextSpace,
|
|
161
|
+
scrollAdjust
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Wires a paginated data source into a `Mosaic.VirtualStack`: requests the next/previous page near
|
|
166
|
+
* either loaded edge (`evaluateTriggers`), and keeps the scrollbar stable across the resulting
|
|
167
|
+
* `items` change by maintaining a leading spacer instead of rewriting the scroll offset.
|
|
168
|
+
*
|
|
169
|
+
* Called internally by `Mosaic.VirtualStack` when its `pagination` prop is set -- not normally
|
|
170
|
+
* called directly.
|
|
171
|
+
*
|
|
172
|
+
* `@tanstack/react-virtual` recomputes every item's pixel offset from the current array on each
|
|
173
|
+
* render, with no compensation for insertion/removal at the front -- rewriting the scroll offset
|
|
174
|
+
* to chase that shift visibly jumps the scrollbar thumb. Instead, `leadingSpace` (fed back into
|
|
175
|
+
* `paddingStart`) grows by exactly the evicted height and shrinks by exactly the prepended height,
|
|
176
|
+
* so retained items keep their absolute position and the thumb only scales as the total grows.
|
|
177
|
+
*
|
|
178
|
+
* Eviction (`classifyFrontEdgeChange`'s `'evicted'`) is corrected synchronously during the same
|
|
179
|
+
* render that observes the new `items`, since the evicted height is already known -- deferring it
|
|
180
|
+
* would let the browser observe (and clamp `scrollTop` against) a momentary, too-short frame.
|
|
181
|
+
* Prepending (`'prepended'`) is corrected one render later, from a layout effect
|
|
182
|
+
* (`computePrependCorrection`), since the prepended items' heights aren't known until the incoming
|
|
183
|
+
* render measures them; that lag is safe because prepending only ever grows content before the
|
|
184
|
+
* spacer shrinks to match.
|
|
185
|
+
*
|
|
186
|
+
* The layout effect also re-runs `evaluateTriggers` on every `items` change, evicted/appended or
|
|
187
|
+
* prepended alike -- `@tanstack/react-virtual` only calls `onChange` when the visible range (or
|
|
188
|
+
* `isScrolling`) changes, not merely when `items` does. Once the loaded window's tail is already
|
|
189
|
+
* fully rendered and the scrollbar sits at its physical max, further scrolling can't move that
|
|
190
|
+
* range at all, so `onChange` stops firing -- relying on it alone would permanently stall a
|
|
191
|
+
* `getNext` chain right after its own page lands, before the user scrolls again.
|
|
192
|
+
*
|
|
193
|
+
* Assumes the stack renders with `draggable={false}` (virtual indices map 1:1 onto `items`).
|
|
194
|
+
*/
|
|
195
|
+
var useVirtualizerPagination = ({ items, getId, pagination, threshold = DEFAULT_THRESHOLD }) => {
|
|
196
|
+
const itemCount = items?.length ?? 0;
|
|
197
|
+
const [leadingSpace, setLeadingSpace] = useState(0);
|
|
198
|
+
const leadingSpaceRef = useRef(leadingSpace);
|
|
199
|
+
const setSpacer = useCallback((nextSpace) => {
|
|
200
|
+
if (nextSpace !== leadingSpaceRef.current) {
|
|
201
|
+
leadingSpaceRef.current = nextSpace;
|
|
202
|
+
setLeadingSpace(nextSpace);
|
|
203
|
+
}
|
|
204
|
+
}, []);
|
|
205
|
+
const virtualizerRef = useRef(null);
|
|
206
|
+
const anchorRef = useRef(null);
|
|
207
|
+
const prevItemsRef = useRef(items);
|
|
208
|
+
const restoringRef = useRef(false);
|
|
209
|
+
const triggerState = {
|
|
210
|
+
lastNextRequestedItemsRef: useRef(void 0),
|
|
211
|
+
lastPreviousRequestedItemsRef: useRef(void 0),
|
|
212
|
+
lastGetNextScrollRef: useRef(null),
|
|
213
|
+
lastGetPreviousScrollRef: useRef(null),
|
|
214
|
+
paginationRef: useRef(pagination)
|
|
215
|
+
};
|
|
216
|
+
triggerState.paginationRef.current = pagination;
|
|
217
|
+
const rearmTriggers = useCallback((virtualizer) => {
|
|
218
|
+
evaluateTriggers({
|
|
219
|
+
virtualizer,
|
|
220
|
+
items,
|
|
221
|
+
pagination: triggerState.paginationRef.current,
|
|
222
|
+
itemCount,
|
|
223
|
+
threshold,
|
|
224
|
+
state: triggerState
|
|
225
|
+
});
|
|
226
|
+
}, [
|
|
227
|
+
items,
|
|
228
|
+
itemCount,
|
|
229
|
+
threshold
|
|
230
|
+
]);
|
|
231
|
+
if (prevItemsRef.current !== items) {
|
|
232
|
+
if (pagination) {
|
|
233
|
+
const change = classifyFrontEdgeChange(prevItemsRef.current, items, getId, virtualizerRef.current);
|
|
234
|
+
if (change.kind === "evicted") {
|
|
235
|
+
setSpacer(leadingSpaceRef.current + change.evictedHeight);
|
|
236
|
+
anchorRef.current = null;
|
|
237
|
+
} else if (change.kind === "prepended") {
|
|
238
|
+
anchorRef.current = change.anchor;
|
|
239
|
+
if (!change.anchor) setSpacer(0);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
prevItemsRef.current = items;
|
|
243
|
+
}
|
|
244
|
+
const onChange = useCallback((virtualizer) => {
|
|
245
|
+
virtualizerRef.current = virtualizer;
|
|
246
|
+
if (restoringRef.current) {
|
|
247
|
+
restoringRef.current = false;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
rearmTriggers(virtualizer);
|
|
251
|
+
}, [rearmTriggers]);
|
|
252
|
+
useLayoutEffect(() => {
|
|
253
|
+
const virtualizer = virtualizerRef.current;
|
|
254
|
+
const anchor = anchorRef.current;
|
|
255
|
+
anchorRef.current = null;
|
|
256
|
+
if (!virtualizer || !items) return;
|
|
257
|
+
if (!anchor) {
|
|
258
|
+
rearmTriggers(virtualizer);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const atHead = triggerState.paginationRef.current?.atHead === true;
|
|
262
|
+
const correction = computePrependCorrection(virtualizer, items, getId, anchor, leadingSpaceRef.current, atHead);
|
|
263
|
+
switch (correction.kind) {
|
|
264
|
+
case "pending": return;
|
|
265
|
+
case "no-overlap":
|
|
266
|
+
setSpacer(0);
|
|
267
|
+
rearmTriggers(virtualizer);
|
|
268
|
+
return;
|
|
269
|
+
case "absorbed":
|
|
270
|
+
setSpacer(correction.nextSpace);
|
|
271
|
+
rearmTriggers(virtualizer);
|
|
272
|
+
return;
|
|
273
|
+
case "snap":
|
|
274
|
+
setSpacer(correction.nextSpace);
|
|
275
|
+
restoringRef.current = true;
|
|
276
|
+
virtualizer.scrollToOffset((virtualizer.scrollOffset ?? 0) + correction.scrollAdjust);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}, [
|
|
280
|
+
items,
|
|
281
|
+
getId,
|
|
282
|
+
setSpacer,
|
|
283
|
+
rearmTriggers
|
|
284
|
+
]);
|
|
285
|
+
return {
|
|
286
|
+
onChange,
|
|
287
|
+
leadingSpace
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/hooks/useVisibleItems.ts
|
|
292
|
+
/**
|
|
293
|
+
* Returns items with the dragging item removed.
|
|
294
|
+
*/
|
|
295
|
+
var useVisibleItems = ({ id, items, dragging, getId }) => {
|
|
296
|
+
return useMemo(() => {
|
|
297
|
+
if (!items || !dragging || id !== dragging.containerId) return items ?? [];
|
|
298
|
+
const idx = items.findIndex((item) => getId(item) === dragging.id);
|
|
299
|
+
if (idx === -1) return items;
|
|
300
|
+
const visibleItems = items.slice();
|
|
301
|
+
visibleItems.splice(idx, 1);
|
|
302
|
+
return visibleItems;
|
|
303
|
+
}, [
|
|
304
|
+
id,
|
|
305
|
+
items,
|
|
306
|
+
dragging,
|
|
307
|
+
getId
|
|
308
|
+
]);
|
|
309
|
+
};
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/components/Mosaic/styles.ts
|
|
312
|
+
var styles = { placeholder: {
|
|
313
|
+
root: [
|
|
314
|
+
"group opacity-0",
|
|
315
|
+
"group-data-[mosaic-debug=\"true\"]:opacity-100",
|
|
316
|
+
"group-data-[mosaic-debug=\"true\"]:bg-orange-500",
|
|
317
|
+
"data-[mosaic-placeholder-state=active]:opacity-100",
|
|
318
|
+
"data-[mosaic-placeholder-orientation=vertical]:snap-start",
|
|
319
|
+
"data-[mosaic-placeholder-orientation=vertical]:py-1",
|
|
320
|
+
"data-[mosaic-placeholder-orientation=vertical]:data-[mosaic-placeholder-state=active]:py-2.5",
|
|
321
|
+
"data-[mosaic-placeholder-orientation=horizontal]:px-1",
|
|
322
|
+
"data-[mosaic-placeholder-orientation=horizontal]:data-[mosaic-placeholder-state=active]:px-2.5"
|
|
323
|
+
],
|
|
324
|
+
content: [
|
|
325
|
+
"transition-all duration-0",
|
|
326
|
+
"group-data-[mosaic-placeholder-orientation=vertical]:h-0",
|
|
327
|
+
"group-data-[mosaic-placeholder-orientation=vertical]:group-data-[mosaic-placeholder-state=active]:h-[var(--mosaic-placeholder-height)]",
|
|
328
|
+
"group-data-[mosaic-placeholder-orientation=horizontal]:w-0",
|
|
329
|
+
"group-data-[mosaic-placeholder-orientation=horizontal]:group-data-[mosaic-placeholder-state=active]:w-[var(--mosaic-placeholder-width)]"
|
|
330
|
+
]
|
|
331
|
+
} };
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region src/components/Mosaic/MosaicContainerContext.ts
|
|
334
|
+
var MOSAIC_CONTAINER_NAME = "Mosaic.Container";
|
|
335
|
+
var [MosaicContainerContextProvider, useMosaicContainerContext] = createContext("MosaicContainer");
|
|
336
|
+
//#endregion
|
|
337
|
+
//#region src/components/Mosaic/Container.tsx
|
|
338
|
+
var MOSAIC_CONTAINER_STATE_ATTR = "mosaic-container-state";
|
|
339
|
+
var MOSAIC_CONTAINER_DEBUG_ATTR = "mosaic-debug";
|
|
340
|
+
var MOSAIC_CONTAINER_PLACEHOLDER_WIDTH = "--mosaic-placeholder-width";
|
|
341
|
+
var MOSAIC_CONTAINER_PLACEHOLDER_HEIGHT = "--mosaic-placeholder-height";
|
|
342
|
+
var counter = 0;
|
|
343
|
+
/**
|
|
344
|
+
* Container for a Mosaic layout.
|
|
345
|
+
*/
|
|
346
|
+
var MosaicContainer = composable(({ children, eventHandler: eventHandlerProp, orientation = "vertical", asChild, autoScroll: autoscrollElement, withFocus, controllerRef, currentId, onCurrentChange, selectedIds, onSelectionChange, debug, placeholderDebug, ...props }, forwardedRef) => {
|
|
347
|
+
const Comp = asChild ? Slot : Primitive.div;
|
|
348
|
+
const rootRef = useRef(null);
|
|
349
|
+
const composedRef = useComposedRefs(rootRef, forwardedRef);
|
|
350
|
+
const eventHandler = useMemo(() => eventHandlerProp ?? { id: `mosaic-container-${counter++}` }, [eventHandlerProp]);
|
|
351
|
+
const { dragging } = useDndRootContext(MOSAIC_CONTAINER_NAME);
|
|
352
|
+
const [state, setState] = useState({ type: "idle" });
|
|
353
|
+
const [activeLocation, setActiveLocation] = useState();
|
|
354
|
+
const [scrolling, setScrolling] = useState(false);
|
|
355
|
+
const setCurrentId = useCallback((id) => onCurrentChange?.(id), [onCurrentChange]);
|
|
356
|
+
const setSelected = useCallback((id, selected) => onSelectionChange?.(id, selected), [onSelectionChange]);
|
|
357
|
+
const scrollToRef = useRef(void 0);
|
|
358
|
+
const registerScrollTo = useCallback((fn) => {
|
|
359
|
+
scrollToRef.current = fn;
|
|
360
|
+
}, []);
|
|
361
|
+
useImperativeHandle(controllerRef, () => ({ scrollToItem: (id) => scrollToRef.current?.(id) }), []);
|
|
362
|
+
useEffect(() => {
|
|
363
|
+
if (currentId) scrollToRef.current?.(currentId);
|
|
364
|
+
}, [currentId]);
|
|
365
|
+
const { setFocus } = useFocus();
|
|
366
|
+
useEffect(() => {
|
|
367
|
+
if (withFocus) setFocus?.(state.type === "active" ? "active" : void 0);
|
|
368
|
+
}, [
|
|
369
|
+
setFocus,
|
|
370
|
+
withFocus,
|
|
371
|
+
state
|
|
372
|
+
]);
|
|
373
|
+
const { addContainer, removeContainer } = useDndRootContext(eventHandler.id);
|
|
374
|
+
useEffect(() => {
|
|
375
|
+
addContainer(eventHandler);
|
|
376
|
+
return () => removeContainer(eventHandler.id);
|
|
377
|
+
}, [eventHandler]);
|
|
378
|
+
const data = useMemo(() => ({
|
|
379
|
+
type: "container",
|
|
380
|
+
id: eventHandler.id
|
|
381
|
+
}), [eventHandler.id]);
|
|
382
|
+
useEffect(() => {
|
|
383
|
+
if (!rootRef.current) return;
|
|
384
|
+
let timeout;
|
|
385
|
+
const handleScroll = () => {
|
|
386
|
+
setScrolling(true);
|
|
387
|
+
clearTimeout(timeout);
|
|
388
|
+
timeout = setTimeout(() => {
|
|
389
|
+
setScrolling(false);
|
|
390
|
+
}, 500);
|
|
391
|
+
};
|
|
392
|
+
return combine(...[
|
|
393
|
+
bind(rootRef.current, {
|
|
394
|
+
type: "dnd:cancel",
|
|
395
|
+
listener: () => {
|
|
396
|
+
setState({ type: "idle" });
|
|
397
|
+
}
|
|
398
|
+
}),
|
|
399
|
+
dropTargetForElements({
|
|
400
|
+
element: rootRef.current,
|
|
401
|
+
getData: () => data,
|
|
402
|
+
/**
|
|
403
|
+
* Test if permitted to drop here.
|
|
404
|
+
* NOTE: Contained Tile and Placeholder elements do the same.
|
|
405
|
+
*/
|
|
406
|
+
canDrop: ({ source }) => {
|
|
407
|
+
const data = getSourceData(source);
|
|
408
|
+
return data && eventHandler.canDrop?.({ source: data }) || false;
|
|
409
|
+
},
|
|
410
|
+
getDropEffect: () => "move",
|
|
411
|
+
/**
|
|
412
|
+
* Dragging started in this container.
|
|
413
|
+
*/
|
|
414
|
+
onDragStart: ({ source }) => {
|
|
415
|
+
const sourceData = source.data;
|
|
416
|
+
setState({
|
|
417
|
+
type: "active",
|
|
418
|
+
bounds: sourceData.bounds
|
|
419
|
+
});
|
|
420
|
+
},
|
|
421
|
+
/**
|
|
422
|
+
* Dragging entered this container.
|
|
423
|
+
*/
|
|
424
|
+
onDragEnter: ({ source }) => {
|
|
425
|
+
const sourceData = source.data;
|
|
426
|
+
setState({
|
|
427
|
+
type: "active",
|
|
428
|
+
bounds: sourceData.bounds
|
|
429
|
+
});
|
|
430
|
+
},
|
|
431
|
+
/**
|
|
432
|
+
* Dragging left this container.
|
|
433
|
+
* NOTE: If the container isn't full-height, then when the dragged item is temporarily removed the container will shrink,
|
|
434
|
+
* triggering `onDragLeave`, which then causes the item to be added and removed continually (flickering).
|
|
435
|
+
*/
|
|
436
|
+
onDragLeave: ({ source }) => {
|
|
437
|
+
if (source.data.containerId !== eventHandler.id) setState({ type: "idle" });
|
|
438
|
+
},
|
|
439
|
+
/**
|
|
440
|
+
* Dropped in this container.
|
|
441
|
+
*/
|
|
442
|
+
onDrop: () => {
|
|
443
|
+
setState({ type: "idle" });
|
|
444
|
+
}
|
|
445
|
+
}),
|
|
446
|
+
autoscrollElement && [
|
|
447
|
+
autoScrollForElements({
|
|
448
|
+
element: autoscrollElement,
|
|
449
|
+
canScroll: ({ source }) => getSourceData(source) != null,
|
|
450
|
+
getAllowedAxis: () => orientation,
|
|
451
|
+
getConfiguration: () => ({ maxScrollSpeed: "fast" })
|
|
452
|
+
}),
|
|
453
|
+
bind(autoscrollElement, {
|
|
454
|
+
type: "scroll",
|
|
455
|
+
listener: handleScroll
|
|
456
|
+
}),
|
|
457
|
+
() => setScrolling(false)
|
|
458
|
+
]
|
|
459
|
+
].filter(isTruthy).flatMap((x) => x));
|
|
460
|
+
}, [
|
|
461
|
+
rootRef,
|
|
462
|
+
eventHandler,
|
|
463
|
+
data,
|
|
464
|
+
autoscrollElement
|
|
465
|
+
]);
|
|
466
|
+
return /* @__PURE__ */ jsxs(MosaicContainerContextProvider, {
|
|
467
|
+
id: eventHandler.id,
|
|
468
|
+
eventHandler,
|
|
469
|
+
orientation,
|
|
470
|
+
state,
|
|
471
|
+
dragging: state.type === "active" ? dragging : void 0,
|
|
472
|
+
scrolling,
|
|
473
|
+
activeLocation,
|
|
474
|
+
setActiveLocation,
|
|
475
|
+
currentId,
|
|
476
|
+
setCurrentId,
|
|
477
|
+
selectedIds,
|
|
478
|
+
setSelected,
|
|
479
|
+
registerScrollTo,
|
|
480
|
+
children: [/* @__PURE__ */ jsx(Comp, {
|
|
481
|
+
...composableProps(props, {
|
|
482
|
+
classNames: "h-full group",
|
|
483
|
+
style: {
|
|
484
|
+
[MOSAIC_CONTAINER_PLACEHOLDER_WIDTH]: state.type === "active" && state.bounds ? `${state.bounds.width}px` : "0px",
|
|
485
|
+
[MOSAIC_CONTAINER_PLACEHOLDER_HEIGHT]: state.type === "active" && state.bounds ? `${state.bounds.height}px` : "0px"
|
|
486
|
+
}
|
|
487
|
+
}),
|
|
488
|
+
[`data-${MOSAIC_CONTAINER_STATE_ATTR}`]: state.type,
|
|
489
|
+
[`data-${MOSAIC_CONTAINER_DEBUG_ATTR}`]: placeholderDebug,
|
|
490
|
+
ref: composedRef,
|
|
491
|
+
children
|
|
492
|
+
}), debug?.()]
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
MosaicContainer.displayName = MOSAIC_CONTAINER_NAME;
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/components/Mosaic/MosaicTileContext.ts
|
|
498
|
+
var MOSAIC_TILE_NAME = "Mosaic.Tile";
|
|
499
|
+
var [MosaicTileContextProvider, useMosaicTileContext] = createContext("MosaicTile");
|
|
500
|
+
//#endregion
|
|
501
|
+
//#region src/components/Mosaic/DragHandle.tsx
|
|
502
|
+
var MOSAIC_DRAG_HANDLE_NAME = "Mosaic.DragHandle";
|
|
503
|
+
var REACT_UI_TRANSLATION_KEY = "@dxos/react-ui";
|
|
504
|
+
/**
|
|
505
|
+
* Affordance that initiates dragging the enclosing {@link MosaicTile}. Renders a button and
|
|
506
|
+
* registers itself with the tile (via context), so consumers only need to drop it inside a
|
|
507
|
+
* `Mosaic.Tile` — no `dragHandle` ref plumbing required.
|
|
508
|
+
*/
|
|
509
|
+
var MosaicDragHandle = forwardRef(({ icon = "ph--dots-six-vertical--regular", label, variant = "ghost", classNames, testId, children }, forwardedRef) => {
|
|
510
|
+
const { t } = useTranslation(REACT_UI_TRANSLATION_KEY);
|
|
511
|
+
const { setDragHandle } = useMosaicTileContext(MOSAIC_DRAG_HANDLE_NAME);
|
|
512
|
+
const ref = composeRefs(forwardedRef, setDragHandle);
|
|
513
|
+
const testIdProps = testId ? { "data-testid": testId } : {};
|
|
514
|
+
if (children) return /* @__PURE__ */ jsxs(Button, {
|
|
515
|
+
ref,
|
|
516
|
+
variant,
|
|
517
|
+
tabIndex: -1,
|
|
518
|
+
classNames: ["grid place-items-center cursor-grab", classNames],
|
|
519
|
+
...testIdProps,
|
|
520
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
521
|
+
className: "sr-only",
|
|
522
|
+
children: label ?? t("toolbar-drag-handle.label")
|
|
523
|
+
})]
|
|
524
|
+
});
|
|
525
|
+
return /* @__PURE__ */ jsx(IconButton, {
|
|
526
|
+
ref,
|
|
527
|
+
iconOnly: true,
|
|
528
|
+
noTooltip: true,
|
|
529
|
+
tabIndex: -1,
|
|
530
|
+
variant,
|
|
531
|
+
icon,
|
|
532
|
+
label: label ?? t("toolbar-drag-handle.label"),
|
|
533
|
+
classNames: ["cursor-grab", classNames],
|
|
534
|
+
...testIdProps
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
MosaicDragHandle.displayName = MOSAIC_DRAG_HANDLE_NAME;
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/components/Mosaic/Placeholder.tsx
|
|
540
|
+
var MOSAIC_PLACEHOLDER_NAME = "Mosaic.Placeholder";
|
|
541
|
+
var MOSAIC_PLACEHOLDER_ORIENTATION_ATTR = "mosaic-placeholder-orientation";
|
|
542
|
+
var MOSAIC_PLACEHOLDER_STATE_ATTR = "mosaic-placeholder-state";
|
|
543
|
+
var MOSAIC_PLACEHOLDER_LOCATION_ATTR = "mosaic-placeholder-location";
|
|
544
|
+
var MosaicPlaceholder = ({ classNames, children, asChild, orientation = "vertical", location }) => {
|
|
545
|
+
const rootRef = useRef(null);
|
|
546
|
+
const Comp = asChild ? Slot : Primitive.div;
|
|
547
|
+
const { id: containerId, eventHandler, scrolling, activeLocation, setActiveLocation } = useMosaicContainerContext(MOSAIC_PLACEHOLDER_NAME);
|
|
548
|
+
const data = useMemo(() => ({
|
|
549
|
+
type: "placeholder",
|
|
550
|
+
containerId,
|
|
551
|
+
location
|
|
552
|
+
}), [containerId, location]);
|
|
553
|
+
useLayoutEffect(() => {
|
|
554
|
+
const root = rootRef.current;
|
|
555
|
+
if (!root || scrolling) return;
|
|
556
|
+
return dropTargetForElements({
|
|
557
|
+
element: root,
|
|
558
|
+
getData: () => data,
|
|
559
|
+
canDrop: ({ source }) => {
|
|
560
|
+
const data = getSourceData(source);
|
|
561
|
+
return data && eventHandler.canDrop?.({ source: data }) || false;
|
|
562
|
+
},
|
|
563
|
+
getDropEffect: () => "move",
|
|
564
|
+
onDragEnter: () => {
|
|
565
|
+
setActiveLocation(data.location);
|
|
566
|
+
},
|
|
567
|
+
onDragLeave: () => {
|
|
568
|
+
setActiveLocation(void 0);
|
|
569
|
+
},
|
|
570
|
+
onDrop: () => {
|
|
571
|
+
setActiveLocation(void 0);
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
}, [
|
|
575
|
+
rootRef,
|
|
576
|
+
data,
|
|
577
|
+
scrolling,
|
|
578
|
+
setActiveLocation
|
|
579
|
+
]);
|
|
580
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
581
|
+
[`data-${MOSAIC_PLACEHOLDER_ORIENTATION_ATTR}`]: orientation,
|
|
582
|
+
[`data-${MOSAIC_PLACEHOLDER_STATE_ATTR}`]: data.location === activeLocation ? "active" : "idle",
|
|
583
|
+
[`data-${MOSAIC_PLACEHOLDER_LOCATION_ATTR}`]: String(location),
|
|
584
|
+
className: mx("relative", classNames),
|
|
585
|
+
ref: rootRef,
|
|
586
|
+
children
|
|
587
|
+
});
|
|
588
|
+
};
|
|
589
|
+
MosaicPlaceholder.displayName = MOSAIC_PLACEHOLDER_NAME;
|
|
590
|
+
var DROP_INDICATOR_NAME = "Mosaic.DropIndicator";
|
|
591
|
+
var MosaicDropIndicator = (props) => {
|
|
592
|
+
const { state } = useMosaicTileContext(DROP_INDICATOR_NAME);
|
|
593
|
+
return state.type === "target" && state.closestEdge ? /* @__PURE__ */ jsx(DropIndicator, {
|
|
594
|
+
...props,
|
|
595
|
+
edge: state.closestEdge
|
|
596
|
+
}) : null;
|
|
597
|
+
};
|
|
598
|
+
MosaicDropIndicator.displayName = DROP_INDICATOR_NAME;
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/components/Mosaic/ResizeHandle.tsx
|
|
601
|
+
var MOSAIC_RESIZE_HANDLE_NAME = "Mosaic.ResizeHandle";
|
|
602
|
+
var MIN_WIDTH = 20;
|
|
603
|
+
var MIN_HEIGHT = 3;
|
|
604
|
+
var DEFAULT_EXTRINSIC_SIZE = 50;
|
|
605
|
+
/**
|
|
606
|
+
* Edge affordance that resizes the enclosing {@link MosaicTile}. The axis tracks the container
|
|
607
|
+
* orientation (width when horizontal, height when vertical); the tile must be given a `size` so the
|
|
608
|
+
* dragged extent is reflected and persisted via its `onSizeChange`. Resize bounds default to the
|
|
609
|
+
* tile's `minSize`/`maxSize` and can be overridden per handle.
|
|
610
|
+
*/
|
|
611
|
+
var MosaicResizeHandle = ({ minSize, maxSize, fallbackSize = DEFAULT_EXTRINSIC_SIZE }) => {
|
|
612
|
+
const { orientation = "vertical" } = useMosaicContainerContext(MOSAIC_RESIZE_HANDLE_NAME);
|
|
613
|
+
const { size, setSize, minSize: tileMinSize, maxSize: tileMaxSize } = useMosaicTileContext(MOSAIC_RESIZE_HANDLE_NAME);
|
|
614
|
+
const horizontal = orientation === "horizontal";
|
|
615
|
+
return /* @__PURE__ */ jsx(ResizeHandle, {
|
|
616
|
+
side: horizontal ? "inline-end" : "block-end",
|
|
617
|
+
fallbackSize,
|
|
618
|
+
minSize: minSize ?? tileMinSize ?? (horizontal ? MIN_WIDTH : MIN_HEIGHT),
|
|
619
|
+
maxSize: maxSize ?? tileMaxSize,
|
|
620
|
+
size,
|
|
621
|
+
onSizeChange: setSize
|
|
622
|
+
});
|
|
623
|
+
};
|
|
624
|
+
MosaicResizeHandle.displayName = MOSAIC_RESIZE_HANDLE_NAME;
|
|
625
|
+
//#endregion
|
|
626
|
+
//#region src/components/Mosaic/Stack.tsx
|
|
627
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-mosaic/src/components/Mosaic/Stack.tsx";
|
|
628
|
+
var MOSAIC_STACK_NAME = "MosaicStack";
|
|
629
|
+
/**
|
|
630
|
+
* Linear layout of Mosaic tiles.
|
|
631
|
+
* NOTE: This is a low-level component and should be wrapped by a scrollable container.
|
|
632
|
+
*/
|
|
633
|
+
var MosaicStackInner = composable(({ orientation: orientationProp = "vertical", getId, items, scrollIntoView = true, Tile, draggable = true, debug, ...props }, forwardedRef) => {
|
|
634
|
+
const { id, orientation = orientationProp, dragging, currentId, selectedIds, registerScrollTo, setActiveLocation } = useMosaicContainerContext(MOSAIC_STACK_NAME);
|
|
635
|
+
const visibleItems = useVisibleItems({
|
|
636
|
+
id,
|
|
637
|
+
items,
|
|
638
|
+
dragging: dragging?.source.data,
|
|
639
|
+
getId
|
|
640
|
+
});
|
|
641
|
+
invariant(orientation === "vertical" || orientation === "horizontal", `Invalid orientation: ${orientation}`, {
|
|
642
|
+
"~LogMeta": "~LogMeta",
|
|
643
|
+
F: __dxlog_file,
|
|
644
|
+
L: 95,
|
|
645
|
+
S: void 0,
|
|
646
|
+
A: ["orientation === 'vertical' || orientation === 'horizontal'", "`Invalid orientation: ${orientation}`"]
|
|
647
|
+
});
|
|
648
|
+
const draggingSource = dragging?.source.data;
|
|
649
|
+
const sourceIndex = useMemo(() => {
|
|
650
|
+
if (!items || !draggingSource || draggingSource.containerId !== id) return -1;
|
|
651
|
+
return items.findIndex((item) => getId(item) === draggingSource.id);
|
|
652
|
+
}, [
|
|
653
|
+
items,
|
|
654
|
+
draggingSource,
|
|
655
|
+
id,
|
|
656
|
+
getId
|
|
657
|
+
]);
|
|
658
|
+
useLayoutEffect(() => {
|
|
659
|
+
if (sourceIndex >= 0) setActiveLocation(sourceIndex + .5);
|
|
660
|
+
}, [sourceIndex, setActiveLocation]);
|
|
661
|
+
const rootRef = useRef(null);
|
|
662
|
+
const scrollToId = useCallback((targetId) => {
|
|
663
|
+
if (!scrollIntoView) return;
|
|
664
|
+
(rootRef.current?.querySelector(`[data-object-id="${CSS.escape(targetId)}"]`))?.scrollIntoView({
|
|
665
|
+
block: "nearest",
|
|
666
|
+
behavior: "smooth"
|
|
667
|
+
});
|
|
668
|
+
}, [scrollIntoView]);
|
|
669
|
+
useEffect(() => {
|
|
670
|
+
registerScrollTo(scrollToId);
|
|
671
|
+
return () => registerScrollTo(void 0);
|
|
672
|
+
}, [registerScrollTo, scrollToId]);
|
|
673
|
+
const composedRef = useComposedRefs(rootRef, forwardedRef);
|
|
674
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
675
|
+
...composableProps(props, {
|
|
676
|
+
role: "list",
|
|
677
|
+
classNames: [
|
|
678
|
+
"flex",
|
|
679
|
+
orientation === "horizontal" && "h-full [&>*]:shrink-0",
|
|
680
|
+
orientation === "vertical" && "flex-col"
|
|
681
|
+
]
|
|
682
|
+
}),
|
|
683
|
+
ref: composedRef,
|
|
684
|
+
children: [draggable && /* @__PURE__ */ jsx(InternalPlaceholder, {
|
|
685
|
+
orientation,
|
|
686
|
+
location: .5
|
|
687
|
+
}), visibleItems?.map((item, index) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tile, {
|
|
688
|
+
id: getId(item),
|
|
689
|
+
data: item,
|
|
690
|
+
location: index + 1,
|
|
691
|
+
draggable,
|
|
692
|
+
current: getId(item) === currentId,
|
|
693
|
+
selected: selectedIds?.has(getId(item)),
|
|
694
|
+
debug
|
|
695
|
+
}), draggable && /* @__PURE__ */ jsx(InternalPlaceholder, {
|
|
696
|
+
orientation,
|
|
697
|
+
location: index + 1.5
|
|
698
|
+
})] }, getId(item)))]
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
MosaicStackInner.displayName = MOSAIC_STACK_NAME;
|
|
702
|
+
var MosaicStack = MosaicStackInner;
|
|
703
|
+
var MOSAIC_VIRTUAL_STACK_NAME = "MosaicVirtualStack";
|
|
704
|
+
var MosaicVirtualStackInner = forwardRef(({ orientation = "vertical", getId, items, scrollIntoView = true, Tile, estimateSize, getScrollElement, overscan = 8, gap, pagination, paginationThreshold, onChange, draggable = true, debug, ...props }, forwardedRef) => {
|
|
705
|
+
invariant(Tile, void 0, {
|
|
706
|
+
"~LogMeta": "~LogMeta",
|
|
707
|
+
F: __dxlog_file,
|
|
708
|
+
L: 210,
|
|
709
|
+
S: void 0,
|
|
710
|
+
A: ["Tile", ""]
|
|
711
|
+
});
|
|
712
|
+
invariant(!pagination || !draggable, "pagination requires draggable={false}", {
|
|
713
|
+
"~LogMeta": "~LogMeta",
|
|
714
|
+
F: __dxlog_file,
|
|
715
|
+
L: 211,
|
|
716
|
+
S: void 0,
|
|
717
|
+
A: ["!pagination || !draggable", "'pagination requires draggable={false}'"]
|
|
718
|
+
});
|
|
719
|
+
const { id, dragging, currentId, selectedIds, registerScrollTo } = useMosaicContainerContext(MOSAIC_VIRTUAL_STACK_NAME);
|
|
720
|
+
const visibleItems = useVisibleItems({
|
|
721
|
+
id,
|
|
722
|
+
items,
|
|
723
|
+
dragging: dragging?.source.data,
|
|
724
|
+
getId
|
|
725
|
+
});
|
|
726
|
+
const wrappedEstimateSize = draggable ? (index) => index % 2 === 0 ? 0 : estimateSize(Math.floor(index / 2)) : estimateSize;
|
|
727
|
+
const { onChange: handlePaginationChange, leadingSpace } = useVirtualizerPagination({
|
|
728
|
+
items: visibleItems,
|
|
729
|
+
getId,
|
|
730
|
+
pagination,
|
|
731
|
+
threshold: paginationThreshold
|
|
732
|
+
});
|
|
733
|
+
const handleChange = useCallback((nextVirtualizer, sync) => {
|
|
734
|
+
handlePaginationChange(nextVirtualizer);
|
|
735
|
+
onChange?.(nextVirtualizer, sync);
|
|
736
|
+
}, [handlePaginationChange, onChange]);
|
|
737
|
+
const virtualizer = useVirtualizer({
|
|
738
|
+
count: draggable ? visibleItems.length * 2 + 1 : visibleItems.length,
|
|
739
|
+
estimateSize: wrappedEstimateSize,
|
|
740
|
+
gap,
|
|
741
|
+
paddingStart: (gap ?? 0) + leadingSpace,
|
|
742
|
+
paddingEnd: gap,
|
|
743
|
+
getItemKey: draggable ? (index) => index % 2 === 0 ? `ph-${index}` : getId(visibleItems[Math.floor(index / 2)]) : (index) => getId(visibleItems[index]),
|
|
744
|
+
getScrollElement,
|
|
745
|
+
overscan,
|
|
746
|
+
onChange: handleChange
|
|
747
|
+
});
|
|
748
|
+
const scrollToId = useCallback((targetId) => {
|
|
749
|
+
const itemIndex = visibleItems.findIndex((item) => getId(item) === targetId);
|
|
750
|
+
if (itemIndex >= 0) {
|
|
751
|
+
if (!scrollIntoView) return;
|
|
752
|
+
const virtualIndex = draggable ? itemIndex * 2 + 1 : itemIndex;
|
|
753
|
+
const [offset] = virtualizer.getOffsetForIndex(virtualIndex, "start") ?? [];
|
|
754
|
+
if (offset != null) virtualizer.scrollToOffset(Math.max(0, offset - (gap ?? 0)), { behavior: "smooth" });
|
|
755
|
+
}
|
|
756
|
+
}, [
|
|
757
|
+
visibleItems,
|
|
758
|
+
getId,
|
|
759
|
+
draggable,
|
|
760
|
+
virtualizer,
|
|
761
|
+
scrollIntoView,
|
|
762
|
+
gap
|
|
763
|
+
]);
|
|
764
|
+
useEffect(() => {
|
|
765
|
+
registerScrollTo(scrollToId);
|
|
766
|
+
return () => registerScrollTo(void 0);
|
|
767
|
+
}, [registerScrollTo, scrollToId]);
|
|
768
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
769
|
+
const getData = (index) => {
|
|
770
|
+
if (draggable) if (index % 2 === 0) return { location: Math.floor(index / 2) + .5 };
|
|
771
|
+
else return {
|
|
772
|
+
data: visibleItems[Math.floor(index / 2)],
|
|
773
|
+
location: Math.floor(index / 2) + 1
|
|
774
|
+
};
|
|
775
|
+
else return {
|
|
776
|
+
data: visibleItems[index],
|
|
777
|
+
location: index + 1
|
|
778
|
+
};
|
|
779
|
+
};
|
|
780
|
+
return /* @__PURE__ */ jsx("div", {
|
|
781
|
+
...composableProps(props, {
|
|
782
|
+
role: "list",
|
|
783
|
+
classNames: [
|
|
784
|
+
"flex shrink-0",
|
|
785
|
+
orientation === "horizontal" && "h-full [&>*]:shrink-0",
|
|
786
|
+
orientation === "vertical" && "flex-col"
|
|
787
|
+
]
|
|
788
|
+
}),
|
|
789
|
+
style: {
|
|
790
|
+
position: "relative",
|
|
791
|
+
...orientation === "vertical" ? {
|
|
792
|
+
width: "100%",
|
|
793
|
+
height: virtualizer.getTotalSize()
|
|
794
|
+
} : {
|
|
795
|
+
width: virtualizer.getTotalSize(),
|
|
796
|
+
height: "100%"
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
ref: forwardedRef,
|
|
800
|
+
children: virtualItems.map(({ key, index, start }) => {
|
|
801
|
+
const { data, location } = getData(index);
|
|
802
|
+
return /* @__PURE__ */ jsx("div", {
|
|
803
|
+
"data-index": index,
|
|
804
|
+
style: {
|
|
805
|
+
position: "absolute",
|
|
806
|
+
top: 0,
|
|
807
|
+
left: 0,
|
|
808
|
+
...orientation === "vertical" ? {
|
|
809
|
+
width: "100%",
|
|
810
|
+
transform: `translateY(${start}px)`
|
|
811
|
+
} : {
|
|
812
|
+
height: "100%",
|
|
813
|
+
transform: `translateX(${start}px)`
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
ref: virtualizer.measureElement,
|
|
817
|
+
children: data ? /* @__PURE__ */ jsx(Tile, {
|
|
818
|
+
id: getId(data),
|
|
819
|
+
data,
|
|
820
|
+
location,
|
|
821
|
+
draggable,
|
|
822
|
+
current: getId(data) === currentId,
|
|
823
|
+
selected: selectedIds?.has(getId(data)),
|
|
824
|
+
debug
|
|
825
|
+
}) : /* @__PURE__ */ jsx(InternalPlaceholder, {
|
|
826
|
+
orientation,
|
|
827
|
+
location
|
|
828
|
+
})
|
|
829
|
+
}, key);
|
|
830
|
+
})
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
MosaicVirtualStackInner.displayName = MOSAIC_VIRTUAL_STACK_NAME;
|
|
834
|
+
var MosaicVirtualStack = MosaicVirtualStackInner;
|
|
835
|
+
MosaicVirtualStack[Symbol.for("dxos.composable")] = true;
|
|
836
|
+
var InternalPlaceholder = (props) => {
|
|
837
|
+
return /* @__PURE__ */ jsx(MosaicPlaceholder, {
|
|
838
|
+
...props,
|
|
839
|
+
classNames: styles.placeholder.root,
|
|
840
|
+
children: /* @__PURE__ */ jsx("div", { className: mx("flex h-full bg-base-surface border border-dashed border-separator rounded-xs", styles.placeholder.content) })
|
|
841
|
+
});
|
|
842
|
+
};
|
|
843
|
+
InternalPlaceholder.displayName = "InternalPlaceholder";
|
|
844
|
+
//#endregion
|
|
845
|
+
//#region src/components/Mosaic/Tile.tsx
|
|
846
|
+
var MOSAIC_TILE_STATE_ATTR = "mosaic-tile-state";
|
|
847
|
+
var MosaicTile = slottable(({ children, asChild, dragHandle: dragHandleProp, allowedEdges: allowedEdgesProp, location, id, data: dataProp, draggable: draggableProp, current, selected, size: sizeProp, onSizeChange, minSize, maxSize, debug: _, ...props }, forwardedRef) => {
|
|
848
|
+
const Comp = asChild ? Slot : Primitive.div;
|
|
849
|
+
const rootRef = useRef(null);
|
|
850
|
+
const composedRef = composeRefs(rootRef, forwardedRef);
|
|
851
|
+
const { id: containerId, eventHandler, orientation, scrolling, setActiveLocation } = useMosaicContainerContext(MOSAIC_TILE_NAME);
|
|
852
|
+
const [state, setState] = useState({ type: "idle" });
|
|
853
|
+
const [registeredDragHandle, setRegisteredDragHandle] = useState(null);
|
|
854
|
+
const dragHandle = dragHandleProp ?? registeredDragHandle;
|
|
855
|
+
const [size, setInternalSize] = useState(sizeProp);
|
|
856
|
+
const setSize = useCallback((nextSize, commit) => {
|
|
857
|
+
setInternalSize(nextSize);
|
|
858
|
+
if (commit) onSizeChange?.(nextSize);
|
|
859
|
+
}, [onSizeChange]);
|
|
860
|
+
useEffect(() => {
|
|
861
|
+
setInternalSize(sizeProp);
|
|
862
|
+
}, [sizeProp]);
|
|
863
|
+
const allowedEdges = useMemo(() => allowedEdgesProp || (orientation === "vertical" ? ["top", "bottom"] : ["left", "right"]), [allowedEdgesProp, orientation]);
|
|
864
|
+
const data = useMemo(() => ({
|
|
865
|
+
type: "tile",
|
|
866
|
+
id,
|
|
867
|
+
containerId,
|
|
868
|
+
data: dataProp,
|
|
869
|
+
location
|
|
870
|
+
}), [
|
|
871
|
+
containerId,
|
|
872
|
+
location,
|
|
873
|
+
dataProp
|
|
874
|
+
]);
|
|
875
|
+
useLayoutEffect(() => {
|
|
876
|
+
const root = rootRef.current;
|
|
877
|
+
if (!draggableProp || !root || !containerId || scrolling) return;
|
|
878
|
+
const handleChange = ({ self, source }) => {
|
|
879
|
+
if (source.data.id !== dataProp.id) {
|
|
880
|
+
const closestEdge = extractClosestEdge(self.data);
|
|
881
|
+
const location = data.location + (closestEdge === "top" || closestEdge === "left" ? -.5 : .5);
|
|
882
|
+
setActiveLocation(location);
|
|
883
|
+
setState({
|
|
884
|
+
type: "target",
|
|
885
|
+
closestEdge
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
const handleNativeDragStart = (event) => {
|
|
890
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
|
891
|
+
};
|
|
892
|
+
root.addEventListener("dragstart", handleNativeDragStart);
|
|
893
|
+
return combine(() => root.removeEventListener("dragstart", handleNativeDragStart), draggable({
|
|
894
|
+
element: root,
|
|
895
|
+
dragHandle: dragHandle && state.type !== "preview" ? dragHandle : void 0,
|
|
896
|
+
canDrag: () => true,
|
|
897
|
+
getInitialData: () => data,
|
|
898
|
+
onGenerateDragPreview: ({ location, nativeSetDragImage }) => {
|
|
899
|
+
setCustomNativeDragPreview({
|
|
900
|
+
nativeSetDragImage,
|
|
901
|
+
getOffset: preserveOffsetOnSource({
|
|
902
|
+
element: root,
|
|
903
|
+
input: location.current.input
|
|
904
|
+
}),
|
|
905
|
+
render: ({ container }) => {
|
|
906
|
+
data.bounds = root.getBoundingClientRect();
|
|
907
|
+
setState({
|
|
908
|
+
type: "preview",
|
|
909
|
+
container,
|
|
910
|
+
rect: root.getBoundingClientRect()
|
|
911
|
+
});
|
|
912
|
+
return () => setState({ type: "dragging" });
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
},
|
|
916
|
+
onDragStart: () => {
|
|
917
|
+
setState({ type: "dragging" });
|
|
918
|
+
},
|
|
919
|
+
onDrop: () => {
|
|
920
|
+
setState({ type: "idle" });
|
|
921
|
+
}
|
|
922
|
+
}), dropTargetForElements({
|
|
923
|
+
element: root,
|
|
924
|
+
getData: ({ input, element }) => attachClosestEdge(data, {
|
|
925
|
+
input,
|
|
926
|
+
element,
|
|
927
|
+
allowedEdges
|
|
928
|
+
}),
|
|
929
|
+
canDrop: ({ source }) => {
|
|
930
|
+
const data = getSourceData(source);
|
|
931
|
+
return data && eventHandler.canDrop?.({ source: data }) || false;
|
|
932
|
+
},
|
|
933
|
+
getDropEffect: () => "move",
|
|
934
|
+
onDragEnter: ({ self, source }) => {
|
|
935
|
+
handleChange({
|
|
936
|
+
self,
|
|
937
|
+
source
|
|
938
|
+
});
|
|
939
|
+
},
|
|
940
|
+
onDragLeave: () => {
|
|
941
|
+
setState({ type: "idle" });
|
|
942
|
+
setActiveLocation(void 0);
|
|
943
|
+
},
|
|
944
|
+
onDrag: ({ self, source }) => {
|
|
945
|
+
handleChange({
|
|
946
|
+
self,
|
|
947
|
+
source
|
|
948
|
+
});
|
|
949
|
+
},
|
|
950
|
+
onDrop: () => {
|
|
951
|
+
setState({ type: "idle" });
|
|
952
|
+
setActiveLocation(void 0);
|
|
953
|
+
}
|
|
954
|
+
}));
|
|
955
|
+
}, [
|
|
956
|
+
rootRef,
|
|
957
|
+
forwardedRef,
|
|
958
|
+
containerId,
|
|
959
|
+
dragHandle,
|
|
960
|
+
eventHandler,
|
|
961
|
+
data,
|
|
962
|
+
scrolling,
|
|
963
|
+
allowedEdges,
|
|
964
|
+
setActiveLocation
|
|
965
|
+
]);
|
|
966
|
+
const { className, ...rest } = composableProps(props, { classNames: "relative outline-none" });
|
|
967
|
+
const vertical = orientation === "vertical";
|
|
968
|
+
const sized = size != null;
|
|
969
|
+
const bounded = sized || minSize != null || maxSize != null;
|
|
970
|
+
const sizeStyles = {
|
|
971
|
+
...sized && sizeStyle(size, vertical ? "vertical" : "horizontal"),
|
|
972
|
+
...minSize != null && { [vertical ? "minBlockSize" : "minInlineSize"]: `${minSize}rem` },
|
|
973
|
+
...maxSize != null && { [vertical ? "maxBlockSize" : "maxInlineSize"]: `${maxSize}rem` }
|
|
974
|
+
};
|
|
975
|
+
return /* @__PURE__ */ jsxs(MosaicTileContextProvider, {
|
|
976
|
+
state,
|
|
977
|
+
setDragHandle: setRegisteredDragHandle,
|
|
978
|
+
size,
|
|
979
|
+
setSize,
|
|
980
|
+
minSize,
|
|
981
|
+
maxSize,
|
|
982
|
+
children: [/* @__PURE__ */ jsx(Comp, {
|
|
983
|
+
...rest,
|
|
984
|
+
...bounded && resizeAttributes,
|
|
985
|
+
...bounded && { style: {
|
|
986
|
+
...rest.style,
|
|
987
|
+
...sizeStyles
|
|
988
|
+
} },
|
|
989
|
+
"data-object-id": id,
|
|
990
|
+
[`data-${MOSAIC_TILE_STATE_ATTR}`]: state.type,
|
|
991
|
+
role: "listitem",
|
|
992
|
+
"aria-current": current ? "true" : void 0,
|
|
993
|
+
"aria-selected": selected ? "true" : void 0,
|
|
994
|
+
className,
|
|
995
|
+
ref: composedRef,
|
|
996
|
+
children
|
|
997
|
+
}), state.type === "preview" && createPortal(/* @__PURE__ */ jsx(Comp, {
|
|
998
|
+
[`data-${MOSAIC_TILE_STATE_ATTR}`]: state.type,
|
|
999
|
+
className,
|
|
1000
|
+
style: {
|
|
1001
|
+
width: `${state.rect.width}px`,
|
|
1002
|
+
height: `${state.rect.height}px`
|
|
1003
|
+
},
|
|
1004
|
+
children
|
|
1005
|
+
}), state.container)]
|
|
1006
|
+
});
|
|
1007
|
+
});
|
|
1008
|
+
MosaicTile.displayName = MOSAIC_TILE_NAME;
|
|
1009
|
+
//#endregion
|
|
1010
|
+
//#region src/components/Mosaic/Mosaic.ts
|
|
1011
|
+
var Mosaic = {
|
|
1012
|
+
Container: MosaicContainer,
|
|
1013
|
+
Tile: MosaicTile,
|
|
1014
|
+
DragHandle: MosaicDragHandle,
|
|
1015
|
+
ResizeHandle: MosaicResizeHandle,
|
|
1016
|
+
Placeholder: MosaicPlaceholder,
|
|
1017
|
+
DropIndicator: MosaicDropIndicator,
|
|
1018
|
+
Stack: MosaicStack,
|
|
1019
|
+
VirtualStack: MosaicVirtualStack
|
|
1020
|
+
};
|
|
1021
|
+
//#endregion
|
|
1022
|
+
export { Focus as a, useVirtualizerPagination as c, styles as i, useMosaicTileContext as n, useFocus as o, useMosaicContainerContext as r, useVisibleItems as s, Mosaic as t };
|
|
1023
|
+
|
|
1024
|
+
//# sourceMappingURL=chunk-Mosaic.mjs.map
|