@dxos/react-ui-mosaic 0.9.1-staging.ee54ba693a → 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-mosaic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Modular UI components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -19,26 +19,22 @@
|
|
|
19
19
|
".": {
|
|
20
20
|
"source": "./src/index.ts",
|
|
21
21
|
"types": "./dist/types/src/index.d.ts",
|
|
22
|
-
"
|
|
23
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
22
|
+
"import": "./dist/lib/index.mjs"
|
|
24
23
|
},
|
|
25
24
|
"./playwright": {
|
|
26
25
|
"source": "./src/playwright/index.ts",
|
|
27
26
|
"types": "./dist/types/src/playwright/index.d.ts",
|
|
28
|
-
"
|
|
29
|
-
"node": "./dist/lib/node-esm/playwright/index.mjs"
|
|
27
|
+
"import": "./dist/lib/playwright.mjs"
|
|
30
28
|
},
|
|
31
29
|
"./testing": {
|
|
32
30
|
"source": "./src/testing/index.ts",
|
|
33
31
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
34
|
-
"
|
|
35
|
-
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
32
|
+
"import": "./dist/lib/testing.mjs"
|
|
36
33
|
},
|
|
37
34
|
"./translations": {
|
|
38
35
|
"source": "./src/translations.ts",
|
|
39
36
|
"types": "./dist/types/src/translations.d.ts",
|
|
40
|
-
"
|
|
41
|
-
"node": "./dist/lib/node-esm/translations.mjs"
|
|
37
|
+
"import": "./dist/lib/translations.mjs"
|
|
42
38
|
}
|
|
43
39
|
},
|
|
44
40
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -51,8 +47,8 @@
|
|
|
51
47
|
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.2",
|
|
52
48
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "1.1.0",
|
|
53
49
|
"@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "3.2.10",
|
|
54
|
-
"@codemirror/state": "^6.
|
|
55
|
-
"@codemirror/view": "^6.43.
|
|
50
|
+
"@codemirror/state": "^6.7.1",
|
|
51
|
+
"@codemirror/view": "^6.43.6",
|
|
56
52
|
"@fluentui/react-tabster": "9.26.11",
|
|
57
53
|
"@playwright/test": "1.57.0",
|
|
58
54
|
"@radix-ui/react-compose-refs": "1.1.1",
|
|
@@ -62,46 +58,48 @@
|
|
|
62
58
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
63
59
|
"@tanstack/react-virtual": "^3.13.18",
|
|
64
60
|
"bind-event-listener": "^3.0.0",
|
|
65
|
-
"@dxos/async": "0.
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/react-
|
|
72
|
-
"@dxos/react-ui-
|
|
73
|
-
"@dxos/react-ui-
|
|
74
|
-
"@dxos/react-ui-
|
|
75
|
-
"@dxos/react-ui-
|
|
76
|
-
"@dxos/ui-
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/util": "0.
|
|
61
|
+
"@dxos/async": "0.11.0",
|
|
62
|
+
"@dxos/echo": "0.11.0",
|
|
63
|
+
"@dxos/keys": "0.11.0",
|
|
64
|
+
"@dxos/echo-react": "0.11.0",
|
|
65
|
+
"@dxos/invariant": "0.11.0",
|
|
66
|
+
"@dxos/log": "0.11.0",
|
|
67
|
+
"@dxos/react-client": "0.11.0",
|
|
68
|
+
"@dxos/react-ui-dnd": "0.11.0",
|
|
69
|
+
"@dxos/react-ui-menu": "0.11.0",
|
|
70
|
+
"@dxos/react-ui-attention": "0.11.0",
|
|
71
|
+
"@dxos/react-ui-list": "0.11.0",
|
|
72
|
+
"@dxos/react-ui-search": "0.11.0",
|
|
73
|
+
"@dxos/ui-theme": "0.11.0",
|
|
74
|
+
"@dxos/util": "0.11.0",
|
|
75
|
+
"@dxos/react-ui-syntax-highlighter": "0.11.0"
|
|
79
76
|
},
|
|
80
77
|
"devDependencies": {
|
|
81
78
|
"@effect-atom/atom-react": "^0.5.0",
|
|
79
|
+
"@testing-library/react": "^16.3.2",
|
|
82
80
|
"@types/react": "~19.2.17",
|
|
83
81
|
"@types/react-dom": "~19.2.3",
|
|
84
82
|
"effect": "3.21.4",
|
|
85
83
|
"react": "~19.2.7",
|
|
86
84
|
"react-dom": "~19.2.7",
|
|
87
|
-
"vite": "^8.
|
|
88
|
-
"@dxos/
|
|
89
|
-
"@dxos/
|
|
90
|
-
"@dxos/
|
|
91
|
-
"@dxos/
|
|
92
|
-
"@dxos/
|
|
93
|
-
"@dxos/
|
|
94
|
-
"@dxos/
|
|
95
|
-
"@dxos/
|
|
85
|
+
"vite": "^8.1.4",
|
|
86
|
+
"@dxos/react-ui": "0.11.0",
|
|
87
|
+
"@dxos/random": "0.11.0",
|
|
88
|
+
"@dxos/schema": "0.11.0",
|
|
89
|
+
"@dxos/test-utils": "0.11.0",
|
|
90
|
+
"@dxos/react-ui-syntax-highlighter": "0.11.0",
|
|
91
|
+
"@dxos/types": "0.11.0",
|
|
92
|
+
"@dxos/ui-theme": "0.11.0",
|
|
93
|
+
"@dxos/storybook-utils": "0.11.0"
|
|
96
94
|
},
|
|
97
95
|
"peerDependencies": {
|
|
98
96
|
"@effect-atom/atom-react": "^0.5.0",
|
|
99
97
|
"effect": "3.21.4",
|
|
100
98
|
"react": "~19.2.7",
|
|
101
99
|
"react-dom": "~19.2.7",
|
|
102
|
-
"@dxos/ui
|
|
103
|
-
"@dxos/random": "0.
|
|
104
|
-
"@dxos/
|
|
100
|
+
"@dxos/react-ui": "^0.11.0",
|
|
101
|
+
"@dxos/random": "^0.11.0",
|
|
102
|
+
"@dxos/ui-theme": "^0.11.0"
|
|
105
103
|
},
|
|
106
104
|
"publishConfig": {
|
|
107
105
|
"access": "public"
|
|
@@ -11,6 +11,7 @@ import { type Database, Filter, Obj, Ref } from '@dxos/echo';
|
|
|
11
11
|
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { random } from '@dxos/random';
|
|
13
13
|
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
14
|
+
import { Dnd, type DndContainerHandler } from '@dxos/react-ui-dnd';
|
|
14
15
|
import { Loading, withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
15
16
|
import { withRegistry } from '@dxos/storybook-utils';
|
|
16
17
|
import { mx } from '@dxos/ui-theme';
|
|
@@ -20,8 +21,8 @@ import { translations } from '#translations';
|
|
|
20
21
|
import { useEventHandlerAdapter } from '../../hooks';
|
|
21
22
|
import { TestColumn, TestItem } from '../../testing';
|
|
22
23
|
import { Focus } from '../Focus';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
24
|
+
import { Board } from './Board';
|
|
25
|
+
import { type BoardModel } from './BoardContext';
|
|
25
26
|
import { DefaultBoardColumn } from './Column';
|
|
26
27
|
|
|
27
28
|
random.seed(999);
|
|
@@ -57,7 +58,7 @@ type StoryArgs = {
|
|
|
57
58
|
|
|
58
59
|
type TestBoardModelResult = {
|
|
59
60
|
model: BoardModel<TestColumn, TestItem>;
|
|
60
|
-
eventHandler:
|
|
61
|
+
eventHandler: DndContainerHandler<TestColumn>;
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const useTestBoardModel = (): TestBoardModelResult => {
|
|
@@ -172,10 +173,10 @@ const DefaultStory = ({ debug = false, columns: columnsProp = 0 }: StoryArgs) =>
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
return (
|
|
175
|
-
<
|
|
176
|
+
<Dnd.Root>
|
|
176
177
|
<div className={mx('grid md:p-2 overflow-hidden', debug && 'grid-cols-[1fr_20rem] gap-2')}>
|
|
177
178
|
<Board.Root model={model}>
|
|
178
|
-
<Board.Content
|
|
179
|
+
<Board.Content debug={debug} eventHandler={eventHandler} Tile={DefaultBoardColumn} />
|
|
179
180
|
</Board.Root>
|
|
180
181
|
{debug && (
|
|
181
182
|
<Focus.Group classNames='flex flex-col gap-2 overflow-hidden'>
|
|
@@ -183,7 +184,7 @@ const DefaultStory = ({ debug = false, columns: columnsProp = 0 }: StoryArgs) =>
|
|
|
183
184
|
</Focus.Group>
|
|
184
185
|
)}
|
|
185
186
|
</div>
|
|
186
|
-
</
|
|
187
|
+
</Dnd.Root>
|
|
187
188
|
);
|
|
188
189
|
};
|
|
189
190
|
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import { createContext } from '@radix-ui/react-context';
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
7
6
|
import React, {
|
|
8
7
|
type PropsWithChildren,
|
|
9
8
|
type ReactElement,
|
|
@@ -15,59 +14,17 @@ import React, {
|
|
|
15
14
|
|
|
16
15
|
import { ScrollArea, type ThemedClassName } from '@dxos/react-ui';
|
|
17
16
|
import { composable, composableProps } from '@dxos/react-ui';
|
|
17
|
+
import { type DndContainerHandler, useDndRootContext } from '@dxos/react-ui-dnd';
|
|
18
18
|
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
19
19
|
import { mx } from '@dxos/ui-theme';
|
|
20
20
|
|
|
21
21
|
import { useContainerDebug } from '../../hooks';
|
|
22
22
|
import { Focus } from '../Focus';
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
type MosaicEventHandler,
|
|
27
|
-
type MosaicPlaceholderProps,
|
|
28
|
-
type MosaicStackProps,
|
|
29
|
-
mosaicStyles,
|
|
30
|
-
useMosaic,
|
|
31
|
-
} from '../Mosaic';
|
|
32
|
-
import { BoardColumn, type BoardColumnProps, DefaultBoardColumn, useBoardColumn } from './Column';
|
|
23
|
+
import { Mosaic, type MosaicPlaceholderProps, type MosaicStackProps, mosaicStyles } from '../Mosaic';
|
|
24
|
+
import { BoardContextProvider, type BoardContextValue, useBoardContext } from './BoardContext';
|
|
25
|
+
import { BoardColumn, type BoardColumnProps, DefaultBoardColumn } from './Column';
|
|
33
26
|
import { BoardItem, type BoardItemProps } from './Item';
|
|
34
27
|
|
|
35
|
-
//
|
|
36
|
-
// Model
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
export interface BoardModel<TColumn = any, TItem = any> {
|
|
40
|
-
getColumnId: GetId<TColumn>;
|
|
41
|
-
getItemId: GetId<TItem>;
|
|
42
|
-
isColumn: (obj: unknown) => obj is TColumn;
|
|
43
|
-
isItem: (obj: unknown) => obj is TItem;
|
|
44
|
-
columns: Atom.Atom<readonly TColumn[] | TColumn[]>;
|
|
45
|
-
items: (column: TColumn) => Atom.Atom<TItem[]>;
|
|
46
|
-
getColumns: () => TColumn[];
|
|
47
|
-
getItems: (column: TColumn) => TItem[];
|
|
48
|
-
onColumnCreate?: () => Promise<TColumn>;
|
|
49
|
-
onColumnDelete?: (column: TColumn) => void;
|
|
50
|
-
onItemCreate?: (column: TColumn) => Promise<TItem>;
|
|
51
|
-
onItemDelete?: (column: TColumn, item: TItem) => void;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
// Context
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
const BOARD_NAME = 'Board';
|
|
59
|
-
|
|
60
|
-
type BoardContextValue<TColumn = any, TItem = any> = {
|
|
61
|
-
model: BoardModel<TColumn, TItem>;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const [BoardContextProvider, useBoardContext] = createContext<BoardContextValue>(BOARD_NAME);
|
|
65
|
-
|
|
66
|
-
/** Hook to read the board model from context (e.g. in custom column tiles). Pass TColumn and TItem for typed model. */
|
|
67
|
-
function useBoard<TColumn = any, TItem = any>(displayName?: string): BoardContextValue<TColumn, TItem> {
|
|
68
|
-
return useBoardContext(displayName ?? BOARD_NAME) as BoardContextValue<TColumn, TItem>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
28
|
//
|
|
72
29
|
// Root
|
|
73
30
|
//
|
|
@@ -91,14 +48,13 @@ const BoardRoot = BoardRootInner as <TColumn = any, TItem = any>(props: BoardRoo
|
|
|
91
48
|
const BOARD_CONTENT_NAME = 'Board.Content';
|
|
92
49
|
|
|
93
50
|
type BoardContentProps<TColumn = any> = ThemedClassName<{
|
|
94
|
-
id: string;
|
|
95
51
|
debug?: boolean;
|
|
96
|
-
eventHandler?:
|
|
52
|
+
eventHandler?: DndContainerHandler<TColumn>;
|
|
97
53
|
Tile?: MosaicStackProps<TColumn>['Tile'];
|
|
98
54
|
}>;
|
|
99
55
|
|
|
100
56
|
const BoardContentInner = composable<HTMLDivElement, BoardContentProps>(
|
|
101
|
-
({
|
|
57
|
+
({ debug, eventHandler, Tile = DefaultBoardColumn, ...props }, forwardedRef) => {
|
|
102
58
|
const { model } = useBoardContext(BOARD_CONTENT_NAME);
|
|
103
59
|
const [DebugInfo, debugHandler] = useContainerDebug(debug);
|
|
104
60
|
const [viewport, setViewport] = useState<HTMLElement | null>(null);
|
|
@@ -115,6 +71,7 @@ const BoardContentInner = composable<HTMLDivElement, BoardContentProps>(
|
|
|
115
71
|
autoScroll={viewport}
|
|
116
72
|
eventHandler={eventHandler}
|
|
117
73
|
debug={debugHandler}
|
|
74
|
+
placeholderDebug={debug}
|
|
118
75
|
>
|
|
119
76
|
<ScrollArea.Root orientation='horizontal' centered padding>
|
|
120
77
|
<ScrollArea.Viewport classNames='snap-mandatory snap-x md:snap-none' ref={setViewport}>
|
|
@@ -160,7 +117,7 @@ BoardPlaceholder.displayName = BOARD_PLACEHOLDER_NAME;
|
|
|
160
117
|
const BOARD_DEBUG_NAME = 'Board.Debug';
|
|
161
118
|
|
|
162
119
|
export const BoardDebug = forwardRef<HTMLDivElement, ThemedClassName>(({ classNames }, forwardedRef) => {
|
|
163
|
-
const { containers, dragging } =
|
|
120
|
+
const { containers, dragging } = useDndRootContext(BOARD_DEBUG_NAME);
|
|
164
121
|
const counter = useRef(0);
|
|
165
122
|
return (
|
|
166
123
|
<JsonHighlighter
|
|
@@ -186,6 +143,4 @@ export const Board = {
|
|
|
186
143
|
Debug: BoardDebug,
|
|
187
144
|
};
|
|
188
145
|
|
|
189
|
-
export { useBoard, useBoardColumn };
|
|
190
|
-
|
|
191
146
|
export type { BoardColumnProps, BoardContentProps, BoardItemProps, BoardRootProps };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext } from '@radix-ui/react-context';
|
|
6
|
+
|
|
7
|
+
// Kept out of `Column.tsx`: react-refresh only fast-refreshes a module whose exports are all
|
|
8
|
+
// components, so the context and hook exported beside them force a full page reload on every edit.
|
|
9
|
+
|
|
10
|
+
export const BOARD_COLUMN_CONTEXT_NAME = 'Board.Column';
|
|
11
|
+
|
|
12
|
+
export type BoardColumnContextValue<TColumn = unknown> = {
|
|
13
|
+
column: TColumn;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const [BoardColumnProvider, useBoardColumnContext] = createContext<BoardColumnContextValue | null>(
|
|
17
|
+
BOARD_COLUMN_CONTEXT_NAME,
|
|
18
|
+
null,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/** Returns the current column when rendered inside a board column (e.g., in column header or item tile). */
|
|
22
|
+
export function useBoardColumn<TColumn = unknown>(): TColumn | undefined {
|
|
23
|
+
const value = useBoardColumnContext(BOARD_COLUMN_CONTEXT_NAME);
|
|
24
|
+
return value?.column as TColumn | undefined;
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Atom } from '@effect-atom/atom-react';
|
|
6
|
+
import { createContext } from '@radix-ui/react-context';
|
|
7
|
+
|
|
8
|
+
import { type GetId } from '@dxos/react-ui-dnd';
|
|
9
|
+
|
|
10
|
+
// Kept out of `Board.tsx`: react-refresh only fast-refreshes a module whose exports are all
|
|
11
|
+
// components, so the model, context and hook exported beside them force a full page reload on every edit.
|
|
12
|
+
|
|
13
|
+
export interface BoardModel<TColumn = any, TItem = any> {
|
|
14
|
+
getColumnId: GetId<TColumn>;
|
|
15
|
+
getItemId: GetId<TItem>;
|
|
16
|
+
isColumn: (obj: unknown) => obj is TColumn;
|
|
17
|
+
isItem: (obj: unknown) => obj is TItem;
|
|
18
|
+
columns: Atom.Atom<readonly TColumn[] | TColumn[]>;
|
|
19
|
+
items: (column: TColumn) => Atom.Atom<TItem[]>;
|
|
20
|
+
getColumns: () => TColumn[];
|
|
21
|
+
getItems: (column: TColumn) => TItem[];
|
|
22
|
+
onColumnCreate?: () => Promise<TColumn>;
|
|
23
|
+
onColumnDelete?: (column: TColumn) => void;
|
|
24
|
+
onItemCreate?: (column: TColumn) => Promise<TItem>;
|
|
25
|
+
onItemDelete?: (column: TColumn, item: TItem) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//
|
|
29
|
+
// Context
|
|
30
|
+
//
|
|
31
|
+
|
|
32
|
+
export const BOARD_NAME = 'Board';
|
|
33
|
+
|
|
34
|
+
export type BoardContextValue<TColumn = any, TItem = any> = {
|
|
35
|
+
model: BoardModel<TColumn, TItem>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const [BoardContextProvider, useBoardContext] = createContext<BoardContextValue>(BOARD_NAME);
|
|
39
|
+
|
|
40
|
+
/** Hook to read the board model from context (e.g. in custom column tiles). Pass TColumn and TItem for typed model. */
|
|
41
|
+
export function useBoard<TColumn = any, TItem = any>(displayName?: string): BoardContextValue<TColumn, TItem> {
|
|
42
|
+
return useBoardContext(displayName ?? BOARD_NAME) as BoardContextValue<TColumn, TItem>;
|
|
43
|
+
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
-
import { createContext } from '@radix-ui/react-context';
|
|
7
6
|
import React, {
|
|
8
7
|
type PropsWithChildren,
|
|
9
8
|
type ReactElement,
|
|
@@ -14,7 +13,7 @@ import React, {
|
|
|
14
13
|
} from 'react';
|
|
15
14
|
|
|
16
15
|
import { Obj, Ref } from '@dxos/echo';
|
|
17
|
-
import { useObject } from '@dxos/react
|
|
16
|
+
import { useObject } from '@dxos/echo-react';
|
|
18
17
|
import { IconButton, ScrollArea, type ThemedClassName, Toolbar, useTranslation } from '@dxos/react-ui';
|
|
19
18
|
import { composable, composableProps } from '@dxos/react-ui';
|
|
20
19
|
import { Menu, createMenuAction } from '@dxos/react-ui-menu';
|
|
@@ -25,30 +24,10 @@ import { translationKey } from '#translations';
|
|
|
25
24
|
import { useContainerDebug, useEventHandlerAdapter } from '../../hooks';
|
|
26
25
|
import { Focus } from '../Focus';
|
|
27
26
|
import { Mosaic, type MosaicContainerProps, type MosaicStackProps, type MosaicTileProps } from '../Mosaic';
|
|
28
|
-
import {
|
|
27
|
+
import { BoardColumnProvider, useBoardColumn } from './BoardColumnContext';
|
|
28
|
+
import { useBoard } from './BoardContext';
|
|
29
29
|
import { BoardItem } from './Item';
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
// Column context
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
const BOARD_COLUMN_CONTEXT_NAME = 'Board.Column';
|
|
36
|
-
|
|
37
|
-
export type BoardColumnContextValue<TColumn = unknown> = {
|
|
38
|
-
column: TColumn;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const [BoardColumnProvider, useBoardColumnContext] = createContext<BoardColumnContextValue | null>(
|
|
42
|
-
BOARD_COLUMN_CONTEXT_NAME,
|
|
43
|
-
null,
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
/** Returns the current column when rendered inside a board column (e.g., in column header or item tile). */
|
|
47
|
-
export function useBoardColumn<TColumn = unknown>(): TColumn | undefined {
|
|
48
|
-
const value = useBoardColumnContext(BOARD_COLUMN_CONTEXT_NAME);
|
|
49
|
-
return value?.column as TColumn | undefined;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
31
|
type BoardColumnProps<TColumn = any> = Pick<
|
|
53
32
|
MosaicTileProps<TColumn>,
|
|
54
33
|
'classNames' | 'location' | 'data' | 'debug' | 'draggable'
|
|
@@ -87,7 +66,7 @@ const BoardColumnRootInner = composable<HTMLDivElement, BoardColumnRootProps>(
|
|
|
87
66
|
border
|
|
88
67
|
classNames={mx(
|
|
89
68
|
'group/column',
|
|
90
|
-
'h-full w-full md:w-card-default-width snap-center
|
|
69
|
+
'h-full w-full md:w-card-default-width snap-center dx-deck-surface',
|
|
91
70
|
'overflow-hidden',
|
|
92
71
|
classNames,
|
|
93
72
|
)}
|
|
@@ -134,9 +113,16 @@ const BoardColumnHeader = composable<HTMLDivElement, BoardColumnHeaderProps>(
|
|
|
134
113
|
|
|
135
114
|
return (
|
|
136
115
|
<Menu.Root>
|
|
137
|
-
|
|
116
|
+
{/* TODO(burdon): Use Card.Header. */}
|
|
117
|
+
<Toolbar.Root
|
|
118
|
+
{...composableProps(props, { classNames: 'gap-0' })}
|
|
119
|
+
data-testid='board-column-header'
|
|
120
|
+
ref={forwardedRef}
|
|
121
|
+
>
|
|
138
122
|
<Toolbar.DragHandle ref={dragHandleRef} testId='mosaicBoard.columnDragHandle' />
|
|
139
|
-
<Toolbar.Text data-testid='mosaicBoard.columnTitle'>
|
|
123
|
+
<Toolbar.Text classNames='grow px-0' data-testid='mosaicBoard.columnTitle'>
|
|
124
|
+
{label}
|
|
125
|
+
</Toolbar.Text>
|
|
140
126
|
{/* TODO(wittjosiah): Reconcile with Card.Menu. */}
|
|
141
127
|
<Menu.Trigger asChild disabled={!columnMenuItems?.length}>
|
|
142
128
|
<Toolbar.IconButton
|
|
@@ -14,8 +14,8 @@ import { translationKey } from '#translations';
|
|
|
14
14
|
|
|
15
15
|
import { Focus } from '../Focus';
|
|
16
16
|
import { Mosaic, type MosaicTileProps } from '../Mosaic';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { useBoardColumn } from './BoardColumnContext';
|
|
18
|
+
import { useBoard } from './BoardContext';
|
|
19
19
|
|
|
20
20
|
const BOARD_ITEM_NAME = 'Board.Item';
|
|
21
21
|
|
|
@@ -6,7 +6,6 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
|
|
|
6
6
|
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
7
7
|
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
8
8
|
import { useComposedRefs } from '@radix-ui/react-compose-refs';
|
|
9
|
-
import { createContext } from '@radix-ui/react-context';
|
|
10
9
|
import { Primitive } from '@radix-ui/react-primitive';
|
|
11
10
|
import { Slot } from '@radix-ui/react-slot';
|
|
12
11
|
import { bind } from 'bind-event-listener';
|
|
@@ -23,59 +22,34 @@ import React, {
|
|
|
23
22
|
useState,
|
|
24
23
|
} from 'react';
|
|
25
24
|
|
|
26
|
-
import {
|
|
25
|
+
import { composable, composableProps } from '@dxos/react-ui';
|
|
26
|
+
import {
|
|
27
|
+
type DndContainerData,
|
|
28
|
+
type DndLocation,
|
|
29
|
+
type DndTileData,
|
|
30
|
+
getSourceData,
|
|
31
|
+
useDndRootContext,
|
|
32
|
+
} from '@dxos/react-ui-dnd';
|
|
27
33
|
import { isTruthy } from '@dxos/util';
|
|
28
34
|
|
|
29
35
|
import { useFocus } from '../Focus';
|
|
30
|
-
import { type MosaicDraggingState, useMosaicRootContext } from './Root';
|
|
31
36
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
type
|
|
35
|
-
type
|
|
36
|
-
|
|
37
|
-
} from './types';
|
|
37
|
+
MOSAIC_CONTAINER_NAME,
|
|
38
|
+
MosaicContainerContextProvider,
|
|
39
|
+
type MosaicContainerContextValue,
|
|
40
|
+
type MosaicContainerState,
|
|
41
|
+
} from './MosaicContainerContext';
|
|
38
42
|
|
|
39
43
|
//
|
|
40
44
|
// Container
|
|
41
45
|
//
|
|
42
46
|
|
|
43
|
-
const MOSAIC_CONTAINER_NAME = 'Mosaic.Container';
|
|
44
|
-
|
|
45
|
-
type MosaicContainerState = { type: 'idle' } | { type: 'active'; bounds?: DOMRect };
|
|
46
|
-
|
|
47
|
-
type MosaicContainerContextValue<TData = any, Location = LocationType> = {
|
|
48
|
-
id: string;
|
|
49
|
-
eventHandler: MosaicEventHandler<TData>;
|
|
50
|
-
orientation?: AllowedAxis;
|
|
51
|
-
dragging?: MosaicDraggingState;
|
|
52
|
-
scrolling?: boolean;
|
|
53
|
-
state: MosaicContainerState;
|
|
54
|
-
|
|
55
|
-
/** Active drop location. */
|
|
56
|
-
activeLocation?: Location;
|
|
57
|
-
setActiveLocation: (location: Location | undefined) => void;
|
|
58
|
-
|
|
59
|
-
/** ID of the current (aria-current) item. */
|
|
60
|
-
currentId?: string;
|
|
61
|
-
/** Set the current item by ID. */
|
|
62
|
-
setCurrentId: (id: string | undefined) => void;
|
|
63
|
-
|
|
64
|
-
/** IDs of selected (aria-selected) items. */
|
|
65
|
-
selectedIds?: ReadonlySet<string>;
|
|
66
|
-
/** Request to set or unset selection on an item by ID. */
|
|
67
|
-
setSelected: (id: string, selected: boolean) => void;
|
|
68
|
-
|
|
69
|
-
/** Register a scroll-to-item callback (provided by Stack/VirtualStack). */
|
|
70
|
-
registerScrollTo: (fn: ((id: string) => void) | undefined) => void;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const [MosaicContainerContextProvider, useMosaicContainerContext] =
|
|
74
|
-
createContext<MosaicContainerContextValue>('MosaicContainer');
|
|
75
|
-
|
|
76
47
|
// State attribute: [&:has(>_[data-mosaic-container-state=active])]
|
|
77
48
|
const MOSAIC_CONTAINER_STATE_ATTR = 'mosaic-container-state';
|
|
78
49
|
|
|
50
|
+
// Debug flag on the container's `group` element; Placeholder reads it via `group-data-[mosaic-debug=true]` (see styles.ts).
|
|
51
|
+
const MOSAIC_CONTAINER_DEBUG_ATTR = 'mosaic-debug';
|
|
52
|
+
|
|
79
53
|
// CSS variables: [var(--mosaic-placeholder-xxx)]
|
|
80
54
|
const MOSAIC_CONTAINER_PLACEHOLDER_WIDTH = '--mosaic-placeholder-width';
|
|
81
55
|
const MOSAIC_CONTAINER_PLACEHOLDER_HEIGHT = '--mosaic-placeholder-height';
|
|
@@ -104,6 +78,8 @@ type MosaicContainerProps = PropsWithChildren<
|
|
|
104
78
|
/** Called when a tile requests to toggle selection. */
|
|
105
79
|
onSelectionChange?: (id: string, selected: boolean) => void;
|
|
106
80
|
debug?: () => ReactNode;
|
|
81
|
+
/** Toggles the `group` target that Placeholder's debug-highlight selectors read (see styles.ts). */
|
|
82
|
+
placeholderDebug?: boolean;
|
|
107
83
|
}
|
|
108
84
|
>;
|
|
109
85
|
|
|
@@ -126,6 +102,7 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
126
102
|
selectedIds,
|
|
127
103
|
onSelectionChange,
|
|
128
104
|
debug,
|
|
105
|
+
placeholderDebug,
|
|
129
106
|
...props
|
|
130
107
|
},
|
|
131
108
|
forwardedRef,
|
|
@@ -144,9 +121,9 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
144
121
|
);
|
|
145
122
|
|
|
146
123
|
// State.
|
|
147
|
-
const { dragging } =
|
|
124
|
+
const { dragging } = useDndRootContext(MOSAIC_CONTAINER_NAME);
|
|
148
125
|
const [state, setState] = useState<MosaicContainerState>({ type: 'idle' });
|
|
149
|
-
const [activeLocation, setActiveLocation] = useState<
|
|
126
|
+
const [activeLocation, setActiveLocation] = useState<DndLocation | undefined>();
|
|
150
127
|
const [scrolling, setScrolling] = useState(false);
|
|
151
128
|
const setCurrentId = useCallback((id: string | undefined) => onCurrentChange?.(id), [onCurrentChange]);
|
|
152
129
|
const setSelected = useCallback(
|
|
@@ -179,18 +156,18 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
179
156
|
}, [setFocus, withFocus, state]);
|
|
180
157
|
|
|
181
158
|
// Register handler.
|
|
182
|
-
const { addContainer, removeContainer } =
|
|
159
|
+
const { addContainer, removeContainer } = useDndRootContext(eventHandler.id);
|
|
183
160
|
useEffect(() => {
|
|
184
161
|
addContainer(eventHandler);
|
|
185
162
|
return () => removeContainer(eventHandler.id);
|
|
186
163
|
}, [eventHandler]);
|
|
187
164
|
|
|
188
|
-
const data = useMemo<
|
|
165
|
+
const data = useMemo<DndContainerData>(
|
|
189
166
|
() =>
|
|
190
167
|
({
|
|
191
168
|
type: 'container',
|
|
192
169
|
id: eventHandler.id,
|
|
193
|
-
}) satisfies
|
|
170
|
+
}) satisfies DndContainerData,
|
|
194
171
|
[eventHandler.id],
|
|
195
172
|
);
|
|
196
173
|
|
|
@@ -242,14 +219,14 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
242
219
|
* Dragging started in this container.
|
|
243
220
|
*/
|
|
244
221
|
onDragStart: ({ source }) => {
|
|
245
|
-
const sourceData = source.data as
|
|
222
|
+
const sourceData = source.data as DndTileData;
|
|
246
223
|
setState({ type: 'active', bounds: sourceData.bounds });
|
|
247
224
|
},
|
|
248
225
|
/**
|
|
249
226
|
* Dragging entered this container.
|
|
250
227
|
*/
|
|
251
228
|
onDragEnter: ({ source }) => {
|
|
252
|
-
const sourceData = source.data as
|
|
229
|
+
const sourceData = source.data as DndTileData;
|
|
253
230
|
setState({ type: 'active', bounds: sourceData.bounds });
|
|
254
231
|
},
|
|
255
232
|
/**
|
|
@@ -258,7 +235,7 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
258
235
|
* triggering `onDragLeave`, which then causes the item to be added and removed continually (flickering).
|
|
259
236
|
*/
|
|
260
237
|
onDragLeave: ({ source }) => {
|
|
261
|
-
const sourceData = source.data as
|
|
238
|
+
const sourceData = source.data as DndTileData;
|
|
262
239
|
if (sourceData.containerId !== eventHandler.id) {
|
|
263
240
|
setState({ type: 'idle' });
|
|
264
241
|
}
|
|
@@ -317,7 +294,7 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
317
294
|
>
|
|
318
295
|
<Comp
|
|
319
296
|
{...composableProps(props, {
|
|
320
|
-
classNames: 'h-full',
|
|
297
|
+
classNames: 'h-full group',
|
|
321
298
|
style: {
|
|
322
299
|
[MOSAIC_CONTAINER_PLACEHOLDER_WIDTH]:
|
|
323
300
|
state.type === 'active' && state.bounds ? `${state.bounds.width}px` : '0px',
|
|
@@ -327,6 +304,7 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
327
304
|
})}
|
|
328
305
|
{...{
|
|
329
306
|
[`data-${MOSAIC_CONTAINER_STATE_ATTR}`]: state.type,
|
|
307
|
+
[`data-${MOSAIC_CONTAINER_DEBUG_ATTR}`]: placeholderDebug,
|
|
330
308
|
}}
|
|
331
309
|
ref={composedRef}
|
|
332
310
|
>
|
|
@@ -340,6 +318,6 @@ const MosaicContainer = composable<HTMLDivElement, MosaicContainerProps>(
|
|
|
340
318
|
|
|
341
319
|
MosaicContainer.displayName = MOSAIC_CONTAINER_NAME;
|
|
342
320
|
|
|
343
|
-
export { MosaicContainer
|
|
321
|
+
export { MosaicContainer };
|
|
344
322
|
|
|
345
|
-
export type { MosaicContainerProps
|
|
323
|
+
export type { MosaicContainerProps };
|
|
@@ -7,7 +7,7 @@ import React, { type ReactNode, forwardRef } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { Button, IconButton, type IconButtonProps, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
|
-
import { useMosaicTileContext } from './
|
|
10
|
+
import { useMosaicTileContext } from './MosaicTileContext';
|
|
11
11
|
|
|
12
12
|
const MOSAIC_DRAG_HANDLE_NAME = 'Mosaic.DragHandle';
|
|
13
13
|
|