@dxos/plugin-kanban 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc

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 (191) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/neutral/KanbanArticle-NEJ3LNBO.mjs +132 -0
  4. package/dist/lib/neutral/KanbanArticle-NEJ3LNBO.mjs.map +7 -0
  5. package/dist/lib/neutral/KanbanPlugin.mjs +36 -0
  6. package/dist/lib/neutral/KanbanPlugin.mjs.map +7 -0
  7. package/dist/lib/neutral/KanbanPlugin.node.mjs +27 -0
  8. package/dist/lib/neutral/KanbanPlugin.node.mjs.map +7 -0
  9. package/dist/lib/neutral/KanbanPlugin.workerd.mjs +21 -0
  10. package/dist/lib/neutral/KanbanPlugin.workerd.mjs.map +7 -0
  11. package/dist/lib/neutral/KanbanSettings-G6M47NSK.mjs +83 -0
  12. package/dist/lib/neutral/KanbanSettings-G6M47NSK.mjs.map +7 -0
  13. package/dist/lib/neutral/blueprint-definition-FHVIEKTQ.mjs +15 -0
  14. package/dist/lib/neutral/blueprint-definition-FHVIEKTQ.mjs.map +7 -0
  15. package/dist/lib/neutral/blueprints/index.mjs +8 -0
  16. package/dist/lib/neutral/capabilities/index.mjs +19 -0
  17. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  18. package/dist/lib/neutral/chunk-6FPBLOS3.mjs +8 -0
  19. package/dist/lib/neutral/chunk-6FPBLOS3.mjs.map +7 -0
  20. package/dist/lib/{browser/index.mjs → neutral/chunk-KDPM77BD.mjs} +1 -9
  21. package/dist/lib/neutral/chunk-KDPM77BD.mjs.map +7 -0
  22. package/dist/lib/neutral/chunk-OQ72EEGU.mjs +254 -0
  23. package/dist/lib/neutral/chunk-OQ72EEGU.mjs.map +7 -0
  24. package/dist/lib/{browser/blueprints/index.mjs → neutral/chunk-ZTQW5KQS.mjs} +3 -4
  25. package/dist/lib/{browser/blueprints/index.mjs.map → neutral/chunk-ZTQW5KQS.mjs.map} +2 -2
  26. package/dist/lib/neutral/components/index.mjs +243 -0
  27. package/dist/lib/neutral/components/index.mjs.map +7 -0
  28. package/dist/lib/neutral/containers/index.mjs +11 -0
  29. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  30. package/dist/lib/neutral/create-object-DKBSI46K.mjs +40 -0
  31. package/dist/lib/neutral/create-object-DKBSI46K.mjs.map +7 -0
  32. package/dist/lib/{browser/delete-card-7OSCNCW2.mjs → neutral/delete-card-356CBACE.mjs} +4 -4
  33. package/dist/lib/neutral/delete-card-356CBACE.mjs.map +7 -0
  34. package/dist/lib/{browser/delete-card-field-NSB2RE3Z.mjs → neutral/delete-card-field-IRCZL2BR.mjs} +4 -4
  35. package/dist/lib/neutral/delete-card-field-IRCZL2BR.mjs.map +7 -0
  36. package/dist/lib/neutral/hooks/index.mjs +432 -0
  37. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  38. package/dist/lib/neutral/index.mjs +34 -0
  39. package/dist/lib/neutral/index.mjs.map +7 -0
  40. package/dist/lib/neutral/meta.json +1 -0
  41. package/dist/lib/neutral/meta.mjs +8 -0
  42. package/dist/lib/neutral/meta.mjs.map +7 -0
  43. package/dist/lib/neutral/migrations-IWBT35UT.mjs +31 -0
  44. package/dist/lib/neutral/migrations-IWBT35UT.mjs.map +7 -0
  45. package/dist/lib/neutral/operation-handler-B7IW6MXU.mjs +13 -0
  46. package/dist/lib/neutral/operation-handler-B7IW6MXU.mjs.map +7 -0
  47. package/dist/lib/neutral/operations/index.mjs +8 -0
  48. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  49. package/dist/lib/neutral/plugin.mjs +16 -0
  50. package/dist/lib/neutral/plugin.mjs.map +7 -0
  51. package/dist/lib/neutral/react-surface-QROEHBRW.mjs +93 -0
  52. package/dist/lib/neutral/react-surface-QROEHBRW.mjs.map +7 -0
  53. package/dist/lib/{browser/restore-card-FO3WERIE.mjs → neutral/restore-card-P25Y4YSE.mjs} +4 -4
  54. package/dist/lib/neutral/restore-card-P25Y4YSE.mjs.map +7 -0
  55. package/dist/lib/{browser/restore-card-field-U5XYEEOW.mjs → neutral/restore-card-field-NHR3R4XI.mjs} +4 -4
  56. package/dist/lib/neutral/restore-card-field-NHR3R4XI.mjs.map +7 -0
  57. package/dist/lib/neutral/testing/index.mjs +60 -0
  58. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  59. package/dist/lib/neutral/types/index.mjs +22 -0
  60. package/dist/lib/neutral/types/index.mjs.map +7 -0
  61. package/dist/lib/neutral/undo-mappings-6CHW6BOF.mjs +42 -0
  62. package/dist/lib/neutral/undo-mappings-6CHW6BOF.mjs.map +7 -0
  63. package/dist/types/src/KanbanPlugin.d.ts.map +1 -1
  64. package/dist/types/src/KanbanPlugin.node.d.ts.map +1 -1
  65. package/dist/types/src/KanbanPlugin.workerd.d.ts +4 -0
  66. package/dist/types/src/KanbanPlugin.workerd.d.ts.map +1 -0
  67. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  68. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  69. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/index.d.ts +6 -0
  71. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  72. package/dist/types/src/capabilities/migrations.d.ts +2 -1
  73. package/dist/types/src/capabilities/migrations.d.ts.map +1 -1
  74. package/dist/types/src/components/KanbanBoard/KanbanBoard.d.ts +3 -23
  75. package/dist/types/src/components/KanbanBoard/KanbanBoard.d.ts.map +1 -1
  76. package/dist/types/src/components/KanbanBoard/KanbanBoard.stories.d.ts.map +1 -1
  77. package/dist/types/src/components/KanbanBoard/KanbanCard.d.ts +2 -3
  78. package/dist/types/src/components/KanbanBoard/KanbanCard.d.ts.map +1 -1
  79. package/dist/types/src/components/KanbanBoard/KanbanColumn.d.ts +2 -3
  80. package/dist/types/src/components/KanbanBoard/KanbanColumn.d.ts.map +1 -1
  81. package/dist/types/src/components/KanbanBoard/context.d.ts +38 -0
  82. package/dist/types/src/components/KanbanBoard/context.d.ts.map +1 -0
  83. package/dist/types/src/containers/KanbanArticle/KanbanArticle.d.ts +6 -0
  84. package/dist/types/src/containers/KanbanArticle/KanbanArticle.d.ts.map +1 -0
  85. package/dist/types/src/containers/{KanbanContainer/KanbanContainer.stories.d.ts → KanbanArticle/KanbanArticle.stories.d.ts} +1 -1
  86. package/dist/types/src/containers/KanbanArticle/KanbanArticle.stories.d.ts.map +1 -0
  87. package/dist/types/src/containers/KanbanArticle/index.d.ts +2 -0
  88. package/dist/types/src/containers/KanbanArticle/index.d.ts.map +1 -0
  89. package/dist/types/src/containers/index.d.ts +1 -1
  90. package/dist/types/src/containers/index.d.ts.map +1 -1
  91. package/dist/types/src/index.d.ts +3 -2
  92. package/dist/types/src/index.d.ts.map +1 -1
  93. package/dist/types/src/operations/delete-card-field.d.ts +2 -2
  94. package/dist/types/src/operations/delete-card-field.d.ts.map +1 -1
  95. package/dist/types/src/operations/delete-card.d.ts +2 -2
  96. package/dist/types/src/operations/delete-card.d.ts.map +1 -1
  97. package/dist/types/src/operations/index.d.ts +0 -1
  98. package/dist/types/src/operations/index.d.ts.map +1 -1
  99. package/dist/types/src/operations/restore-card-field.d.ts +2 -2
  100. package/dist/types/src/operations/restore-card-field.d.ts.map +1 -1
  101. package/dist/types/src/operations/restore-card.d.ts +2 -2
  102. package/dist/types/src/operations/restore-card.d.ts.map +1 -1
  103. package/dist/types/src/plugin.d.ts +4 -0
  104. package/dist/types/src/plugin.d.ts.map +1 -0
  105. package/dist/types/src/types/Kanban.d.ts +1 -2
  106. package/dist/types/src/types/Kanban.d.ts.map +1 -1
  107. package/dist/types/src/{operations/definitions.d.ts → types/KanbanOperation.d.ts} +1 -1
  108. package/dist/types/src/types/KanbanOperation.d.ts.map +1 -0
  109. package/dist/types/src/types/index.d.ts +2 -1
  110. package/dist/types/src/types/index.d.ts.map +1 -1
  111. package/dist/types/tsconfig.tsbuildinfo +1 -1
  112. package/package.json +101 -73
  113. package/src/KanbanPlugin.node.ts +3 -37
  114. package/src/KanbanPlugin.tsx +11 -38
  115. package/src/KanbanPlugin.workerd.ts +18 -0
  116. package/src/capabilities/artifact-definition.ts +3 -4
  117. package/src/capabilities/create-object.ts +40 -0
  118. package/src/capabilities/index.ts +1 -0
  119. package/src/capabilities/migrations.ts +3 -3
  120. package/src/capabilities/react-surface.tsx +3 -4
  121. package/src/capabilities/undo-mappings.ts +1 -1
  122. package/src/components/KanbanBoard/KanbanBoard.stories.tsx +1 -2
  123. package/src/components/KanbanBoard/KanbanBoard.tsx +13 -47
  124. package/src/components/KanbanBoard/KanbanCard.tsx +75 -72
  125. package/src/components/KanbanBoard/KanbanColumn.tsx +9 -8
  126. package/src/components/KanbanBoard/context.ts +54 -0
  127. package/src/containers/{KanbanContainer/KanbanContainer.stories.tsx → KanbanArticle/KanbanArticle.stories.tsx} +9 -8
  128. package/src/containers/{KanbanContainer/KanbanContainer.tsx → KanbanArticle/KanbanArticle.tsx} +8 -8
  129. package/src/containers/KanbanArticle/index.ts +5 -0
  130. package/src/containers/index.ts +1 -1
  131. package/src/hooks/useKanbanBoardModel.browser.test.ts +1 -2
  132. package/src/index.ts +3 -6
  133. package/src/operations/delete-card-field.ts +2 -2
  134. package/src/operations/delete-card.ts +2 -2
  135. package/src/operations/index.ts +0 -2
  136. package/src/operations/restore-card-field.ts +2 -2
  137. package/src/operations/restore-card.ts +2 -2
  138. package/src/playwright/smoke.spec.ts +3 -3
  139. package/src/plugin.ts +11 -0
  140. package/src/types/Kanban.ts +1 -2
  141. package/src/{operations/definitions.ts → types/KanbanOperation.ts} +4 -0
  142. package/src/types/index.ts +3 -1
  143. package/src/types/migrations.test.ts +2 -3
  144. package/src/types/schema.ts +1 -1
  145. package/src/util/arrangement.test.ts +1 -2
  146. package/dist/lib/browser/chunk-T32TEM55.mjs +0 -105
  147. package/dist/lib/browser/chunk-T32TEM55.mjs.map +0 -7
  148. package/dist/lib/browser/delete-card-7OSCNCW2.mjs.map +0 -7
  149. package/dist/lib/browser/delete-card-field-NSB2RE3Z.mjs.map +0 -7
  150. package/dist/lib/browser/index.mjs.map +0 -7
  151. package/dist/lib/browser/meta.json +0 -1
  152. package/dist/lib/browser/operations/index.mjs +0 -13
  153. package/dist/lib/browser/operations/index.mjs.map +0 -7
  154. package/dist/lib/browser/restore-card-FO3WERIE.mjs.map +0 -7
  155. package/dist/lib/browser/restore-card-field-U5XYEEOW.mjs.map +0 -7
  156. package/dist/lib/browser/types/index.mjs +0 -160
  157. package/dist/lib/browser/types/index.mjs.map +0 -7
  158. package/dist/lib/node-esm/blueprints/index.mjs +0 -28
  159. package/dist/lib/node-esm/blueprints/index.mjs.map +0 -7
  160. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  161. package/dist/lib/node-esm/chunk-W2RNFBMZ.mjs +0 -106
  162. package/dist/lib/node-esm/chunk-W2RNFBMZ.mjs.map +0 -7
  163. package/dist/lib/node-esm/delete-card-ZIREL6HN.mjs +0 -25
  164. package/dist/lib/node-esm/delete-card-ZIREL6HN.mjs.map +0 -7
  165. package/dist/lib/node-esm/delete-card-field-IPTEGVPP.mjs +0 -43
  166. package/dist/lib/node-esm/delete-card-field-IPTEGVPP.mjs.map +0 -7
  167. package/dist/lib/node-esm/index.mjs +0 -30
  168. package/dist/lib/node-esm/index.mjs.map +0 -7
  169. package/dist/lib/node-esm/meta.json +0 -1
  170. package/dist/lib/node-esm/operations/index.mjs +0 -14
  171. package/dist/lib/node-esm/operations/index.mjs.map +0 -7
  172. package/dist/lib/node-esm/restore-card-WJJ4YB7K.mjs +0 -22
  173. package/dist/lib/node-esm/restore-card-WJJ4YB7K.mjs.map +0 -7
  174. package/dist/lib/node-esm/restore-card-field-L24WJXAW.mjs +0 -41
  175. package/dist/lib/node-esm/restore-card-field-L24WJXAW.mjs.map +0 -7
  176. package/dist/lib/node-esm/translations.mjs +0 -45
  177. package/dist/lib/node-esm/translations.mjs.map +0 -7
  178. package/dist/lib/node-esm/types/index.mjs +0 -161
  179. package/dist/lib/node-esm/types/index.mjs.map +0 -7
  180. package/dist/types/src/containers/KanbanContainer/KanbanContainer.d.ts +0 -6
  181. package/dist/types/src/containers/KanbanContainer/KanbanContainer.d.ts.map +0 -1
  182. package/dist/types/src/containers/KanbanContainer/KanbanContainer.stories.d.ts.map +0 -1
  183. package/dist/types/src/containers/KanbanContainer/index.d.ts +0 -2
  184. package/dist/types/src/containers/KanbanContainer/index.d.ts.map +0 -1
  185. package/dist/types/src/operations/definitions.d.ts.map +0 -1
  186. package/src/containers/KanbanContainer/index.ts +0 -5
  187. /package/dist/lib/{browser/chunk-J5LGTIGS.mjs.map → neutral/blueprints/index.mjs.map} +0 -0
  188. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  189. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  190. /package/dist/lib/{browser → neutral}/translations.mjs +0 -0
  191. /package/dist/lib/{browser → neutral}/translations.mjs.map +0 -0
