@dxos/react-ui-mosaic 0.10.0 → 0.11.1

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.
Files changed (115) hide show
  1. package/dist/lib/chunk-Mosaic.mjs +1024 -0
  2. package/dist/lib/chunk-Mosaic.mjs.map +1 -0
  3. package/dist/lib/index.mjs +653 -0
  4. package/dist/lib/index.mjs.map +1 -0
  5. package/dist/lib/playwright.mjs +200 -0
  6. package/dist/lib/playwright.mjs.map +1 -0
  7. package/dist/lib/testing.mjs +127 -0
  8. package/dist/lib/testing.mjs.map +1 -0
  9. package/dist/lib/translations.mjs +11 -0
  10. package/dist/lib/translations.mjs.map +1 -0
  11. package/dist/types/src/components/Board/Board.d.ts +7 -27
  12. package/dist/types/src/components/Board/Board.d.ts.map +1 -1
  13. package/dist/types/src/components/Board/Board.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/Board/BoardColumnContext.d.ts +10 -0
  15. package/dist/types/src/components/Board/BoardColumnContext.d.ts.map +1 -0
  16. package/dist/types/src/components/Board/BoardContext.d.ts +26 -0
  17. package/dist/types/src/components/Board/BoardContext.d.ts.map +1 -0
  18. package/dist/types/src/components/Board/Column.d.ts +2 -7
  19. package/dist/types/src/components/Board/Column.d.ts.map +1 -1
  20. package/dist/types/src/components/Board/index.d.ts +2 -0
  21. package/dist/types/src/components/Board/index.d.ts.map +1 -1
  22. package/dist/types/src/components/Focus/Focus.d.ts.map +1 -1
  23. package/dist/types/src/components/Mosaic/Container.d.ts +6 -34
  24. package/dist/types/src/components/Mosaic/Container.d.ts.map +1 -1
  25. package/dist/types/src/components/Mosaic/Mosaic.d.ts +10 -15
  26. package/dist/types/src/components/Mosaic/Mosaic.d.ts.map +1 -1
  27. package/dist/types/src/components/Mosaic/MosaicContainerContext.d.ts +34 -0
  28. package/dist/types/src/components/Mosaic/MosaicContainerContext.d.ts.map +1 -0
  29. package/dist/types/src/components/Mosaic/MosaicTileContext.d.ts +31 -0
  30. package/dist/types/src/components/Mosaic/MosaicTileContext.d.ts.map +1 -0
  31. package/dist/types/src/components/Mosaic/Placeholder.d.ts +3 -3
  32. package/dist/types/src/components/Mosaic/Placeholder.d.ts.map +1 -1
  33. package/dist/types/src/components/Mosaic/Stack.d.ts +8 -2
  34. package/dist/types/src/components/Mosaic/Stack.d.ts.map +1 -1
  35. package/dist/types/src/components/Mosaic/Stack.stories.d.ts.map +1 -1
  36. package/dist/types/src/components/Mosaic/Tile.d.ts +6 -32
  37. package/dist/types/src/components/Mosaic/Tile.d.ts.map +1 -1
  38. package/dist/types/src/components/Mosaic/VirtualStackPagination.stories.d.ts +13 -0
  39. package/dist/types/src/components/Mosaic/VirtualStackPagination.stories.d.ts.map +1 -0
  40. package/dist/types/src/components/Mosaic/index.d.ts +0 -1
  41. package/dist/types/src/components/Mosaic/index.d.ts.map +1 -1
  42. package/dist/types/src/components/SearchStack/SearchStack.d.ts +1 -1
  43. package/dist/types/src/components/SearchStack/SearchStack.d.ts.map +1 -1
  44. package/dist/types/src/hooks/index.d.ts +1 -0
  45. package/dist/types/src/hooks/index.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useDefaultColumnEventHandler.d.ts +2 -2
  47. package/dist/types/src/hooks/useDefaultColumnEventHandler.d.ts.map +1 -1
  48. package/dist/types/src/hooks/useEventHandlerAdapter.d.ts +3 -3
  49. package/dist/types/src/hooks/useEventHandlerAdapter.d.ts.map +1 -1
  50. package/dist/types/src/hooks/useVirtualizerPagination.d.ts +60 -0
  51. package/dist/types/src/hooks/useVirtualizerPagination.d.ts.map +1 -0
  52. package/dist/types/src/hooks/useVirtualizerPagination.test.d.ts +2 -0
  53. package/dist/types/src/hooks/useVirtualizerPagination.test.d.ts.map +1 -0
  54. package/dist/types/src/hooks/useVisibleItems.d.ts +2 -2
  55. package/dist/types/src/hooks/useVisibleItems.d.ts.map +1 -1
  56. package/dist/types/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +35 -37
  58. package/src/components/Board/Board.stories.tsx +7 -6
  59. package/src/components/Board/Board.tsx +9 -54
  60. package/src/components/Board/BoardColumnContext.ts +25 -0
  61. package/src/components/Board/BoardContext.ts +43 -0
  62. package/src/components/Board/Column.tsx +13 -27
  63. package/src/components/Board/Item.tsx +2 -2
  64. package/src/components/Board/index.ts +3 -0
  65. package/src/components/Mosaic/Container.tsx +31 -53
  66. package/src/components/Mosaic/DragHandle.tsx +1 -1
  67. package/src/components/Mosaic/{Mosaic.tsx → Mosaic.ts} +5 -16
  68. package/src/components/Mosaic/MosaicContainerContext.ts +44 -0
  69. package/src/components/Mosaic/MosaicTileContext.ts +34 -0
  70. package/src/components/Mosaic/Placeholder.tsx +7 -8
  71. package/src/components/Mosaic/ResizeHandle.stories.tsx +4 -4
  72. package/src/components/Mosaic/ResizeHandle.tsx +2 -2
  73. package/src/components/Mosaic/Stack.stories.tsx +49 -13
  74. package/src/components/Mosaic/Stack.tsx +30 -9
  75. package/src/components/Mosaic/Tile.tsx +33 -33
  76. package/src/components/Mosaic/VirtualStackPagination.stories.tsx +423 -0
  77. package/src/components/Mosaic/index.ts +0 -1
  78. package/src/hooks/index.ts +1 -0
  79. package/src/hooks/useDefaultColumnEventHandler.ts +2 -2
  80. package/src/hooks/useEventHandlerAdapter.ts +4 -5
  81. package/src/hooks/useVirtualizerPagination.test.tsx +105 -0
  82. package/src/hooks/useVirtualizerPagination.ts +473 -0
  83. package/src/hooks/useVisibleItems.ts +2 -2
  84. package/src/playwright/smoke.spec.ts +4 -1
  85. package/src/testing/DefaultStackTile.tsx +1 -1
  86. package/src/testing/decorator.tsx +3 -3
  87. package/dist/lib/browser/chunk-OBVIYZLR.mjs +0 -1119
  88. package/dist/lib/browser/chunk-OBVIYZLR.mjs.map +0 -7
  89. package/dist/lib/browser/index.mjs +0 -701
  90. package/dist/lib/browser/index.mjs.map +0 -7
  91. package/dist/lib/browser/meta.json +0 -1
  92. package/dist/lib/browser/playwright/index.mjs +0 -256
  93. package/dist/lib/browser/playwright/index.mjs.map +0 -7
  94. package/dist/lib/browser/testing/index.mjs +0 -136
  95. package/dist/lib/browser/testing/index.mjs.map +0 -7
  96. package/dist/lib/browser/translations.mjs +0 -18
  97. package/dist/lib/browser/translations.mjs.map +0 -7
  98. package/dist/lib/node-esm/chunk-4IEMCMIV.mjs +0 -1121
  99. package/dist/lib/node-esm/chunk-4IEMCMIV.mjs.map +0 -7
  100. package/dist/lib/node-esm/index.mjs +0 -702
  101. package/dist/lib/node-esm/index.mjs.map +0 -7
  102. package/dist/lib/node-esm/meta.json +0 -1
  103. package/dist/lib/node-esm/playwright/index.mjs +0 -258
  104. package/dist/lib/node-esm/playwright/index.mjs.map +0 -7
  105. package/dist/lib/node-esm/testing/index.mjs +0 -137
  106. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  107. package/dist/lib/node-esm/translations.mjs +0 -20
  108. package/dist/lib/node-esm/translations.mjs.map +0 -7
  109. package/dist/types/src/components/Mosaic/Root.d.ts +0 -35
  110. package/dist/types/src/components/Mosaic/Root.d.ts.map +0 -1
  111. package/dist/types/src/components/Mosaic/types.d.ts +0 -86
  112. package/dist/types/src/components/Mosaic/types.d.ts.map +0 -1
  113. package/src/components/Mosaic/Root.tsx +0 -288
  114. package/src/components/Mosaic/types.ts +0 -94
  115. /package/src/components/Focus/{Focus.tsx → Focus.ts} +0 -0
