@dxos/plugin-board 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.
Files changed (65) hide show
  1. package/dist/lib/BoardPlugin.mjs +21 -0
  2. package/dist/lib/BoardPlugin.mjs.map +1 -0
  3. package/dist/lib/capabilities.mjs +8 -0
  4. package/dist/lib/capabilities.mjs.map +1 -0
  5. package/dist/lib/chunk-BoardArticle.mjs +190 -0
  6. package/dist/lib/chunk-BoardArticle.mjs.map +1 -0
  7. package/dist/lib/chunk-create-object.mjs +24 -0
  8. package/dist/lib/chunk-create-object.mjs.map +1 -0
  9. package/dist/lib/{neutral/chunk-TIEIAVKG.mjs → chunk-meta.mjs} +20 -27
  10. package/dist/lib/chunk-meta.mjs.map +1 -0
  11. package/dist/lib/chunk-react-surface.mjs +21 -0
  12. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  13. package/dist/lib/chunk-types.mjs +41 -0
  14. package/dist/lib/chunk-types.mjs.map +1 -0
  15. package/dist/lib/components.mjs +0 -0
  16. package/dist/lib/containers.mjs +7 -0
  17. package/dist/lib/containers.mjs.map +1 -0
  18. package/dist/lib/index.mjs +3 -0
  19. package/dist/lib/meta.mjs +2 -0
  20. package/dist/lib/plugin.mjs +8 -0
  21. package/dist/lib/plugin.mjs.map +1 -0
  22. package/dist/lib/translations.mjs +22 -0
  23. package/dist/lib/translations.mjs.map +1 -0
  24. package/dist/lib/types.mjs +2 -0
  25. package/dist/types/src/capabilities/create-object.d.ts +1 -1
  26. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  27. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  28. package/dist/types/src/containers/BoardArticle/BoardArticle.d.ts +4 -1
  29. package/dist/types/src/containers/BoardArticle/BoardArticle.d.ts.map +1 -1
  30. package/dist/types/src/containers/BoardArticle/BoardArticle.stories.d.ts.map +1 -1
  31. package/dist/types/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +39 -39
  33. package/src/capabilities/{react-surface.tsx → react-surface.ts} +2 -4
  34. package/src/containers/BoardArticle/BoardArticle.stories.tsx +2 -1
  35. package/src/containers/BoardArticle/BoardArticle.tsx +116 -78
  36. package/dist/lib/neutral/BoardArticle-S7VMVBPU.mjs +0 -167
  37. package/dist/lib/neutral/BoardArticle-S7VMVBPU.mjs.map +0 -7
  38. package/dist/lib/neutral/BoardPlugin.mjs +0 -42
  39. package/dist/lib/neutral/BoardPlugin.mjs.map +0 -7
  40. package/dist/lib/neutral/capabilities/index.mjs +0 -11
  41. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  42. package/dist/lib/neutral/chunk-I4YKWAK6.mjs +0 -35
  43. package/dist/lib/neutral/chunk-I4YKWAK6.mjs.map +0 -7
  44. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  45. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  46. package/dist/lib/neutral/chunk-TIEIAVKG.mjs.map +0 -7
  47. package/dist/lib/neutral/components/index.mjs +0 -1
  48. package/dist/lib/neutral/components/index.mjs.map +0 -7
  49. package/dist/lib/neutral/containers/index.mjs +0 -9
  50. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  51. package/dist/lib/neutral/create-object-VMKGCE3J.mjs +0 -27
  52. package/dist/lib/neutral/create-object-VMKGCE3J.mjs.map +0 -7
  53. package/dist/lib/neutral/index.mjs +0 -12
  54. package/dist/lib/neutral/index.mjs.map +0 -7
  55. package/dist/lib/neutral/meta.json +0 -1
  56. package/dist/lib/neutral/meta.mjs +0 -8
  57. package/dist/lib/neutral/meta.mjs.map +0 -7
  58. package/dist/lib/neutral/plugin.mjs +0 -12
  59. package/dist/lib/neutral/plugin.mjs.map +0 -7
  60. package/dist/lib/neutral/react-surface-M5JHK3ZQ.mjs +0 -26
  61. package/dist/lib/neutral/react-surface-M5JHK3ZQ.mjs.map +0 -7
  62. package/dist/lib/neutral/translations.mjs +0 -30
  63. package/dist/lib/neutral/translations.mjs.map +0 -7
  64. package/dist/lib/neutral/types/index.mjs +0 -8
  65. package/dist/lib/neutral/types/index.mjs.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-board",
3
- "version": "0.9.1-staging.ee54ba693a",
3
+ "version": "0.11.0",
4
4
  "description": "Surface plugin for card baords",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,55 +16,55 @@
16
16
  "#capabilities": {
17
17
  "source": "./src/capabilities/index.ts",
18
18
  "types": "./dist/types/src/capabilities/index.d.ts",
19
- "default": "./dist/lib/neutral/capabilities/index.mjs"
19
+ "default": "./dist/lib/capabilities.mjs"
20
20
  },