@@ -9,90 +9,93 @@ import { AppSurface, useObjectMenuItems } from '@dxos/app-toolkit/ui';
9
9
  import { Obj } from '@dxos/echo';
10
10
  import { Card, Toolbar, useTranslation } from '@dxos/react-ui';
11
11
  import { Menu, createMenuAction } from '@dxos/react-ui-menu';
12
- import { Focus, Mosaic, type MosaicTileProps, useBoard } from '@dxos/react-ui-mosaic';
12
+ import { Focus, Mosaic, useBoard } from '@dxos/react-ui-mosaic';
13
13
 
14
14
  import { meta } from '#meta';
15
15
 
16
- import { useKanbanBoard } from './KanbanBoard';
16
+ import { type KanbanCardProps, useKanbanBoard } from './context';
17
17
 
18
- const KANBAN_CARD_TILE_NAME = 'KanbanBoard.Card';
18
+ export { type KanbanCardProps };
19
19
 
20
- export type KanbanCardProps = Pick<MosaicTileProps<Obj.Unknown>, 'location' | 'data' | 'debug'>;
20
+ const KANBAN_CARD_TILE_NAME = 'KanbanBoard.Card';
21
21
 
22
22
  /**
23
23
  * Mosaic Tile for Kanban card.
24
24
  * Uses Surface for content; requires plugin manager context.
25
25
  */