@@ -0,0 +1,653 @@
1
+ import { a as Focus, c as useVirtualizerPagination, i as styles, n as useMosaicTileContext, o as useFocus, r as useMosaicContainerContext, s as useVisibleItems, t as Mosaic } from "./chunk-Mosaic.mjs";
2
+ import { createContext } from "@radix-ui/react-context";
3
+ import { useAtomValue } from "@effect-atom/atom-react";
4
+ import { forwardRef, useCallback, useMemo, useRef, useState } from "react";
5
+ import { Card, Icon, IconButton, ScrollArea, Tag, Toolbar, composable, composableProps, useTranslation } from "@dxos/react-ui";
6
+ import { useDndRootContext } from "@dxos/react-ui-dnd";
7
+ import { JsonHighlighter } from "@dxos/react-ui-syntax-highlighter";
8
+ import { getHashStyles, mx } from "@dxos/ui-theme";
9
+ import { createPortal } from "react-dom";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { log } from "@dxos/log";
12
+ import { arrayMove } from "@dxos/util";
13
+ import { StateEffect, StateField } from "@codemirror/state";
14
+ import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view";
15
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
16
+ import { Obj, Ref } from "@dxos/echo";
17
+ import { useObject } from "@dxos/echo-react";
18
+ import { Menu, createMenuAction } from "@dxos/react-ui-menu";
19
+ import { translationKey } from "#translations";
20
+ //#region src/components/Board/BoardColumnContext.ts
21
+ var BOARD_COLUMN_CONTEXT_NAME = "Board.Column";
22
+ var [BoardColumnProvider, useBoardColumnContext] = createContext(BOARD_COLUMN_CONTEXT_NAME, null);
23
+ /** Returns the current column when rendered inside a board column (e.g., in column header or item tile). */
24
+ function useBoardColumn() {
25
+ return useBoardColumnContext(BOARD_COLUMN_CONTEXT_NAME)?.column;
26
+ }
27
+ var [BoardContextProvider, useBoardContext] = createContext("Board");
28
+ /** Hook to read the board model from context (e.g. in custom column tiles). Pass TColumn and TItem for typed model. */
29
+ function useBoard(displayName) {
30
+ return useBoardContext(displayName ?? "Board");
31
+ }
32
+ //#endregion
33
+ //#region src/hooks/useContainerDebug.tsx
34
+ /**
35
+ * Hook that returns a component to be rendered in the container's viewport (within the context),
36
+ * and a component that creates a portal in the container's debug area.
37
+ */
38
+ var useContainerDebug = (debug) => {
39
+ const debugRef = useRef(null);
40
+ return useMemo(() => {
41
+ if (!debug) return [() => null, void 0];
42
+ return [({ classNames }) => /* @__PURE__ */ jsx("div", {
43
+ className: mx("overflow-hidden", classNames),
44
+ ref: debugRef
45
+ }), () => debugRef.current && createPortal(/* @__PURE__ */ jsx(ContainerInfo, {}), debugRef.current)];
46
+ }, [debug, debugRef]);
47
+ };
48
+ var CONTAINER_INFO_NAME = "ContainerInfo";
49
+ var ContainerInfo = forwardRef(({ classNames }, forwardedRef) => {
50
+ const { id, state, activeLocation, scrolling } = useMosaicContainerContext(CONTAINER_INFO_NAME);
51
+ const counter = useRef(0);
52
+ return /* @__PURE__ */ jsx(JsonHighlighter, {
53
+ data: {
54
+ id,
55
+ activeLocation,
56
+ scrolling,
57
+ state,
58
+ count: counter.current++
59
+ },
60
+ classNames: mx("text-xs", classNames),
61
+ ref: forwardedRef
62
+ });
63
+ });
64
+ ContainerInfo.displayName = CONTAINER_INFO_NAME;
65
+ //#endregion
66
+ //#region src/hooks/useEventHandlerAdapter.ts
67
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-mosaic/src/hooks/useEventHandlerAdapter.ts";
68
+ /**
69
+ * Returns a handler for the given items.
70
+ * NOTE: This supports arrays of objects, or arrays of refs to objects.
71
+ */
72
+ var useEventHandlerAdapter = ({ items, getId, get, make, onChange, ...props }) => {
73
+ return useMemo(() => ({
74
+ ...props,
75
+ onTake: ({ source }, cb) => {
76
+ log.info("onTake", { source }, {
77
+ "~LogMeta": "~LogMeta",
78
+ F: __dxlog_file,
79
+ L: 60,
80
+ S: void 0
81
+ });
82
+ const mutate = (items) => {
83
+ const from = items.findIndex((item) => getId(item) === source.id);
84
+ if (from !== -1) items.splice(from, 1);
85
+ };
86
+ if (onChange) onChange(mutate);
87
+ else mutate(items);
88
+ cb(source.data);
89
+ },
90
+ onDrop: ({ source, target }) => {
91
+ log.info("onDrop", {
92
+ source,
93
+ target
94
+ }, {
95
+ "~LogMeta": "~LogMeta",
96
+ F: __dxlog_file,
97
+ L: 77,
98
+ S: void 0
99
+ });
100
+ const mutate = (items) => {
101
+ const to = target?.type === "tile" || target?.type === "placeholder" ? target.location : target?.type === "container" ? items.length : -1;
102
+ const from = items.findIndex((item) => getId(item) === source.id);
103
+ const insertIndex = typeof to === "number" && to >= 0 ? Math.floor(to) : -1;
104
+ if (insertIndex !== -1) if (from !== -1) arrayMove(items, from, insertIndex);
105
+ else items.splice(insertIndex, 0, make(get(source.data)));
106
+ };
107
+ if (onChange) onChange(mutate);
108
+ else mutate(items);
109
+ }
110
+ }), [items, onChange]);
111
+ };
112
+ //#endregion
113
+ //#region src/hooks/useDefaultColumnEventHandler.ts
114
+ /**
115
+ * Returns the default column drag/drop handler (no persistence).
116
+ * Use this when no custom eventHandler is passed to Board.Content.
117
+ */
118
+ var useDefaultColumnEventHandler = (id, model, items) => useEventHandlerAdapter({
119
+ id,
120
+ items,
121
+ getId: model.getColumnId,
122
+ get: (data) => data,
123
+ make: (object) => object,
124
+ canDrop: ({ source }) => model.isColumn(source.data)
125
+ });
126
+ //#endregion
127
+ //#region src/components/Mosaic/extension.ts
128
+ var dropHandler = ({ onDrop = handleDrop } = {}) => {
129
+ const views = /* @__PURE__ */ new Set();
130
+ let state = null;
131
+ const reset = () => {
132
+ views.forEach((view) => view.dispatch({ effects: setDrop.of(null) }));
133
+ state = null;
134
+ };
135
+ return {
136
+ extension: [dropField, ViewPlugin.fromClass(class {
137
+ _view;
138
+ constructor(_view) {
139
+ this._view = _view;
140
+ views.add(_view);
141
+ }
142
+ destroy() {
143
+ views.delete(this._view);
144
+ }
145
+ })],
146
+ update: (position) => {
147
+ if (!position) {
148
+ reset();
149
+ return;
150
+ }
151
+ views.forEach((view) => {
152
+ const rect = view.dom.getBoundingClientRect();
153
+ if (position.x >= rect.left && position.x <= rect.right && position.y >= rect.top && position.y <= rect.bottom) {
154
+ let pos = view.posAtCoords(position);
155
+ if (pos !== null) {
156
+ const word = view.state.wordAt(pos);
157
+ if (word) pos = Math.abs(pos - word.from) < Math.abs(pos - word.to) ? word.from : word.to;
158
+ view.dispatch({ effects: setDrop.of(pos) });
159
+ state = {
160
+ view,
161
+ pos
162
+ };
163
+ return;
164
+ }
165
+ }
166
+ view.dispatch({ effects: setDrop.of(null) });
167
+ });
168
+ },
169
+ cancel: () => {
170
+ reset();
171
+ },
172
+ drop: ({ text, url }) => {
173
+ if (state) onDrop(state.view, state.pos, {
174
+ text,
175
+ url
176
+ });
177
+ reset();
178
+ }
179
+ };
180
+ };
181
+ var handleDrop = (view, pos, event) => {
182
+ if (view.state.doc.lineAt(pos).text.trim() === "") view.dispatch({
183
+ changes: {
184
+ from: pos,
185
+ insert: `\n![${event.text}](${event.url})\n`
186
+ },
187
+ effects: setDrop.of(null)
188
+ });
189
+ else view.dispatch({
190
+ changes: {
191
+ from: pos,
192
+ insert: ` [${event.text}](${event.url})`
193
+ },
194
+ effects: setDrop.of(null)
195
+ });
196
+ };
197
+ var setDrop = StateEffect.define();
198
+ var dropField = StateField.define({
199
+ create: () => Decoration.none,
200
+ update: (decorations, tr) => {
201
+ for (const effect of tr.effects) if (effect.is(setDrop)) {
202
+ if (effect.value === null) return Decoration.none;
203
+ return Decoration.set([Decoration.widget({
204
+ widget: new class extends WidgetType {
205
+ toDOM() {
206
+ const dom = document.createElement("span");
207
+ dom.className = "cm-drop-cursor";
208
+ dom.style.borderLeft = "2px solid var(--color-accent-bg)";
209
+ dom.style.display = "inline-block";
210
+ dom.style.height = "1em";
211
+ dom.style.verticalAlign = "middle";
212
+ return dom;
213
+ }
214
+ }(),
215
+ side: 0
216
+ }).range(effect.value)]);
217
+ }
218
+ return decorations.map(tr.changes);
219
+ },
220
+ provide: (field) => EditorView.decorations.from(field)
221
+ });
222
+ //#endregion
223
+ //#region src/components/Board/Item.tsx
224
+ var BOARD_ITEM_NAME = "Board.Item";
225
+ var BoardItemInner = forwardRef(({ classNames, data, location, debug, draggable }, forwardedRef) => {
226
+ const { t } = useTranslation(translationKey);
227
+ const rootRef = useRef(null);
228
+ const composedRef = useComposedRefs(rootRef, forwardedRef);
229
+ const [dragHandle, setDragHandle] = useState(null);
230
+ const { model } = useBoard(BOARD_ITEM_NAME);
231
+ const column = useBoardColumn();
232
+ const items = useMemo(() => column != null && model.onItemDelete ? [createMenuAction("delete-item", () => model.onItemDelete?.(column, data), {
233
+ label: t("delete-menu.label"),
234
+ icon: "ph--trash--regular"
235
+ })] : [], [
236
+ column,
237
+ data,
238
+ model.onItemDelete,
239
+ t
240
+ ]);
241
+ if (!data) return null;
242
+ const label = Obj.getLabel(data);
243
+ const description = Obj.getDescription(data);
244
+ return /* @__PURE__ */ jsx(Menu.Root, { children: /* @__PURE__ */ jsx(Mosaic.Tile, {
245
+ ref: rootRef,
246
+ asChild: true,
247
+ draggable,
248
+ dragHandle,
249
+ id: data.id,
250
+ data,
251
+ location,
252
+ debug,
253
+ children: /* @__PURE__ */ jsx(Focus.Item, {
254
+ asChild: true,
255
+ children: /* @__PURE__ */ jsxs(Card.Root, {
256
+ classNames,
257
+ "data-testid": "board-item",
258
+ ref: composedRef,
259
+ onClick: (event) => event.currentTarget.focus(),
260
+ children: [
261
+ /* @__PURE__ */ jsxs(Card.Header, { children: [
262
+ /* @__PURE__ */ jsx(Card.DragHandle, {
263
+ ref: setDragHandle,
264
+ testId: "mosaicBoard.cardDragHandle"
265
+ }),
266
+ /* @__PURE__ */ jsx(Card.Title, {
267
+ "data-testid": "mosaicBoard.cardTitle",
268
+ children: label
269
+ }),
270
+ /* @__PURE__ */ jsx(Card.Block, {
271
+ end: true,
272
+ children: /* @__PURE__ */ jsx(Menu.Trigger, {
273
+ asChild: true,
274
+ disabled: !items?.length,
275
+ children: /* @__PURE__ */ jsx(IconButton, {
276
+ iconOnly: true,
277
+ variant: "ghost",
278
+ icon: "ph--dots-three-vertical--regular",
279
+ label: t("action-menu.label")
280
+ })
281
+ })
282
+ }),
283
+ /* @__PURE__ */ jsx(Menu.Content, { items })
284
+ ] }),
285
+ /* @__PURE__ */ jsxs(Card.Row, {
286
+ classNames: "text-description",
287
+ children: [/* @__PURE__ */ jsx(Card.Block, { children: /* @__PURE__ */ jsx(Icon, { icon: "ph--note--regular" }) }), /* @__PURE__ */ jsx(Card.Text, { children: description })]
288
+ }),
289
+ /* @__PURE__ */ jsxs(Card.Row, { children: [/* @__PURE__ */ jsx(Card.Block, { children: /* @__PURE__ */ jsx(Icon, { icon: "ph--tag--regular" }) }), label && /* @__PURE__ */ jsx("div", {
290
+ className: "shrink-0 flex gap-1 items-center text-xs",
291
+ children: /* @__PURE__ */ jsx(Tag, {
292
+ hue: getHashStyles(label).hue,
293
+ children: label
294
+ })
295
+ })] })
296
+ ]
297
+ })
298
+ })
299
+ }) });
300
+ });
301
+ BoardItemInner.displayName = BOARD_ITEM_NAME;
302
+ /**
303
+ * Default board tile.
304
+ */
305
+ var BoardItem = BoardItemInner;
306
+ //#endregion
307
+ //#region src/components/Board/Column.tsx
308
+ var BOARD_COLUMN_ROOT_NAME = "Board.Column.Root";
309
+ var BoardColumnRootInner = composable(({ classNames, children, location, data, debug, draggable, dragHandle, ...rest }, forwardedRef) => {
310
+ const { model } = useBoard(BOARD_COLUMN_ROOT_NAME);
311
+ return /* @__PURE__ */ jsx(Mosaic.Tile, {
312
+ asChild: true,
313
+ location,
314
+ id: model.getColumnId(data),
315
+ data,
316
+ debug,
317
+ draggable,
318
+ dragHandle,
319
+ children: /* @__PURE__ */ jsx(Focus.Group, {
320
+ ...rest,
321
+ "data-testid": "board-column",
322
+ border: true,
323
+ classNames: mx("group/column", "h-full w-full md:w-card-default-width snap-center dx-deck-surface", "overflow-hidden", classNames),
324
+ ref: forwardedRef,
325
+ children: /* @__PURE__ */ jsx(BoardColumnProvider, {
326
+ column: data,
327
+ children
328
+ })
329
+ })
330
+ });
331
+ });
332
+ BoardColumnRootInner.displayName = BOARD_COLUMN_ROOT_NAME;
333
+ var BoardColumnRoot = BoardColumnRootInner;
334
+ var BOARD_COLUMN_HEADER_NAME = "Board.Column.Header";
335
+ var BoardColumnHeader = composable(({ label, dragHandleRef, ...props }, forwardedRef) => {
336
+ const { t } = useTranslation(translationKey);
337
+ const { model } = useBoard(BOARD_COLUMN_HEADER_NAME);
338
+ const column = useBoardColumn();
339
+ const columnMenuItems = useMemo(() => column != null && model.onColumnDelete ? [createMenuAction("delete-column", () => model.onColumnDelete?.(column), {
340
+ label: t("delete-menu.label"),
341
+ icon: "ph--trash--regular"
342
+ })] : [], [
343
+ column,
344
+ model.onColumnDelete,
345
+ t
346
+ ]);
347
+ return /* @__PURE__ */ jsx(Menu.Root, { children: /* @__PURE__ */ jsxs(Toolbar.Root, {
348
+ ...composableProps(props, { classNames: "gap-0" }),
349
+ "data-testid": "board-column-header",
350
+ ref: forwardedRef,
351
+ children: [
352
+ /* @__PURE__ */ jsx(Toolbar.DragHandle, {
353
+ ref: dragHandleRef,
354
+ testId: "mosaicBoard.columnDragHandle"
355
+ }),
356
+ /* @__PURE__ */ jsx(Toolbar.Text, {
357
+ classNames: "grow px-0",
358
+ "data-testid": "mosaicBoard.columnTitle",
359
+ children: label
360
+ }),
361
+ /* @__PURE__ */ jsx(Menu.Trigger, {
362
+ asChild: true,
363
+ disabled: !columnMenuItems?.length,
364
+ children: /* @__PURE__ */ jsx(Toolbar.IconButton, {
365
+ iconOnly: true,
366
+ variant: "ghost",
367
+ icon: "ph--dots-three-vertical--regular",
368
+ label: t("action-menu.label")
369
+ })
370
+ }),
371
+ /* @__PURE__ */ jsx(Menu.Content, { items: columnMenuItems })
372
+ ]
373
+ }) });
374
+ });
375
+ BoardColumnHeader.displayName = BOARD_COLUMN_HEADER_NAME;
376
+ var BOARD_COLUMN_BODY_NAME = "Board.Column.Body";
377
+ var BoardColumnBody = composable(({ data, eventHandler, Tile = BoardItem, debug, ...props }, forwardedRef) => {
378
+ const { model } = useBoard(BOARD_COLUMN_BODY_NAME);
379
+ const [viewport, setViewport] = useState(null);
380
+ const items = useAtomValue(model.items(data));
381
+ return /* @__PURE__ */ jsx(Mosaic.Container, {
382
+ ...composableProps(props),
383
+ asChild: true,
384
+ withFocus: true,
385
+ orientation: "vertical",
386
+ autoScroll: viewport,
387
+ eventHandler,
388
+ debug,
389
+ ref: forwardedRef,
390
+ children: /* @__PURE__ */ jsx(ScrollArea.Root, {
391
+ orientation: "vertical",
392
+ thin: true,
393
+ centered: true,
394
+ padding: true,
395
+ children: /* @__PURE__ */ jsx(ScrollArea.Viewport, {
396
+ classNames: "snap-y md:snap-none",
397
+ ref: setViewport,
398
+ children: /* @__PURE__ */ jsx(Mosaic.Stack, {
399
+ items,
400
+ getId: model.getItemId,
401
+ Tile
402
+ })
403
+ })
404
+ })
405
+ });
406
+ });
407
+ BoardColumnBody.displayName = BOARD_COLUMN_BODY_NAME;
408
+ var BOARD_COLUMN_FOOTER_NAME = "Board.Column.Footer";
409
+ var BoardColumnFooter = forwardRef(({ classNames, data, onAdd }, forwardedRef) => {
410
+ const { t } = useTranslation(translationKey);
411
+ const { model } = useBoard(BOARD_COLUMN_FOOTER_NAME);
412
+ const handleAdd = onAdd ?? (model.onItemCreate && data ? () => void model.onItemCreate?.(data) : void 0);
413
+ return /* @__PURE__ */ jsx(Toolbar.Root, {
414
+ classNames: mx("rounded-b-sm border-t border-separator", classNames),
415
+ ref: forwardedRef,
416
+ children: handleAdd && /* @__PURE__ */ jsx(IconButton, {
417
+ "data-testid": "board-column-add-item",
418
+ classNames: "group-hover/column:opacity-100 md:opacity-0 transition transition-opacity duration-500",
419
+ variant: "ghost",
420
+ icon: "ph--plus--regular",
421
+ iconOnly: true,
422
+ label: t("add-item.label"),
423
+ onClick: handleAdd
424
+ })
425
+ });
426
+ });
427
+ BoardColumnFooter.displayName = BOARD_COLUMN_FOOTER_NAME;
428
+ var BOARD_DEFAULT_COLUMN_NAME = "Board.DefaultColumn";
429
+ var DefaultBoardColumn = forwardRef(({ classNames, location, data, debug, draggable, Tile = BoardItem }, forwardedRef) => {
430
+ const { model } = useBoard(BOARD_DEFAULT_COLUMN_NAME);
431
+ const [DebugInfo, debugHandler] = useContainerDebug(debug);
432
+ const [dragHandle, setDragHandle] = useState(null);
433
+ const [column, updateColumn] = useObject(data);
434
+ const eventHandler = useEventHandlerAdapter({
435
+ id: model.getColumnId(data),
436
+ items: column?.items ?? [],
437
+ getId: (ref) => ref.target?.id ?? ref.uri,
438
+ get: (refOrObj) => Ref.isRef(refOrObj) ? refOrObj.target : refOrObj,
439
+ make: (object) => Ref.make(object),
440
+ canDrop: ({ source }) => {
441
+ const item = Ref.isRef(source.data) ? source.data.target : source.data;
442
+ return item != null && Obj.isObject(item) && model.isItem(item);
443
+ },
444
+ onChange: (mutator) => updateColumn((col) => mutator(col.items))
445
+ });
446
+ return /* @__PURE__ */ jsxs(BoardColumnRootInner, {
447
+ classNames: mx("group/column grid", debug ? "grid-rows-[var(--dx-rail-action)_1fr_20rem]" : "grid-rows-[var(--dx-rail-action)_1fr_var(--dx-rail-action)]", classNames),
448
+ location,
449
+ data,
450
+ draggable,
451
+ dragHandle,
452
+ ref: forwardedRef,
453
+ children: [
454
+ /* @__PURE__ */ jsx(BoardColumnHeader, {
455
+ label: Obj.getLabel(data) ?? data.id,
456
+ dragHandleRef: setDragHandle
457
+ }),
458
+ /* @__PURE__ */ jsx(BoardColumnBody, {
459
+ data,
460
+ eventHandler,
461
+ debug: debugHandler,
462
+ Tile
463
+ }),
464
+ /* @__PURE__ */ jsxs("div", {
465
+ className: "flex flex-col col-span-full",
466
+ children: [/* @__PURE__ */ jsx(BoardColumnFooter, { data }), /* @__PURE__ */ jsx(DebugInfo, {})]
467
+ })
468
+ ]
469
+ });
470
+ });
471
+ DefaultBoardColumn.displayName = BOARD_DEFAULT_COLUMN_NAME;
472
+ var BoardColumn = {
473
+ Root: BoardColumnRoot,
474
+ Header: BoardColumnHeader,
475
+ Body: BoardColumnBody,
476
+ Footer: BoardColumnFooter
477
+ };
478
+ //#endregion
479
+ //#region src/components/Board/Board.tsx
480
+ var BOARD_ROOT_NAME = "Board.Root";
481
+ var BoardRootInner = ({ model, children }) => {
482
+ return /* @__PURE__ */ jsx(BoardContextProvider, {
483
+ model,
484
+ children
485
+ });
486
+ };
487
+ BoardRootInner.displayName = BOARD_ROOT_NAME;
488
+ var BoardRoot = BoardRootInner;
489
+ var BOARD_CONTENT_NAME = "Board.Content";
490
+ var BoardContentInner = composable(({ debug, eventHandler, Tile = DefaultBoardColumn, ...props }, forwardedRef) => {
491
+ const { model } = useBoardContext(BOARD_CONTENT_NAME);
492
+ const [DebugInfo, debugHandler] = useContainerDebug(debug);
493
+ const [viewport, setViewport] = useState(null);
494
+ const items = useAtomValue(model.columns);
495
+ return /* @__PURE__ */ jsxs("div", {
496
+ ...composableProps(props, { classNames: "dx-container" }),
497
+ ref: forwardedRef,
498
+ children: [/* @__PURE__ */ jsx(Focus.Group, {
499
+ asChild: true,
500
+ orientation: "horizontal",
501
+ children: /* @__PURE__ */ jsx(Mosaic.Container, {
502
+ asChild: true,
503
+ withFocus: true,
504
+ orientation: "horizontal",
505
+ autoScroll: viewport,
506
+ eventHandler,
507
+ debug: debugHandler,
508
+ placeholderDebug: debug,
509
+ children: /* @__PURE__ */ jsx(ScrollArea.Root, {
510
+ orientation: "horizontal",
511
+ centered: true,
512
+ padding: true,
513
+ children: /* @__PURE__ */ jsx(ScrollArea.Viewport, {
514
+ classNames: "snap-mandatory snap-x md:snap-none",
515
+ ref: setViewport,
516
+ children: /* @__PURE__ */ jsx(Mosaic.Stack, {
517
+ items,
518
+ getId: model.getColumnId,
519
+ Tile,
520
+ debug
521
+ })
522
+ })
523
+ })
524
+ })
525
+ }), /* @__PURE__ */ jsx(DebugInfo, {})]
526
+ });
527
+ });
528
+ BoardContentInner.displayName = BOARD_CONTENT_NAME;
529
+ var BoardContent = BoardContentInner;
530
+ var BOARD_PLACEHOLDER_NAME = "Board.Placeholder";
531
+ var BoardPlaceholder = (props) => {
532
+ return /* @__PURE__ */ jsx(Mosaic.Placeholder, {
533
+ ...props,
534
+ classNames: styles.placeholder.root,
535
+ children: /* @__PURE__ */ jsx("div", { className: mx("flex h-full border border-dashed border-separator rounded-xs", styles.placeholder.content) })
536
+ });
537
+ };
538
+ BoardPlaceholder.displayName = BOARD_PLACEHOLDER_NAME;
539
+ var BOARD_DEBUG_NAME = "Board.Debug";
540
+ var BoardDebug = forwardRef(({ classNames }, forwardedRef) => {
541
+ const { containers, dragging } = useDndRootContext(BOARD_DEBUG_NAME);
542
+ const counter = useRef(0);
543
+ return /* @__PURE__ */ jsx(JsonHighlighter, {
544
+ data: {
545
+ containers,
546
+ dragging,
547
+ count: counter.current++
548
+ },
549
+ classNames: mx("text-xs", classNames),
550
+ ref: forwardedRef
551
+ });
552
+ });
553
+ BoardDebug.displayName = BOARD_DEBUG_NAME;
554
+ var Board = {
555
+ Root: BoardRoot,
556
+ Content: BoardContent,
557
+ Column: BoardColumn,
558
+ Item: BoardItem,
559
+ Placeholder: BoardPlaceholder,
560
+ Debug: BoardDebug
561
+ };
562
+ //#endregion
563
+ //#region src/components/SearchStack/SearchStack.tsx
564
+ /**
565
+ * Card-based search result stack component using mosaic layout.
566
+ */
567
+ var SearchStack = composable(({ results = [], currentId, onAction, ...props }, forwardedRef) => {
568
+ const [viewport, setViewport] = useState(null);
569
+ const items = useMemo(() => results.map((result) => ({
570
+ result,
571
+ onAction
572
+ })), [results, onAction]);
573
+ const handleCurrentChange = useCallback((id) => {
574
+ if (id) onAction?.({
575
+ type: "select",
576
+ resultId: id
577
+ });
578
+ }, [onAction]);
579
+ const handleKeyDown = useCallback((event) => {
580
+ if (event.key === "Enter") {
581
+ event.preventDefault();
582
+ document.activeElement?.click();
583
+ }
584
+ }, []);
585
+ return /* @__PURE__ */ jsx(Focus.Group, {
586
+ asChild: true,
587
+ ...composableProps(props),
588
+ onKeyDown: handleKeyDown,
589
+ ref: forwardedRef,
590
+ children: /* @__PURE__ */ jsx(Mosaic.Container, {
591
+ asChild: true,
592
+ withFocus: true,
593
+ currentId,
594
+ onCurrentChange: handleCurrentChange,
595
+ children: /* @__PURE__ */ jsx(ScrollArea.Root, {
596
+ orientation: "vertical",
597
+ padding: true,
598
+ centered: true,
599
+ thin: true,
600
+ children: /* @__PURE__ */ jsx(ScrollArea.Viewport, {
601
+ ref: setViewport,
602
+ children: /* @__PURE__ */ jsx(Mosaic.VirtualStack, {
603
+ Tile: SearchTile,
604
+ classNames: "my-2",
605
+ gap: 8,
606
+ items,
607
+ draggable: false,
608
+ getId: (item) => item.result.id,
609
+ getScrollElement: () => viewport,
610
+ estimateSize: () => 100
611
+ })
612
+ })
613
+ })
614
+ })
615
+ });
616
+ });
617
+ SearchStack.displayName = "SearchStack";
618
+ /**
619
+ * Default search result tile with a simple Card header.
620
+ */
621
+ var SearchTile = forwardRef(({ data, location, current }, forwardedRef) => {
622
+ const { result } = data;
623
+ const { setCurrentId } = useMosaicContainerContext("SearchTile");
624
+ const handleCurrentChange = useCallback(() => {
625
+ setCurrentId(result.id);
626
+ }, [result.id, setCurrentId]);
627
+ return /* @__PURE__ */ jsx(Mosaic.Tile, {
628
+ asChild: true,
629
+ classNames: "dx-hover dx-current",
630
+ id: result.id,
631
+ data,
632
+ location,
633
+ current,
634
+ children: /* @__PURE__ */ jsx(Focus.Item, {
635
+ asChild: true,
636
+ current,
637
+ onCurrentChange: handleCurrentChange,
638
+ children: /* @__PURE__ */ jsxs(Card.Root, {
639
+ fullWidth: true,
640
+ ref: forwardedRef,
641
+ children: [/* @__PURE__ */ jsxs(Card.Header, { children: [/* @__PURE__ */ jsx(Card.Block, {}), /* @__PURE__ */ jsx(Card.Title, { children: result.label })] }), result.snippet && /* @__PURE__ */ jsx(Card.Body, { children: /* @__PURE__ */ jsx(Card.Row, { children: /* @__PURE__ */ jsx(Card.Text, {
642
+ variant: "description",
643
+ children: result.snippet
644
+ }) }) })]
645
+ })
646
+ })
647
+ });
648
+ });
649
+ SearchTile.displayName = "SearchTile";
650
+ //#endregion
651
+ export { Board, BoardDebug, Focus, Mosaic, SearchStack, dropHandler, styles as mosaicStyles, useBoard, useBoardColumn, useContainerDebug, useDefaultColumnEventHandler, useEventHandlerAdapter, useFocus, useMosaicContainerContext as useMosaicContainer, useMosaicTileContext as useMosaicTile, useVirtualizerPagination, useVisibleItems };
652
+
653
+ //# sourceMappingURL=index.mjs.map