21
21
  "#components": {
22
22
  "source": "./src/components/index.ts",
23
23
  "types": "./dist/types/src/components/index.d.ts",
24
- "default": "./dist/lib/neutral/components/index.mjs"
24
+ "default": "./dist/lib/components.mjs"
25
25
  },
26
26
  "#containers": {
27
27
  "source": "./src/containers/index.ts",
28
28
  "types": "./dist/types/src/containers/index.d.ts",
29
- "default": "./dist/lib/neutral/containers/index.mjs"
29
+ "default": "./dist/lib/containers.mjs"
30
30
  },
31
31
  "#meta": {
32
32
  "source": "./src/meta.ts",
33
33
  "types": "./dist/types/src/meta.d.ts",
34
- "default": "./dist/lib/neutral/meta.mjs"
34
+ "default": "./dist/lib/meta.mjs"
35
35
  },
36
36
  "#plugin": {
37
37
  "source": "./src/BoardPlugin.tsx",
38
38
  "types": "./dist/types/src/BoardPlugin.d.ts",
39
- "default": "./dist/lib/neutral/BoardPlugin.mjs"
39
+ "default": "./dist/lib/BoardPlugin.mjs"
40
40
  },
41
41
  "#translations": {
42
42
  "source": "./src/translations.ts",
43
43
  "types": "./dist/types/src/translations.d.ts",
44
- "default": "./dist/lib/neutral/translations.mjs"
44
+ "default": "./dist/lib/translations.mjs"
45
45
  },
46
46
  "#types": {
47
47
  "source": "./src/types/index.ts",
48
48
  "types": "./dist/types/src/types/index.d.ts",
49
- "default": "./dist/lib/neutral/types/index.mjs"
49
+ "default": "./dist/lib/types.mjs"
50
50
  }
51
51
  },
52
52
  "exports": {
53
53
  ".": {
54
54
  "source": "./src/index.ts",
55
55
  "types": "./dist/types/src/index.d.ts",
56
- "default": "./dist/lib/neutral/index.mjs"
56
+ "import": "./dist/lib/index.mjs"
57
57
  },
58
58
  "./assets/PLUGIN.mdl": "./PLUGIN.mdl",
59
59
  "./plugin": {
60
60
  "source": "./src/plugin.ts",
61
61
  "types": "./dist/types/src/plugin.d.ts",
62
- "default": "./dist/lib/neutral/plugin.mjs"
62
+ "import": "./dist/lib/plugin.mjs"
63
63
  },
64
64
  "./translations": {
65
65
  "source": "./src/translations.ts",
66
66
  "types": "./dist/types/src/translations.d.ts",
67
- "default": "./dist/lib/neutral/translations.mjs"
67
+ "import": "./dist/lib/translations.mjs"
68
68
  }
69
69
  },
70
70
  "types": "dist/types/src/index.d.ts",
@@ -78,45 +78,45 @@
78
78
  "@effect-atom/atom": "^0.5.3",
79
79
  "@effect-atom/atom-react": "^0.5.0",
80
80
  "effect": "3.21.4",
81
- "@dxos/compute": "0.9.1-staging.ee54ba693a",
82
- "@dxos/app-toolkit": "0.9.1-staging.ee54ba693a",
83
- "@dxos/invariant": "0.9.1-staging.ee54ba693a",
84
- "@dxos/keys": "0.9.1-staging.ee54ba693a",
85
- "@dxos/echo": "0.9.1-staging.ee54ba693a",
86
- "@dxos/app-framework": "0.9.1-staging.ee54ba693a",
87
- "@dxos/echo-react": "0.9.1-staging.ee54ba693a",
88
- "@dxos/plugin-markdown": "0.9.1-staging.ee54ba693a",
89
- "@dxos/plugin-client": "0.9.1-staging.ee54ba693a",
90
- "@dxos/plugin-space": "0.9.1-staging.ee54ba693a",
91
- "@dxos/react-client": "0.9.1-staging.ee54ba693a",
92
- "@dxos/random": "0.9.1-staging.ee54ba693a",
93
- "@dxos/react-ui": "0.9.1-staging.ee54ba693a",
94
- "@dxos/react-ui-board": "0.9.1-staging.ee54ba693a",
95
- "@dxos/react-ui-attention": "0.9.1-staging.ee54ba693a",
96
- "@dxos/react-ui-form": "0.9.1-staging.ee54ba693a",
97
- "@dxos/util": "0.9.1-staging.ee54ba693a",
98
- "@dxos/types": "0.9.1-staging.ee54ba693a"
81
+ "@dxos/app-framework": "0.11.0",
82
+ "@dxos/app-toolkit": "0.11.0",
83
+ "@dxos/compute": "0.11.0",
84
+ "@dxos/echo": "0.11.0",
85
+ "@dxos/echo-react": "0.11.0",
86
+ "@dxos/invariant": "0.11.0",
87
+ "@dxos/plugin-client": "0.11.0",
88
+ "@dxos/plugin-markdown": "0.11.0",
89
+ "@dxos/keys": "0.11.0",
90
+ "@dxos/plugin-space": "0.11.0",
91
+ "@dxos/random": "0.11.0",
92
+ "@dxos/react-client": "0.11.0",
93
+ "@dxos/react-ui": "0.11.0",
94
+ "@dxos/react-ui-attention": "0.11.0",
95
+ "@dxos/react-ui-board": "0.11.0",
96
+ "@dxos/react-ui-form": "0.11.0",
97
+ "@dxos/types": "0.11.0",
98
+ "@dxos/util": "0.11.0"
99
99
  },