26
- export const KanbanCard = forwardRef<HTMLDivElement, KanbanCardProps>(({ data, location, debug }, forwardedRef) => {
27
- const { t } = useTranslation(meta.id);
28
- const { model } = useBoard(KANBAN_CARD_TILE_NAME);
29
- const { projection, columnFieldPath, onCardRemove } = useKanbanBoard(KANBAN_CARD_TILE_NAME);
30
- const [dragHandle, setDragHandle] = useState<HTMLButtonElement | null>(null);
31
- const dragHandleRef = useCallback((el: HTMLButtonElement | null) => setDragHandle(el), []);
26
+ export const KanbanCard = forwardRef<HTMLDivElement, KanbanCardProps>(
27
+ ({ data, location, debug, draggable }, forwardedRef) => {
28
+ const { t } = useTranslation(meta.id);
29
+ const { model } = useBoard(KANBAN_CARD_TILE_NAME);
30
+ const { projection, columnFieldPath, onCardRemove } = useKanbanBoard(KANBAN_CARD_TILE_NAME);
31
+ const [dragHandle, setDragHandle] = useState<HTMLButtonElement | null>(null);
32
+ const dragHandleRef = useCallback((el: HTMLButtonElement | null) => setDragHandle(el), []);
32
33
 
33
- const objectMenuItems = useObjectMenuItems(data);
34
+ const objectMenuItems = useObjectMenuItems(data);
34
35
 
35
- const menuItems = useMemo(
36
- () => [
37
- ...objectMenuItems,
38
- ...(onCardRemove
39
- ? [
40
- createMenuAction('remove', () => onCardRemove(data), {
41
- label: t('remove-card.label'),
42
- icon: 'ph--trash--regular',
43
- }),
44
- ]
45
- : []),
46
- ],
47
- [objectMenuItems, onCardRemove, data, t],
48
- );
36
+ const menuItems = useMemo(
37
+ () => [
38
+ ...objectMenuItems,
39
+ ...(onCardRemove
40
+ ? [
41
+ createMenuAction('remove', () => onCardRemove(data), {
42
+ label: t('remove-card.label'),
43
+ icon: 'ph--trash--regular',
44
+ }),
45
+ ]
46
+ : []),
47
+ ],
48
+ [objectMenuItems, onCardRemove, data, t],
49
+ );
49
50
 
50
- return (
51
- <Menu.Root>
52
- <Mosaic.Tile
53
- asChild
54
- id={model.getItemId(data)}
55
- data={data}
56
- location={location}
57
- debug={debug}
58
- dragHandle={dragHandle}
59
- >
60
- <Focus.Item asChild>
61
- <Card.Root ref={forwardedRef} data-testid='board-item'>
62
- <Card.Toolbar>
63
- <Card.DragHandle ref={dragHandleRef} testId='mosaicBoard.cardDragHandle' />
64
- <Card.Title data-testid='mosaicBoard.cardTitle'>{Obj.getLabel(data)}</Card.Title>
65
- {/* TODO(wittjosiah): Reconcile with Card.Menu. */}
66
- <Menu.Trigger asChild disabled={!menuItems?.length}>
67
- <Toolbar.IconButton
68
- iconOnly
69
- variant='ghost'
70
- icon='ph--dots-three-vertical--regular'
71
- label={t('action-menu.label')}
72
- />
73
- </Menu.Trigger>
74
- <Menu.Content items={menuItems} />
75
- </Card.Toolbar>
76
- <Card.Content>
77
- {projection && (
78
- <Surface.Surface
79
- type={AppSurface.Card}
80
- limit={1}
81
- data={{
82
- subject: data,
83
- projection,
84
- // Hide the pivot field: its value is already conveyed by
85
- // which column the card sits in.
86
- ignorePaths: columnFieldPath ? [columnFieldPath] : undefined,
87
- }}
88
- />
89
- )}
90
- </Card.Content>
91
- </Card.Root>
92
- </Focus.Item>
93
- </Mosaic.Tile>
94
- </Menu.Root>
95
- );
96
- });
51
+ return (
52
+ <Menu.Root>
53
+ <Mosaic.Tile
54
+ asChild
55
+ id={model.getItemId(data)}
56
+ data={data}
57
+ location={location}
58
+ debug={debug}
59
+ draggable={draggable}
60
+ dragHandle={dragHandle}
61
+ >
62
+ <Focus.Item asChild>
63
+ <Card.Root ref={forwardedRef} data-testid='board-item'>
64
+ <Card.Toolbar>
65
+ <Card.DragHandle ref={dragHandleRef} testId='mosaicBoard.cardDragHandle' />
66
+ <Card.Title data-testid='mosaicBoard.cardTitle'>{Obj.getLabel(data)}</Card.Title>
67
+ {/* TODO(wittjosiah): Reconcile with Card.Menu. */}
68
+ <Menu.Trigger asChild disabled={!menuItems?.length}>
69
+ <Toolbar.IconButton
70
+ iconOnly
71
+ variant='ghost'
72
+ icon='ph--dots-three-vertical--regular'
73
+ label={t('action-menu.label')}
74
+ />
75
+ </Menu.Trigger>
76
+ <Menu.Content items={menuItems} />
77
+ </Card.Toolbar>
78
+ <Card.Content>
79
+ {projection && (
80
+ <Surface.Surface
81
+ type={AppSurface.Card}
82
+ limit={1}
83
+ data={{
84
+ subject: data,
85
+ projection,
86
+ // Hide the pivot field: its value is already conveyed by
87
+ // which column the card sits in.
88
+ ignorePaths: columnFieldPath ? [columnFieldPath] : undefined,
89
+ }}
90
+ />
91
+ )}
92
+ </Card.Content>
93
+ </Card.Root>
94
+ </Focus.Item>
95
+ </Mosaic.Tile>
96
+ </Menu.Root>
97
+ );
98
+ },
99
+ );
97
100
 