100
100
  "devDependencies": {
101
101
  "@types/react": "~19.2.17",
102
102
  "@types/react-dom": "~19.2.3",
103
103
  "react": "~19.2.7",
104
104
  "react-dom": "~19.2.7",
105
- "vite": "^8.0.16",
106
- "@dxos/plugin-testing": "0.9.1-staging.ee54ba693a",
107
- "@dxos/plugin-preview": "0.9.1-staging.ee54ba693a",
108
- "@dxos/plugin-theme": "0.9.1-staging.ee54ba693a",
109
- "@dxos/react-ui-syntax-highlighter": "0.9.1-staging.ee54ba693a",
110
- "@dxos/storybook-utils": "0.9.1-staging.ee54ba693a",
111
- "@dxos/ui-theme": "0.9.1-staging.ee54ba693a",
112
- "@dxos/test-utils": "0.9.1-staging.ee54ba693a"
105
+ "vite": "^8.1.4",
106
+ "@dxos/plugin-preview": "0.11.0",
107
+ "@dxos/plugin-testing": "0.11.0",
108
+ "@dxos/plugin-theme": "0.11.0",
109
+ "@dxos/react-ui-syntax-highlighter": "0.11.0",
110
+ "@dxos/storybook-utils": "0.11.0",
111
+ "@dxos/test-utils": "0.11.0",
112
+ "@dxos/ui-theme": "0.11.0"
113
113
  },
114
114
  "peerDependencies": {
115
115
  "effect": "3.21.4",
116
116
  "react": "~19.2.7",
117
117
  "react-dom": "~19.2.7",
118
- "@dxos/react-ui": "0.9.1-staging.ee54ba693a",
119
- "@dxos/ui-theme": "0.9.1-staging.ee54ba693a"
118
+ "@dxos/react-ui": "^0.11.0",
119
+ "@dxos/ui-theme": "^0.11.0"
120
120
  },
121
121
  "publishConfig": {
122
122
  "access": "public"
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  import * as Effect from 'effect/Effect';
6
- import React from 'react';
7
6
 
8
7
  import { Capabilities, Capability } from '@dxos/app-framework';
9
8
  import { Surface } from '@dxos/app-framework/ui';
@@ -22,9 +21,8 @@ export default Capability.makeModule(() =>
22
21
  AppSurface.object(AppSurface.Article, Board.Board),
23
22
  AppSurface.object(AppSurface.Section, Board.Board),
24
23
  ),
25
- component: ({ role, data }) => (
26
- <BoardArticle role={role} subject={data.subject} attendableId={data.attendableId} />
27
- ),
24
+ component: BoardArticle,
25
+ props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),
28
26
  }),
29
27
  ]),
30
28
  ),
@@ -8,12 +8,13 @@ import React, { useEffect, useState } from 'react';
8
8
 
9
9
  import { withPluginManager } from '@dxos/app-framework/testing';
10
10
  import { Filter, Obj, Ref } from '@dxos/echo';
11
+ import { useQuery } from '@dxos/echo-react';
11
12
  import { ClientPlugin } from '@dxos/plugin-client/testing';
12
13
  import { initializeIdentity } from '@dxos/plugin-client/testing';
13
14
  import { PreviewPlugin } from '@dxos/plugin-preview/testing';
14
15
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
15
16
  import { random } from '@dxos/random';
16
- import { useQuery, useSpaces } from '@dxos/react-client/echo';
17
+ import { useSpaces } from '@dxos/react-client/echo';
17
18
  import { withLayout } from '@dxos/react-ui/testing';
18
19
  import { Organization, Person } from '@dxos/types';
19
20
 
@@ -9,28 +9,41 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
9
9
  import { Surface } from '@dxos/app-framework/ui';
10
10
  import { AppSurface } from '@dxos/app-toolkit/ui';
11
11
  import { Filter, Obj, Ref } from '@dxos/echo';
12
- import { useObject } from '@dxos/echo-react';
12
+ import { useObject, useQuery } from '@dxos/echo-react';
13
13
  import { invariant } from '@dxos/invariant';
14
14
  import { EID } from '@dxos/keys';
15
15
  import { Markdown } from '@dxos/plugin-markdown';
16
- import { useQuery } from '@dxos/react-client/echo';
17
- import { Panel } from '@dxos/react-ui';
16
+ import { Panel, Toolbar, useTranslation } from '@dxos/react-ui';
18
17
  import { useAttention } from '@dxos/react-ui-attention';