98
101
  KanbanCard.displayName = KANBAN_CARD_TILE_NAME;
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { FC, forwardRef, RefObject, useRef } from 'react';
5
+ import React, { FC, forwardRef, useState } from 'react';
6
6
 
7
7
  import type { Obj } from '@dxos/echo';
8
8
  import { Board, type MosaicTileProps, useBoard } from '@dxos/react-ui-mosaic';
@@ -10,23 +10,23 @@ import { Board, type MosaicTileProps, useBoard } from '@dxos/react-ui-mosaic';
10
10
  import { useKanbanItemEventHandler } from '#hooks';
11
11
  import { type ColumnStructure, UNCATEGORIZED_VALUE } from '#types';
12
12
 
13
- import { useKanbanBoard } from './KanbanBoard';
13
+ import { type KanbanColumnProps, useKanbanBoard } from './context';
14
14
 
15
- const KANBAN_COLUMN_NAME = 'KanbanBoard.Column';
15
+ export { type KanbanColumnProps };
16
16
 
17
- export type KanbanColumnProps = Pick<MosaicTileProps<ColumnStructure>, 'location' | 'data' | 'debug'>;
17
+ const KANBAN_COLUMN_NAME = 'KanbanBoard.Column';
18
18
 
19
19
  /**
20
20
  * Mosaic Tile for Kanban column.
21
21
  */