19
- import { Board, type BoardController, type BoardRootProps, type Position } from '@dxos/react-ui-board';
18
+ import { Board, type BoardController, type BoardRootProps, type Layout, resizeToFit } from '@dxos/react-ui-board';
19
+ import { translationKey } from '@dxos/react-ui-board/translations';
20
20
  import { ObjectPicker, type ObjectPickerContentProps } from '@dxos/react-ui-form';
21
21
  import { isNonNullable } from '@dxos/util';
22
22
 
23
23
  import { type Board as BoardType } from '#types';
24
24
 
25
- const DEFAULT_POSITION = { x: 0, y: 0 } satisfies Position;
26
-
27
- type PickerState = {
28
- position: Position;
25
+ type Position = { x: number; y: number };
26
+
27
+ const DEFAULT_POSITION: Position = { x: 0, y: 0 };
28
+
29
+ // Legacy boards stored cells with a centre origin (signed coords); the engine is 0-based, so shift all
30
+ // cells to non-negative before handing them to the component. Freshly-created boards are already 0-based.
31
+ const normalizeCells = (cells: BoardType.Board['layout']['cells']): Layout['items'] => {
32
+ const values = Object.values(cells);
33
+ const minX = Math.min(0, ...values.map((cell) => cell.x));
34
+ const minY = Math.min(0, ...values.map((cell) => cell.y));
35
+ if (minX === 0 && minY === 0) {
36
+ return cells;
37
+ }
38
+ return Object.fromEntries(
39
+ Object.entries(cells).map(([id, cell]) => [id, { ...cell, x: cell.x - minX, y: cell.y - minY }]),
40
+ );
29
41
  };
30
42
 
31
43
  export type BoardArticleProps = AppSurface.ObjectArticleProps<BoardType.Board>;
32
44
 
33
45
  export const BoardArticle = ({ role, subject: board, attendableId }: BoardArticleProps) => {
46
+ const { t } = useTranslation(translationKey);
34
47
  const { hasAttention } = useAttention(attendableId);
35
48
  const db = Obj.getDatabase(board);
36
49
  const [boardItems] = useObject(board, 'items');
@@ -52,7 +65,14 @@ export const BoardArticle = ({ role, subject: board, attendableId }: BoardArticl
52
65
 
53
66
  const controller = useRef<BoardController>(null);
54
67
  const addTriggerRef = useRef<HTMLButtonElement | null>(null);
55
- const [pickerState, setPickerState] = useState<PickerState | null>(null);
68
+ const [pickerState, setPickerState] = useState<{ position: Position } | null>(null);
69
+ const [zoom, setZoom] = useState(1);
70
+
71
+ const layout = useMemo<Layout>(() => ({ items: normalizeCells(board.layout.cells) }), [board.layout.cells]);
72
+ const bounds = useMemo(
73
+ () => ({ columns: board.layout.size.width, rows: board.layout.size.height }),
74
+ [board.layout.size.width, board.layout.size.height],
75
+ );
56
76
 
57
77
  // TODO(burdon): Use search.
58
78
  const objects = useQuery(db, Filter.everything());
@@ -62,36 +82,32 @@ export const BoardArticle = ({ role, subject: board, attendableId }: BoardArticl
62
82
  .filter((obj) => obj.id !== board.id)
63
83
  .map((obj) => {
64
84
  const label = Obj.getLabel(obj);
65
- if (label) {
66
- return {
67
- id: obj.id,
68
- label,
69
- hue: 'neutral' as const,
70
- };
71
- }
85
+ return label ? { id: obj.id, label, hue: 'neutral' as const } : undefined;
72
86
  })
73
87
  .filter(isNonNullable)
74
88
  .sort(({ label: a }, { label: b }) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())),
75
- [objects],
89
+ [objects, board.id],
76
90
  );
77
91
 
92
+ const handleChange = useCallback<NonNullable<BoardRootProps['onChange']>>(
93
+ (next) => {
94
+ Obj.update(board, (board) => {
95
+ board.layout.cells = next.items;
96
+ });
97
+ },
98
+ [board],
99
+ );
100
+
101
+ // Backdrop "+" supplies a position → create a new Markdown document directly.
78
102
  const handleAdd = useCallback<NonNullable<BoardRootProps['onAdd']>>(
79
- async (anchor, position) => {
103
+ (position) => {
80
104
  const db = Obj.getDatabase(board);
81
105
  invariant(db);
82
- // Grid backdrop "+" supplies a position → create a new Markdown document directly.
83
- // Toolbar "+" omits position → fall back to the picker over existing objects.
84
- if (position) {
85
- const doc = db.add(Markdown.make());
86
- Obj.update(board, (board) => {
87
- board.items.push(Ref.make(doc));
88
- board.layout.cells[doc.id.toString()] = position;
89
- });
90
- return;
91
- }
92
-
93
- addTriggerRef.current = anchor;
94
- setPickerState({ position: DEFAULT_POSITION });
106
+ const doc = db.add(Markdown.make());
107
+ Obj.update(board, (board) => {
108
+ board.items.push(Ref.make(doc));
109
+ board.layout.cells[doc.id.toString()] = position;
110
+ });
95
111
  },
96
112
  [board],
97
113
  );
@@ -114,76 +130,98 @@ export const BoardArticle = ({ role, subject: board, attendableId }: BoardArticl
114
130
  [board],
115
131
  );
116
132
 
117
- const handleMove = useCallback<NonNullable<BoardRootProps['onMove']>>(
118
- (id, position) => {
119
- const layout = board.layout.cells[id];
120
- Obj.update(board, (board) => {
121
- board.layout.cells[id] = { ...layout, ...position };
122
- });
123
- },
124
- [board],
125
- );
126
-
133
+ // Toolbar "+" adds an existing object via the picker.
127
134
  const handleSelect = useCallback<NonNullable<ObjectPickerContentProps['onSelect']>>(
128
135
  (id) => {
129
- if (!pickerState) {
136
+ const position = pickerState?.position ?? DEFAULT_POSITION;
137
+ const selected = objects.find((obj) => obj.id === id);
138
+ if (!Obj.isObject(selected)) {
130
139
  return;
131
140
  }
132
-
133
- // Find the selected object by id from the space.
134
- const selectedObject = objects.find((obj) => obj.id === id);
135
- if (!Obj.isObject(selectedObject)) {
136
- return;
137
- }
138
-
139
- // Create a reference to the selected object and add it to the board.
140
141
  Obj.update(board, (board) => {
141
- board.items.push(Ref.make(selectedObject));
142
-
143
- // Set the layout position for the new item.
144
- board.layout.cells[selectedObject.id.toString()] = pickerState.position;
142
+ board.items.push(Ref.make(selected));
143
+ board.layout.cells[selected.id.toString()] = position;
145
144
  });
146
-
147
- // Close the picker.
148
145
  setPickerState(null);
149
146
  },
150
147
  [pickerState, objects, board],
151
148
  );
152
149
 
153
150
  return (
154
- <Board.Root ref={controller} layout={board.layout} onAdd={handleAdd} onDelete={handleDelete} onMove={handleMove}>
155
- <ObjectPicker.Root
156
- open={!!pickerState}
157
- onOpenChange={(nextOpen: boolean) => {
158
- setPickerState(nextOpen ? { position: DEFAULT_POSITION } : null);
159
- }}
151
+ <ObjectPicker.Root
152
+ open={!!pickerState}
153
+ onOpenChange={(next: boolean) => setPickerState(next ? { position: DEFAULT_POSITION } : null)}
154
+ >
155
+ <Board.Root
156
+ ref={controller}
157
+ layout={layout}
158
+ bounds={bounds}
159
+ mode='float'
160
+ resolver={resizeToFit}
161
+ zoom={zoom}
162
+ onChange={handleChange}
163
+ onAdd={handleAdd}
164
+ onDelete={handleDelete}
160
165
  >
161
166
  <Panel.Root role={role}>
167
+ {/* TODO(burdon): Migrate to Menu.Root + useMenuActions (threading attendableId). */}
162
168
  <Panel.Toolbar asChild>
163
- <Board.Toolbar disabled={!hasAttention} />
169
+ <Toolbar.Root>
170
+ <Toolbar.IconButton
171
+ icon='ph--crosshair--regular'
172
+ iconOnly
173
+ label={t('move-to-center.button')}
174
+ disabled={!hasAttention}
175
+ onClick={() => controller.current?.center()}
176
+ />
177
+ <Toolbar.IconButton
178
+ icon={zoom < 1 ? 'ph--arrows-in--regular' : 'ph--arrows-out--regular'}
179
+ iconOnly
180
+ label={t('toggle-zoom.button')}
181
+ disabled={!hasAttention}
182
+ onClick={() => setZoom((value) => (value < 1 ? 1 : 0.5))}
183
+ />
184
+ <Toolbar.IconButton
185
+ icon='ph--plus--regular'
186
+ iconOnly
187
+ label={t('add-object.button')}
188
+ disabled={!hasAttention}
189
+ onClick={(event) => {
190
+ addTriggerRef.current = event.currentTarget as HTMLButtonElement;
191
+ setPickerState({ position: DEFAULT_POSITION });
192
+ }}
193
+ />
194
+ </Toolbar.Root>
164
195
  </Panel.Toolbar>
165
196
  <Panel.Content asChild>
166
- <Board.Container>
167
- <Board.Viewport classNames='border-none'>
197
+ <Board.Container classNames='absolute inset-0'>
198
+ <Board.Viewport>
168
199
  <Board.Backdrop />
169
200
  <Board.Content>
170
- {items?.map((item, index) => (
171
- <Board.Cell item={item} key={index} layout={board.layout?.cells[item.id] ?? { x: 0, y: 0 }}>
172
- <Surface.Surface
173
- type={AppSurface.CardContent}
174
- data={{ subject: item, editable: true }}
175
- limit={1}
176
- />
177
- </Board.Cell>
178
- ))}
201
+ {items?.map((item) => {
202
+ const itemLayout = layout.items[item.id];
203
+ return itemLayout ? (
204
+ <Board.Cell item={item} key={item.id} layout={itemLayout}>
205
+ <Surface.Surface
206
+ type={AppSurface.CardContent}
207
+ data={{ subject: item, editable: true }}
208
+ limit={1}
209
+ />
210
+ </Board.Cell>
211
+ ) : null;
212
+ })}
179
213
  </Board.Content>
180
214
  </Board.Viewport>
215
+ {/* Overview map (outlines the visible region), pinned to the corner over the board. */}
216
+ <Board.Map classNames='absolute bottom-2 right-2 z-10 w-40' />
181
217
  </Board.Container>
182
218
  </Panel.Content>
183
219
  </Panel.Root>
184
- <ObjectPicker.Content options={options} onSelect={handleSelect} classNames='dx-card-popover-width' />
185
- <ObjectPicker.VirtualTrigger virtualRef={addTriggerRef} />
186
- </ObjectPicker.Root>
187
- </Board.Root>
220
+ </Board.Root>
221
+ <ObjectPicker.Content options={options} onSelect={handleSelect} classNames='dx-card-popover-width' />
222
+ <ObjectPicker.VirtualTrigger virtualRef={addTriggerRef} />
223
+ </ObjectPicker.Root>
188
224
  );
189
225
  };
226
+
227
+ BoardArticle.displayName = 'BoardArticle';
@@ -1,167 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/BoardArticle/BoardArticle.tsx
4
- import { useAtomValue } from "@effect-atom/atom-react";
5
- import * as Atom from "@effect-atom/atom/Atom";
6
- import React, { useCallback, useMemo, useRef, useState } from "react";
7
- import { Surface } from "@dxos/app-framework/ui";
8
- import { AppSurface } from "@dxos/app-toolkit/ui";
9
- import { Filter, Obj, Ref } from "@dxos/echo";
10
- import { useObject } from "@dxos/echo-react";
11
- import { invariant } from "@dxos/invariant";
12
- import { EID } from "@dxos/keys";
13
- import { Markdown } from "@dxos/plugin-markdown";
14
- import { useQuery } from "@dxos/react-client/echo";
15
- import { Panel } from "@dxos/react-ui";
16
- import { useAttention } from "@dxos/react-ui-attention";
17
- import { Board } from "@dxos/react-ui-board";
18
- import { ObjectPicker } from "@dxos/react-ui-form";
19
- import { isNonNullable } from "@dxos/util";
20
- var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-board/src/containers/BoardArticle/BoardArticle.tsx";
21
- var DEFAULT_POSITION = {
22
- x: 0,
23
- y: 0
24
- };
25
- var BoardArticle = ({ role, subject: board, attendableId }) => {
26
- const { hasAttention } = useAttention(attendableId);
27
- const db = Obj.getDatabase(board);
28
- const [boardItems] = useObject(board, "items");
29
- const itemsAtom = useMemo(() => Atom.make((get) => {
30
- const result = [];
31
- for (const ref of boardItems ?? []) {
32
- const obj = get(Obj.atomReactive(ref));
33
- if (obj) {
34
- result.push(obj);
35
- }
36
- }
37
- return result;
38
- }), [
39
- boardItems
40
- ]);
41
- const items = useAtomValue(itemsAtom);
42
- const controller = useRef(null);
43
- const addTriggerRef = useRef(null);
44
- const [pickerState, setPickerState] = useState(null);
45
- const objects = useQuery(db, Filter.everything());
46
- const options = useMemo(() => objects.filter((obj) => obj.id !== board.id).map((obj) => {
47
- const label = Obj.getLabel(obj);
48
- if (label) {
49
- return {
50
- id: obj.id,
51
- label,
52
- hue: "neutral"
53
- };
54
- }
55
- }).filter(isNonNullable).sort(({ label: a }, { label: b }) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())), [
56
- objects
57
- ]);
58
- const handleAdd = useCallback(async (anchor, position) => {
59
- const db2 = Obj.getDatabase(board);
60
- invariant(db2, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 60, S: void 0, A: ["db", ""] });
61
- if (position) {
62
- const doc = db2.add(Markdown.make());
63
- Obj.update(board, (board2) => {
64
- board2.items.push(Ref.make(doc));
65
- board2.layout.cells[doc.id.toString()] = position;
66
- });
67
- return;
68
- }
69
- addTriggerRef.current = anchor;
70
- setPickerState({
71
- position: DEFAULT_POSITION
72
- });
73
- }, [
74
- board
75
- ]);
76
- const handleDelete = useCallback((id) => {
77
- const idx = board.items.findIndex((ref) => {
78
- const echoUri = EID.tryParse(ref.uri);
79
- return (echoUri ? EID.getEntityId(echoUri) : void 0) === id;
80
- });
81
- Obj.update(board, (board2) => {
82
- if (idx !== -1) {
83
- board2.items.splice(idx, 1);
84
- }
85
- delete board2.layout.cells[id];
86
- });
87
- }, [
88
- board
89
- ]);
90
- const handleMove = useCallback((id, position) => {
91
- const layout = board.layout.cells[id];
92
- Obj.update(board, (board2) => {
93
- board2.layout.cells[id] = {
94
- ...layout,
95
- ...position
96
- };
97
- });
98
- }, [
99
- board
100
- ]);
101
- const handleSelect = useCallback((id) => {
102
- if (!pickerState) {
103
- return;
104
- }
105
- const selectedObject = objects.find((obj) => obj.id === id);
106
- if (!Obj.isObject(selectedObject)) {
107
- return;
108
- }
109
- Obj.update(board, (board2) => {
110
- board2.items.push(Ref.make(selectedObject));
111
- board2.layout.cells[selectedObject.id.toString()] = pickerState.position;
112
- });
113
- setPickerState(null);
114
- }, [
115
- pickerState,
116
- objects,
117
- board
118
- ]);
119
- return /* @__PURE__ */ React.createElement(Board.Root, {
120
- ref: controller,
121
- layout: board.layout,
122
- onAdd: handleAdd,
123
- onDelete: handleDelete,
124
- onMove: handleMove
125
- }, /* @__PURE__ */ React.createElement(ObjectPicker.Root, {
126
- open: !!pickerState,
127
- onOpenChange: (nextOpen) => {
128
- setPickerState(nextOpen ? {
129
- position: DEFAULT_POSITION
130
- } : null);
131
- }
132
- }, /* @__PURE__ */ React.createElement(Panel.Root, {
133
- role
134
- }, /* @__PURE__ */ React.createElement(Panel.Toolbar, {
135
- asChild: true
136
- }, /* @__PURE__ */ React.createElement(Board.Toolbar, {
137
- disabled: !hasAttention
138
- })), /* @__PURE__ */ React.createElement(Panel.Content, {
139
- asChild: true
140
- }, /* @__PURE__ */ React.createElement(Board.Container, null, /* @__PURE__ */ React.createElement(Board.Viewport, {
141
- classNames: "border-none"
142
- }, /* @__PURE__ */ React.createElement(Board.Backdrop, null), /* @__PURE__ */ React.createElement(Board.Content, null, items?.map((item, index) => /* @__PURE__ */ React.createElement(Board.Cell, {
143
- item,
144
- key: index,
145
- layout: board.layout?.cells[item.id] ?? {
146
- x: 0,
147
- y: 0
148
- }
149
- }, /* @__PURE__ */ React.createElement(Surface.Surface, {
150
- type: AppSurface.CardContent,
151
- data: {
152
- subject: item,
153
- editable: true
154
- },
155
- limit: 1
156
- })))))))), /* @__PURE__ */ React.createElement(ObjectPicker.Content, {
157
- options,
158
- onSelect: handleSelect,
159
- classNames: "dx-card-popover-width"
160
- }), /* @__PURE__ */ React.createElement(ObjectPicker.VirtualTrigger, {
161
- virtualRef: addTriggerRef
162
- })));
163
- };
164
- export {
165
- BoardArticle as default
166
- };
167
- //# sourceMappingURL=BoardArticle-S7VMVBPU.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/BoardArticle/BoardArticle.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect-atom/atom-react';\nimport * as Atom from '@effect-atom/atom/Atom';\nimport React, { useCallback, useMemo, useRef, useState } from 'react';\n\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\nimport { Filter, Obj, Ref } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport { invariant } from '@dxos/invariant';\nimport { EID } from '@dxos/keys';\nimport { Markdown } from '@dxos/plugin-markdown';\nimport { useQuery } from '@dxos/react-client/echo';\nimport { Panel } from '@dxos/react-ui';\nimport { useAttention } from '@dxos/react-ui-attention';\nimport { Board, type BoardController, type BoardRootProps, type Position } from '@dxos/react-ui-board';\nimport { ObjectPicker, type ObjectPickerContentProps } from '@dxos/react-ui-form';\nimport { isNonNullable } from '@dxos/util';\n\nimport { type Board as BoardType } from '#types';\n\nconst DEFAULT_POSITION = { x: 0, y: 0 } satisfies Position;\n\ntype PickerState = {\n position: Position;\n};\n\nexport type BoardArticleProps = AppSurface.ObjectArticleProps<BoardType.Board>;\n\nexport const BoardArticle = ({ role, subject: board, attendableId }: BoardArticleProps) => {\n const { hasAttention } = useAttention(attendableId);\n const db = Obj.getDatabase(board);\n const [boardItems] = useObject(board, 'items');\n const itemsAtom = useMemo(\n () =>\n Atom.make((get) => {\n const result: Obj.Unknown[] = [];\n for (const ref of boardItems ?? []) {\n const obj = get(Obj.atomReactive(ref));\n if (obj) {\n result.push(obj);\n }\n }\n return result;\n }),\n [boardItems],\n );\n const items = useAtomValue(itemsAtom);\n\n const controller = useRef<BoardController>(null);\n const addTriggerRef = useRef<HTMLButtonElement | null>(null);\n const [pickerState, setPickerState] = useState<PickerState | null>(null);\n\n // TODO(burdon): Use search.\n const objects = useQuery(db, Filter.everything());\n const options = useMemo<ObjectPickerContentProps['options']>(\n () =>\n objects\n .filter((obj) => obj.id !== board.id)\n .map((obj) => {\n const label = Obj.getLabel(obj);\n if (label) {\n return {\n id: obj.id,\n label,\n hue: 'neutral' as const,\n };\n }\n })\n .filter(isNonNullable)\n .sort(({ label: a }, { label: b }) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())),\n [objects],\n );\n\n const handleAdd = useCallback<NonNullable<BoardRootProps['onAdd']>>(\n async (anchor, position) => {\n const db = Obj.getDatabase(board);\n invariant(db);\n // Grid backdrop \"+\" supplies a position → create a new Markdown document directly.\n // Toolbar \"+\" omits position → fall back to the picker over existing objects.\n if (position) {\n const doc = db.add(Markdown.make());\n Obj.update(board, (board) => {\n board.items.push(Ref.make(doc));\n board.layout.cells[doc.id.toString()] = position;\n });\n return;\n }\n\n addTriggerRef.current = anchor;\n setPickerState({ position: DEFAULT_POSITION });\n },\n [board],\n );\n\n // TODO(burdon): Use intents so can be undone.\n const handleDelete = useCallback<NonNullable<BoardRootProps['onDelete']>>(\n (id) => {\n // TODO(burdon): Impl. DXN.equals and pass in DXN from `id`.\n const idx = board.items.findIndex((ref) => {\n const echoUri = EID.tryParse(ref.uri);\n return (echoUri ? EID.getEntityId(echoUri) : undefined) === id;\n });\n Obj.update(board, (board) => {\n if (idx !== -1) {\n board.items.splice(idx, 1);\n }\n delete board.layout.cells[id];\n });\n },\n [board],\n );\n\n const handleMove = useCallback<NonNullable<BoardRootProps['onMove']>>(\n (id, position) => {\n const layout = board.layout.cells[id];\n Obj.update(board, (board) => {\n board.layout.cells[id] = { ...layout, ...position };\n });\n },\n [board],\n );\n\n const handleSelect = useCallback<NonNullable<ObjectPickerContentProps['onSelect']>>(\n (id) => {\n if (!pickerState) {\n return;\n }\n\n // Find the selected object by id from the space.\n const selectedObject = objects.find((obj) => obj.id === id);\n if (!Obj.isObject(selectedObject)) {\n return;\n }\n\n // Create a reference to the selected object and add it to the board.\n Obj.update(board, (board) => {\n board.items.push(Ref.make(selectedObject));\n\n // Set the layout position for the new item.\n board.layout.cells[selectedObject.id.toString()] = pickerState.position;\n });\n\n // Close the picker.\n setPickerState(null);\n },\n [pickerState, objects, board],\n );\n\n return (\n <Board.Root ref={controller} layout={board.layout} onAdd={handleAdd} onDelete={handleDelete} onMove={handleMove}>\n <ObjectPicker.Root\n open={!!pickerState}\n onOpenChange={(nextOpen: boolean) => {\n setPickerState(nextOpen ? { position: DEFAULT_POSITION } : null);\n }}\n >\n <Panel.Root role={role}>\n <Panel.Toolbar asChild>\n <Board.Toolbar disabled={!hasAttention} />\n </Panel.Toolbar>\n <Panel.Content asChild>\n <Board.Container>\n <Board.Viewport classNames='border-none'>\n <Board.Backdrop />\n <Board.Content>\n {items?.map((item, index) => (\n <Board.Cell item={item} key={index} layout={board.layout?.cells[item.id] ?? { x: 0, y: 0 }}>\n <Surface.Surface\n type={AppSurface.CardContent}\n data={{ subject: item, editable: true }}\n limit={1}\n />\n </Board.Cell>\n ))}\n </Board.Content>\n </Board.Viewport>\n </Board.Container>\n </Panel.Content>\n </Panel.Root>\n <ObjectPicker.Content options={options} onSelect={handleSelect} classNames='dx-card-popover-width' />\n <ObjectPicker.VirtualTrigger virtualRef={addTriggerRef} />\n </ObjectPicker.Root>\n </Board.Root>\n );\n};\n"],
5
- "mappings": "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",
6
- "names": ["useAtomValue", "Atom", "React", "useCallback", "useMemo", "useRef", "useState", "Surface", "AppSurface", "Filter", "Obj", "Ref", "useObject", "invariant", "EID", "Markdown", "useQuery", "Panel", "useAttention", "Board", "ObjectPicker", "isNonNullable", "DEFAULT_POSITION", "y", "db", "getDatabase", "board", "boardItems", "itemsAtom", "result", "obj", "get", "push", "addTriggerRef", "pickerState", "setPickerState", "objects", "everything", "label", "getLabel", "id", "hue", "filter", "items", "make", "doc", "layout", "cells", "findIndex", "ref", "echoUri", "getEntityId", "update", "idx", "position", "selectedObject", "toString", "onAdd", "onDelete", "handleDelete", "onMove", "open", "onOpenChange", "nextOpen", "role", "Toolbar", "asChild", "disabled", "Container", "classNames", "Backdrop", "Content", "item", "index", "x", "type", "subject", "editable", "limit", "options", "onSelect", "handleSelect", "virtualRef"]
7
- }