22
22
  export const KanbanColumn = forwardRef<HTMLDivElement, KanbanColumnProps>(
23
- ({ data: column, location, debug }, forwardedRef) => {
23
+ ({ data: column, location, debug, draggable }, forwardedRef) => {
24
24
  const { model } = useBoard<ColumnStructure, Obj.Unknown>(KANBAN_COLUMN_NAME);
25
25
  const { columnFieldPath, change, onCardAdd, getPivotAttributes, itemTile } = useKanbanBoard(KANBAN_COLUMN_NAME);
26
26
 
27
27
  const { title } = getPivotAttributes(column.columnValue);
28
28
  const uncategorized = column.columnValue === UNCATEGORIZED_VALUE;
29
- const dragHandleRef = useRef<HTMLButtonElement | null>(null);
29
+ const [dragHandle, setDragHandle] = useState<HTMLButtonElement | null>(null);
30
30
 
31
31
  const eventHandler = useKanbanItemEventHandler({
32
32
  column,
@@ -41,7 +41,8 @@ export const KanbanColumn = forwardRef<HTMLDivElement, KanbanColumnProps>(
41
41
  location={location}
42
42
  classNames='grid grid-rows-[var(--dx-rail-action)_1fr_var(--dx-rail-action)]'
43
43
  debug={debug}
44
- dragHandleRef={dragHandleRef}
44
+ draggable={draggable}
45
+ dragHandle={dragHandle}
45
46
  ref={forwardedRef}
46
47
  >
47
48
  {uncategorized ? (
@@ -49,7 +50,7 @@ export const KanbanColumn = forwardRef<HTMLDivElement, KanbanColumnProps>(
49
50
  <span className='font-medium'>{title}</span>
50
51
  </div>
51
52
  ) : (
52
- <Board.Column.Header label={title} dragHandleRef={dragHandleRef as RefObject<HTMLButtonElement>} />
53
+ <Board.Column.Header label={title} dragHandleRef={setDragHandle} />
53
54
  )}
54
55
  <Board.Column.Body
55
56
  data={column}
@@ -0,0 +1,54 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { createContext } from '@radix-ui/react-context';
6
+ import { type ComponentType } from 'react';
7
+
8
+ import { type Obj } from '@dxos/echo';
9
+ import { type MosaicTileProps } from '@dxos/react-ui-mosaic';
10
+ import { type ProjectionModel } from '@dxos/schema';
11
+
12
+ import { type ColumnStructure, type KanbanChangeCallback, UNCATEGORIZED_ATTRIBUTES, UNCATEGORIZED_VALUE } from '#types';
13
+
14
+ const KANBAN_BOARD_NAME = 'KanbanBoard.Context';
15
+
16
+ /**
17
+ * Card tile props. Defined here (rather than in `KanbanCard.tsx`) so the
18
+ * context type can reference it without forming a cycle through that
19
+ * module's runtime exports — webkit treats the resulting TDZ as a hard
20
+ * error while other engines are lenient.
21
+ */
22
+ export type KanbanCardProps = Pick<MosaicTileProps<Obj.Unknown>, 'location' | 'data' | 'debug' | 'draggable'>;
23
+
24
+ /**
25
+ * Column tile props. See note on {@link KanbanCardProps}.
26
+ */
27
+ export type KanbanColumnProps = Pick<MosaicTileProps<ColumnStructure>, 'location' | 'data' | 'debug' | 'draggable'>;
28
+
29
+ /**
30
+ * Context value for the Kanban board.
31
+ * Items are Echo objects (Obj.Unknown).
32
+ */
33
+ export type KanbanBoardContextValue = {
34
+ kanbanId: string;
35
+ projection: ProjectionModel | undefined;
36
+ columnFieldPath: string | undefined;
37
+ change: KanbanChangeCallback<Obj.Unknown>;
38
+ pivotFieldId: string | undefined;
39
+ getPivotAttributes: (columnValue: string) => { title: string; color: string };
40
+ itemTile?: ComponentType<KanbanCardProps>; // TODO(burdon): Prop.
41
+ onCardAdd?: (columnValue: string | undefined) => string | undefined;
42
+ onCardRemove?: (card: Obj.Unknown) => void;
43
+ };
44
+
45
+ export const [KanbanBoardContext, useKanbanBoard] = createContext<KanbanBoardContextValue>(KANBAN_BOARD_NAME, {
46
+ kanbanId: 'never',
47
+ projection: undefined,
48
+ columnFieldPath: undefined,
49
+ change: { kanban: () => {}, setItemField: () => {} },
50
+ pivotFieldId: undefined,
51
+ getPivotAttributes: (id: string) =>
52
+ id === UNCATEGORIZED_VALUE ? UNCATEGORIZED_ATTRIBUTES : { title: id, color: 'neutral' },
53
+ itemTile: (() => null) as ComponentType<KanbanCardProps>,
54
+ });
@@ -11,17 +11,18 @@ import { expect, waitFor, within } from 'storybook/test';
11
11
  import { withPluginManager } from '@dxos/app-framework/testing';
12
12
  import { Surface } from '@dxos/app-framework/ui';
13
13
  import { AppSurface } from '@dxos/app-toolkit/ui';
14
- import { Obj, type QueryAST, Type } from '@dxos/echo';
15
- import { View } from '@dxos/echo';
14
+ import { Filter, Obj, type QueryAST, Type, View } from '@dxos/echo';
16
15
  import { type Mutable } from '@dxos/echo/internal';
17
16
  import { invariant } from '@dxos/invariant';
18
- import { ClientPlugin } from '@dxos/plugin-client';
17
+ // `/plugin` entrypoints used here for the same reason as `corePlugins()` —
18
+ // see `@dxos/plugin-testing/src/core.ts` for the rationale.
19
+ import { ClientPlugin } from '@dxos/plugin-client/testing';
19
20
  import { initializeIdentity } from '@dxos/plugin-client/testing';
20
- import { PreviewPlugin } from '@dxos/plugin-preview';
21
- import { SpacePlugin } from '@dxos/plugin-space';
21
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
22
+ import { SpacePlugin } from '@dxos/plugin-space/testing';
22
23
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
23
24
  import { random } from '@dxos/random';
24
- import { Filter, type Space, useQuery, useSchema, useSpaces } from '@dxos/react-client/echo';
25
+ import { type Space, useQuery, useSchema, useSpaces } from '@dxos/react-client/echo';
25
26
  import { ViewEditor } from '@dxos/react-ui-form';
26
27
  import { Syntax } from '@dxos/react-ui-syntax-highlighter';
27
28
  import { withLayout } from '@dxos/react-ui/testing';
@@ -56,7 +57,7 @@ type ClientSetupOptions = {
56
57
 
57
58
  /**
58
59
  * Creates the standard plugin manager decorator with client configuration.
59
- * Includes KanbanPlugin so the Surface resolves to KanbanContainer.
60
+ * Includes KanbanPlugin so the Surface resolves to KanbanArticle.
60
61
  */
61
62
  const withKanbanPlugins = ({ types = [], onSpaceCreated }: ClientSetupOptions): Decorator =>
62
63
  withPluginManager({
@@ -80,7 +81,7 @@ const withKanbanPlugins = ({ types = [], onSpaceCreated }: ClientSetupOptions):
80
81
  });
81
82
 
82
83
  /**
83
- * Renders the first Kanban in the space via Surface (resolves to KanbanContainer),
84
+ * Renders the first Kanban in the space via Surface (resolves to KanbanArticle),
84
85
  * with a sidebar containing ViewEditor and Json filter.
85
86
  */
86
87
  const DefaultComponent = () => {
@@ -16,23 +16,23 @@ import { getTagFromQuery, getTypenameFromQuery } from '@dxos/schema';
16
16
 
17
17
  import { KanbanBoard } from '#components';
18
18
  import { useEchoChangeCallback, useItemsProjection, useProjectionModel } from '#hooks';
19
- import { KanbanOperation } from '#operations';
19
+ import { KanbanOperation } from '#types';
20
20
  import { Kanban } from '#types';
21
21
 
22
- export type KanbanContainerProps = AppSurface.ObjectArticleProps<Kanban.Kanban>;
22
+ export type KanbanArticleProps = AppSurface.ObjectArticleProps<Kanban.Kanban>;
23
23
 
24
- export const KanbanContainer = (props: KanbanContainerProps) => {
24
+ export const KanbanArticle = (props: KanbanArticleProps) => {
25
25
  // Branch on `kanban.spec.kind`: view-variant runs a typename query through
26
26
  // `useProjectionModel`; items-variant dereferences `kanban.spec.items` and
27
27
  // uses a stub projection from `useItemsProjection`.
28
28
  return Kanban.isKanbanItems(props.subject) ? (
29
- <ItemsKanbanContainer {...props} subject={props.subject} />
29
+ <ItemsKanbanArticle {...props} subject={props.subject} />
30
30
  ) : (
31
- <ViewKanbanContainer {...props} />
31
+ <ViewKanbanArticle {...props} />
32
32
  );
33
33
  };
34
34
 
35
- const ViewKanbanContainer = ({ role, subject: object }: KanbanContainerProps) => {
35
+ const ViewKanbanArticle = ({ role, subject: object }: KanbanArticleProps) => {
36
36
  const registry = useContext(RegistryContext);
37
37
  const schemas = useCapabilities(AppCapabilities.Schema);
38
38
  const db = Obj.getDatabase(object);
@@ -106,9 +106,9 @@ const ViewKanbanContainer = ({ role, subject: object }: KanbanContainerProps) =>
106
106
  );
107
107
  };
108
108
 
109
- type ItemsKanbanContainerProps = Omit<KanbanContainerProps, 'subject'> & { subject: Kanban.KanbanItems };
109
+ type ItemsKanbanArticleProps = Omit<KanbanArticleProps, 'subject'> & { subject: Kanban.KanbanItems };
110
110
 
111
- const ItemsKanbanContainer = ({ role, subject: object }: ItemsKanbanContainerProps) => {
111
+ const ItemsKanbanArticle = ({ role, subject: object }: ItemsKanbanArticleProps) => {
112
112
  const db = Obj.getDatabase(object);
113
113
  const projection = useItemsProjection(object);
114
114
  const change = useEchoChangeCallback(object);
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export { KanbanArticle as default } from './KanbanArticle';
@@ -4,5 +4,5 @@
4
4
 
5
5
  import { type ComponentType, lazy } from 'react';
6
6
 
7
- export const KanbanContainer: ComponentType<any> = lazy(() => import('./KanbanContainer'));
7
+ export const KanbanArticle: ComponentType<any> = lazy(() => import('./KanbanArticle'));
8
8
  export const KanbanSettings: ComponentType<any> = lazy(() => import('./KanbanSettings'));
@@ -7,8 +7,7 @@ import { act, renderHook } from '@testing-library/react';
7
7
  import * as Schema from 'effect/Schema';
8
8
  import { beforeEach, describe, test } from 'vitest';
9
9
 
10
- import { Filter, JsonSchema, Obj, Query, Type } from '@dxos/echo';
11
- import { type View } from '@dxos/echo';
10
+ import { Filter, JsonSchema, Obj, Query, Type, type View } from '@dxos/echo';
12
11
  import { Format, FormatAnnotation, PropertyMetaAnnotationId } from '@dxos/echo/internal';
13
12
  import { ObjectId } from '@dxos/keys';
14
13
  import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
package/src/index.ts CHANGED
@@ -2,10 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Plugin } from '@dxos/app-framework';
6
-
7
- import { meta } from './meta';
8
-
9
- export const KanbanPlugin = Plugin.lazy(meta, () => import('#plugin'));
10
-
5
+ export * from './blueprints';
11
6
  export * from './meta';
7
+ export * from './operations';
8
+ export * from './types';
@@ -9,9 +9,9 @@ import { type EchoSchema } from '@dxos/echo/internal';
9
9
  import { invariant } from '@dxos/invariant';
10
10
  import { ProjectionModel, createEchoChangeCallback, getTypenameFromQuery } from '@dxos/schema';
11
11
 
12
- import { DeleteCardField } from './definitions';
12
+ import { KanbanOperation } from '../types';
13
13
 
14
- const handler: Operation.WithHandler<typeof DeleteCardField> = DeleteCardField.pipe(
14
+ const handler: Operation.WithHandler<typeof KanbanOperation.DeleteCardField> = KanbanOperation.DeleteCardField.pipe(
15
15
  Operation.withHandler(
16
16
  Effect.fnUntraced(function* ({ view, fieldId }) {
17
17
  const registry = yield* Capability.get(Capabilities.AtomRegistry);
@@ -6,9 +6,9 @@ import { Operation } from '@dxos/compute';
6
6
  import { Obj } from '@dxos/echo';
7
7
  import { invariant } from '@dxos/invariant';
8
8
 
9
- import { DeleteCard } from './definitions';
9
+ import { KanbanOperation } from '../types';
10
10
 
11
- const handler: Operation.WithHandler<typeof DeleteCard> = DeleteCard.pipe(
11
+ const handler: Operation.WithHandler<typeof KanbanOperation.DeleteCard> = KanbanOperation.DeleteCard.pipe(
12
12
  Operation.withHandler(({ card }) =>
13
13
  Effect.sync(() => {
14
14
  const db = Obj.getDatabase(card);
@@ -2,8 +2,6 @@
2
2
 
3
3
  import { OperationHandlerSet } from '@dxos/compute';
4
4
 
5
- export * as KanbanOperation from './definitions';
6
-
7
5
  export const KanbanOperationHandlerSet = OperationHandlerSet.lazy(
8
6
  () => import('./delete-card'),
9
7
  () => import('./delete-card-field'),
@@ -9,9 +9,9 @@ import { type EchoSchema } from '@dxos/echo/internal';
9
9
  import { invariant } from '@dxos/invariant';
10
10
  import { ProjectionModel, createEchoChangeCallback, getTypenameFromQuery } from '@dxos/schema';
11
11
 
12
- import { RestoreCardField } from './definitions';
12
+ import { KanbanOperation } from '../types';
13
13
 
14
- const handler: Operation.WithHandler<typeof RestoreCardField> = RestoreCardField.pipe(
14
+ const handler: Operation.WithHandler<typeof KanbanOperation.RestoreCardField> = KanbanOperation.RestoreCardField.pipe(
15
15
  Operation.withHandler(
16
16
  Effect.fnUntraced(function* ({ view, field, props, index }) {
17
17
  const registry = yield* Capability.get(Capabilities.AtomRegistry);
@@ -6,9 +6,9 @@ import { Operation } from '@dxos/compute';
6
6
  import { Obj } from '@dxos/echo';
7
7
  import { invariant } from '@dxos/invariant';
8
8
 
9
- import { RestoreCard } from './definitions';
9
+ import { KanbanOperation } from '../types';
10
10
 
11
- const handler: Operation.WithHandler<typeof RestoreCard> = RestoreCard.pipe(
11
+ const handler: Operation.WithHandler<typeof KanbanOperation.RestoreCard> = KanbanOperation.RestoreCard.pipe(
12
12
  Operation.withHandler(({ card }) =>
13
13
  Effect.sync(() => {
14
14
  const db = Obj.getDatabase(card);
@@ -49,7 +49,7 @@ test.describe('Kanban MutableSchema', () => {
49
49
  expect(secondLabel).not.toBeNull();
50
50
 
51
51
  // Drag first item below the second item.
52
- await column.item(0).dragTo(column.item(1).locator, { x: 0, y: 200 });
52
+ await column.item(0).dragTo(column.item(1).locator, { x: 0, y: 200 }, 'bottom');
53
53
 
54
54
  // Item count should stay the same.
55
55
  await expect(column.items()).toHaveCount(countBefore);
@@ -71,8 +71,8 @@ test.describe('Kanban MutableSchema', () => {
71
71
  const draggedLabel = await col1.item(0).title().textContent();
72
72
  expect(draggedLabel).not.toBeNull();
73
73
 
74
- // Drop above first item. Kanban cards are taller; use larger negative y so we land in top half.
75
- await col1.item(0).dragTo(col2.item(0).locator, { x: 0, y: -30 });
74
+ // Drop above first item.
75
+ await col1.item(0).dragTo(col2.item(0).locator, { x: 0, y: -30 }, 'top');
76
76
 
77
77
  await expect(col1.items()).toHaveCount(col1CountBefore - 1);
78
78
  await expect(col2.items()).toHaveCount(col2CountBefore + 1);
package/src/plugin.ts ADDED
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+
7
+ import { meta } from './meta';
8
+
9
+ export const KanbanPlugin = Plugin.lazy(meta, () => import('#plugin'));
10
+
11
+ export { KanbanOperationHandlerSet } from './operations';
@@ -4,8 +4,7 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { Annotation, Obj, Ref, Type } from '@dxos/echo';
8
- import { View } from '@dxos/echo';
7
+ import { Annotation, Obj, Ref, Type, View } from '@dxos/echo';
9
8
  import { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/internal';
10
9
  import { ViewAnnotation } from '@dxos/schema';
11
10
 
@@ -1,4 +1,8 @@
1
+ //
1
2
  // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
2
6
 
3
7
  import * as Schema from 'effect/Schema';
4
8
 
@@ -3,6 +3,8 @@
3
3
  //
4
4
 
5
5
  export * from './constants';
6
- export * as Kanban from './Kanban';
7
6
  export * from './schema';
8
7
  export type * from './types';
8
+
9
+ export * as Kanban from './Kanban';
10
+ export * as KanbanOperation from './KanbanOperation';
@@ -5,8 +5,7 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import { afterEach, beforeEach, describe, test } from 'vitest';
7
7
 
8
- import { defineObjectMigration } from '@dxos/client/echo';
9
- import { Filter, JsonSchema, Obj, Query, Ref, Type, View } from '@dxos/echo';
8
+ import { Filter, JsonSchema, Migration, Obj, Query, Ref, Type, View } from '@dxos/echo';
10
9
  import { EchoTestBuilder } from '@dxos/echo-db/testing';
11
10
  import { ViewModel } from '@dxos/schema';
12
11
 
@@ -35,7 +34,7 @@ describe('Kanban migration v1 → v2', () => {
35
34
  await builder.close();
36
35
  });
37
36
 
38
- const migration = defineObjectMigration({
37
+ const migration = Migration.define({
39
38
  from: KanbanV1,
40
39
  to: Kanban,
41
40
  transform: async (from) => ({
@@ -4,7 +4,7 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { TypeInputOptionsAnnotation } from '@dxos/plugin-space/types';
7
+ import { TypeInputOptionsAnnotation } from '@dxos/plugin-space';
8
8
 
9
9
  /**
10
10
  * Kanban data model.
@@ -5,8 +5,7 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import { beforeEach, describe, test } from 'vitest';
7
7
 
8
- import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
9
- import { type View } from '@dxos/echo';
8
+ import { Filter, JsonSchema, Query, Type, type View } from '@dxos/echo';
10
9
  import { ObjectId } from '@dxos/keys';
11
10
  import { ViewModel } from '@dxos/schema';
12
11