@dxos/plugin-kanban 0.8.4-main.c85a9c8dae → 0.8.4-main.dfabb4ec29

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 (274) hide show
  1. package/dist/lib/neutral/KanbanContainer-QK6LNCYT.mjs +132 -0
  2. package/dist/lib/neutral/KanbanContainer-QK6LNCYT.mjs.map +7 -0
  3. package/dist/lib/neutral/KanbanPlugin.mjs +36 -0
  4. package/dist/lib/neutral/KanbanPlugin.mjs.map +7 -0
  5. package/dist/lib/neutral/KanbanPlugin.node.mjs +27 -0
  6. package/dist/lib/neutral/KanbanPlugin.node.mjs.map +7 -0
  7. package/dist/lib/{browser/KanbanViewEditor-IH5CJ6BW.mjs → neutral/KanbanSettings-G6M47NSK.mjs} +36 -19
  8. package/dist/lib/neutral/KanbanSettings-G6M47NSK.mjs.map +7 -0
  9. package/dist/lib/{browser/blueprint-definition-HFEKGFJK.mjs → neutral/blueprint-definition-FHVIEKTQ.mjs} +4 -6
  10. package/dist/lib/neutral/blueprint-definition-FHVIEKTQ.mjs.map +7 -0
  11. package/dist/lib/{browser → neutral}/blueprints/index.mjs +1 -1
  12. package/dist/lib/neutral/capabilities/index.mjs +19 -0
  13. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  14. package/dist/lib/neutral/chunk-E65AME5F.mjs +255 -0
  15. package/dist/lib/neutral/chunk-E65AME5F.mjs.map +7 -0
  16. package/dist/lib/neutral/chunk-KDPM77BD.mjs +21 -0
  17. package/dist/lib/neutral/chunk-KDPM77BD.mjs.map +7 -0
  18. package/dist/lib/neutral/chunk-Z7O5CETK.mjs +8 -0
  19. package/dist/lib/neutral/chunk-Z7O5CETK.mjs.map +7 -0
  20. package/dist/lib/{browser/chunk-TLYZSC7O.mjs → neutral/chunk-ZTQW5KQS.mjs} +3 -6
  21. package/dist/lib/neutral/chunk-ZTQW5KQS.mjs.map +7 -0
  22. package/dist/lib/{browser/KanbanContainer-BCXSJ6KS.mjs → neutral/components/index.mjs} +78 -139
  23. package/dist/lib/neutral/components/index.mjs.map +7 -0
  24. package/dist/lib/neutral/containers/index.mjs +11 -0
  25. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  26. package/dist/lib/neutral/create-object-DKBSI46K.mjs +40 -0
  27. package/dist/lib/neutral/create-object-DKBSI46K.mjs.map +7 -0
  28. package/dist/lib/neutral/delete-card-QKT6OXGP.mjs +24 -0
  29. package/dist/lib/neutral/delete-card-QKT6OXGP.mjs.map +7 -0
  30. package/dist/lib/neutral/delete-card-field-XQKM7ZXE.mjs +42 -0
  31. package/dist/lib/neutral/delete-card-field-XQKM7ZXE.mjs.map +7 -0
  32. package/dist/lib/{node-esm/chunk-CSL3HF2X.mjs → neutral/hooks/index.mjs} +128 -82
  33. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  34. package/dist/lib/neutral/index.mjs +34 -0
  35. package/dist/lib/neutral/meta.json +1 -0
  36. package/dist/lib/neutral/meta.mjs +8 -0
  37. package/dist/lib/neutral/migrations-4NS6H7U2.mjs +31 -0
  38. package/dist/lib/neutral/migrations-4NS6H7U2.mjs.map +7 -0
  39. package/dist/lib/neutral/operation-handler-B7IW6MXU.mjs +13 -0
  40. package/dist/lib/neutral/operation-handler-B7IW6MXU.mjs.map +7 -0
  41. package/dist/lib/neutral/operations/index.mjs +8 -0
  42. package/dist/lib/neutral/plugin.mjs +16 -0
  43. package/dist/lib/neutral/plugin.mjs.map +7 -0
  44. package/dist/lib/{browser/react-surface-FOMOGFVW.mjs → neutral/react-surface-6RVSCHMJ.mjs} +21 -27
  45. package/dist/lib/neutral/react-surface-6RVSCHMJ.mjs.map +7 -0
  46. package/dist/lib/neutral/restore-card-XW7AHMPO.mjs +21 -0
  47. package/dist/lib/neutral/restore-card-XW7AHMPO.mjs.map +7 -0
  48. package/dist/lib/neutral/restore-card-field-QOAUY3RJ.mjs +40 -0
  49. package/dist/lib/neutral/restore-card-field-QOAUY3RJ.mjs.map +7 -0
  50. package/dist/lib/neutral/testing/index.mjs +60 -0
  51. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  52. package/dist/lib/neutral/translations.mjs +44 -0
  53. package/dist/lib/neutral/translations.mjs.map +7 -0
  54. package/dist/lib/{browser → neutral}/types/index.mjs +7 -5
  55. package/dist/lib/neutral/undo-mappings-6CHW6BOF.mjs +42 -0
  56. package/dist/lib/neutral/undo-mappings-6CHW6BOF.mjs.map +7 -0
  57. package/dist/types/src/KanbanPlugin.d.ts +1 -0
  58. package/dist/types/src/KanbanPlugin.d.ts.map +1 -1
  59. package/dist/types/src/KanbanPlugin.node.d.ts +4 -0
  60. package/dist/types/src/KanbanPlugin.node.d.ts.map +1 -0
  61. package/dist/types/src/KanbanPlugin.test.d.ts +2 -0
  62. package/dist/types/src/KanbanPlugin.test.d.ts.map +1 -0
  63. package/dist/types/src/blueprints/kanban-blueprint.d.ts +2 -2
  64. package/dist/types/src/blueprints/kanban-blueprint.d.ts.map +1 -1
  65. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -0
  66. package/dist/types/src/capabilities/blueprint-definition.d.ts +6 -0
  67. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  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 +13 -3
  71. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  72. package/dist/types/src/capabilities/migrations.d.ts +5 -0
  73. package/dist/types/src/capabilities/migrations.d.ts.map +1 -0
  74. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  75. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  76. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  77. package/dist/types/src/capabilities/undo-mappings.d.ts +5 -0
  78. package/dist/types/src/capabilities/undo-mappings.d.ts.map +1 -0
  79. package/dist/types/src/components/KanbanBoard/KanbanBoard.d.ts +17 -38
  80. package/dist/types/src/components/KanbanBoard/KanbanBoard.d.ts.map +1 -1
  81. package/dist/types/src/components/KanbanBoard/KanbanBoard.stories.d.ts +48 -46
  82. package/dist/types/src/components/KanbanBoard/KanbanBoard.stories.d.ts.map +1 -1
  83. package/dist/types/src/components/KanbanBoard/KanbanCard.d.ts +2 -3
  84. package/dist/types/src/components/KanbanBoard/KanbanCard.d.ts.map +1 -1
  85. package/dist/types/src/components/KanbanBoard/KanbanColumn.d.ts +2 -3
  86. package/dist/types/src/components/KanbanBoard/KanbanColumn.d.ts.map +1 -1
  87. package/dist/types/src/components/KanbanBoard/context.d.ts +38 -0
  88. package/dist/types/src/components/KanbanBoard/context.d.ts.map +1 -0
  89. package/dist/types/src/containers/KanbanContainer/KanbanContainer.d.ts +4 -4
  90. package/dist/types/src/containers/KanbanContainer/KanbanContainer.d.ts.map +1 -1
  91. package/dist/types/src/containers/KanbanContainer/KanbanContainer.stories.d.ts +48 -46
  92. package/dist/types/src/containers/KanbanContainer/KanbanContainer.stories.d.ts.map +1 -1
  93. package/dist/types/src/containers/KanbanContainer/index.d.ts +1 -2
  94. package/dist/types/src/containers/KanbanContainer/index.d.ts.map +1 -1
  95. package/dist/types/src/containers/KanbanSettings/KanbanSettings.d.ts +13 -0
  96. package/dist/types/src/containers/KanbanSettings/KanbanSettings.d.ts.map +1 -0
  97. package/dist/types/src/containers/KanbanSettings/index.d.ts +2 -0
  98. package/dist/types/src/containers/KanbanSettings/index.d.ts.map +1 -0
  99. package/dist/types/src/containers/index.d.ts +1 -1
  100. package/dist/types/src/containers/index.d.ts.map +1 -1
  101. package/dist/types/src/hooks/index.d.ts +1 -0
  102. package/dist/types/src/hooks/index.d.ts.map +1 -1
  103. package/dist/types/src/hooks/useEchoChangeCallback.d.ts +1 -1
  104. package/dist/types/src/hooks/useEchoChangeCallback.d.ts.map +1 -1
  105. package/dist/types/src/hooks/useItemsProjection.d.ts +10 -0
  106. package/dist/types/src/hooks/useItemsProjection.d.ts.map +1 -0
  107. package/dist/types/src/hooks/useKanbanBoardModel.browser.test.d.ts +2 -0
  108. package/dist/types/src/hooks/useKanbanBoardModel.browser.test.d.ts.map +1 -0
  109. package/dist/types/src/hooks/useKanbanBoardModel.d.ts +1 -1
  110. package/dist/types/src/hooks/useKanbanBoardModel.d.ts.map +1 -1
  111. package/dist/types/src/hooks/useKanbanColumnEventHandler.d.ts +2 -2
  112. package/dist/types/src/hooks/useKanbanColumnEventHandler.d.ts.map +1 -1
  113. package/dist/types/src/hooks/useKanbanItemEventHandler.d.ts +2 -2
  114. package/dist/types/src/hooks/useKanbanItemEventHandler.d.ts.map +1 -1
  115. package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
  116. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  117. package/dist/types/src/index.d.ts +3 -1
  118. package/dist/types/src/index.d.ts.map +1 -1
  119. package/dist/types/src/operations/delete-card-field.d.ts +5 -0
  120. package/dist/types/src/operations/delete-card-field.d.ts.map +1 -0
  121. package/dist/types/src/operations/delete-card.d.ts +5 -0
  122. package/dist/types/src/operations/delete-card.d.ts.map +1 -0
  123. package/dist/types/src/operations/index.d.ts +3 -0
  124. package/dist/types/src/operations/index.d.ts.map +1 -0
  125. package/dist/types/src/operations/restore-card-field.d.ts +5 -0
  126. package/dist/types/src/operations/restore-card-field.d.ts.map +1 -0
  127. package/dist/types/src/operations/restore-card.d.ts +5 -0
  128. package/dist/types/src/operations/restore-card.d.ts.map +1 -0
  129. package/dist/types/src/playwright/board-manager.d.ts.map +1 -1
  130. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  131. package/dist/types/src/plugin.d.ts +4 -0
  132. package/dist/types/src/plugin.d.ts.map +1 -0
  133. package/dist/types/src/testing/KanbanCardTileSimple.d.ts +4 -2
  134. package/dist/types/src/testing/KanbanCardTileSimple.d.ts.map +1 -1
  135. package/dist/types/src/translations.d.ts +48 -46
  136. package/dist/types/src/translations.d.ts.map +1 -1
  137. package/dist/types/src/types/Kanban.d.ts +78 -6
  138. package/dist/types/src/types/Kanban.d.ts.map +1 -1
  139. package/dist/types/src/types/KanbanOperation.d.ts +52 -0
  140. package/dist/types/src/types/KanbanOperation.d.ts.map +1 -0
  141. package/dist/types/src/types/constants.d.ts +3 -3
  142. package/dist/types/src/types/constants.d.ts.map +1 -1
  143. package/dist/types/src/types/index.d.ts +2 -1
  144. package/dist/types/src/types/index.d.ts.map +1 -1
  145. package/dist/types/src/types/migrations.test.d.ts +2 -0
  146. package/dist/types/src/types/migrations.test.d.ts.map +1 -0
  147. package/dist/types/src/types/schema.d.ts +15 -104
  148. package/dist/types/src/types/schema.d.ts.map +1 -1
  149. package/dist/types/src/types/types.d.ts +2 -2
  150. package/dist/types/src/util/arrangement.d.ts +8 -4
  151. package/dist/types/src/util/arrangement.d.ts.map +1 -1
  152. package/dist/types/tsconfig.tsbuildinfo +1 -1
  153. package/package.json +108 -65
  154. package/src/KanbanPlugin.node.ts +21 -0
  155. package/src/KanbanPlugin.test.ts +31 -0
  156. package/src/KanbanPlugin.tsx +21 -29
  157. package/src/blueprints/kanban-blueprint.ts +4 -8
  158. package/src/capabilities/{artifact-definition/artifact-definition.ts → artifact-definition.ts} +4 -4
  159. package/src/capabilities/{blueprint-definition/blueprint-definition.ts → blueprint-definition.ts} +3 -1
  160. package/src/capabilities/create-object.ts +40 -0
  161. package/src/capabilities/index.ts +13 -3
  162. package/src/capabilities/migrations.ts +35 -0
  163. package/src/capabilities/operation-handler.ts +14 -0
  164. package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +22 -13
  165. package/src/capabilities/undo-mappings.ts +34 -0
  166. package/src/components/KanbanBoard/KanbanBoard.stories.tsx +20 -16
  167. package/src/components/KanbanBoard/KanbanBoard.tsx +44 -64
  168. package/src/components/KanbanBoard/KanbanCard.tsx +77 -63
  169. package/src/components/KanbanBoard/KanbanColumn.tsx +19 -16
  170. package/src/components/KanbanBoard/context.ts +54 -0
  171. package/src/containers/KanbanContainer/KanbanContainer.stories.tsx +44 -30
  172. package/src/containers/KanbanContainer/KanbanContainer.tsx +118 -27
  173. package/src/containers/KanbanContainer/index.ts +1 -3
  174. package/src/containers/KanbanSettings/KanbanSettings.tsx +94 -0
  175. package/src/containers/KanbanSettings/index.ts +5 -0
  176. package/src/containers/index.ts +1 -1
  177. package/src/hooks/index.ts +1 -0
  178. package/src/hooks/useEchoChangeCallback.ts +3 -3
  179. package/src/hooks/useItemsProjection.ts +44 -0
  180. package/src/hooks/{useKanbanBoardModel.test.ts → useKanbanBoardModel.browser.test.ts} +5 -5
  181. package/src/hooks/useKanbanBoardModel.ts +20 -6
  182. package/src/hooks/useKanbanColumnEventHandler.ts +1 -1
  183. package/src/hooks/useKanbanItemEventHandler.ts +1 -1
  184. package/src/hooks/useProjectionModel.ts +3 -3
  185. package/src/index.ts +3 -2
  186. package/src/meta.ts +1 -1
  187. package/src/operations/delete-card-field.ts +47 -0
  188. package/src/operations/delete-card.ts +23 -0
  189. package/src/operations/index.ts +10 -0
  190. package/src/operations/restore-card-field.ts +41 -0
  191. package/src/operations/restore-card.ts +21 -0
  192. package/src/playwright/smoke.spec.ts +3 -3
  193. package/src/plugin.ts +11 -0
  194. package/src/testing/KanbanCardTileSimple.tsx +25 -19
  195. package/src/translations.ts +26 -25
  196. package/src/types/Kanban.ts +98 -14
  197. package/src/types/KanbanOperation.ts +67 -0
  198. package/src/types/index.ts +3 -1
  199. package/src/types/migrations.test.ts +83 -0
  200. package/src/types/schema.ts +20 -78
  201. package/src/types/types.ts +2 -2
  202. package/src/util/arrangement.test.ts +11 -1
  203. package/src/util/arrangement.ts +25 -15
  204. package/dist/lib/browser/KanbanContainer-BCXSJ6KS.mjs.map +0 -7
  205. package/dist/lib/browser/KanbanViewEditor-IH5CJ6BW.mjs.map +0 -7
  206. package/dist/lib/browser/blueprint-definition-HFEKGFJK.mjs.map +0 -7
  207. package/dist/lib/browser/chunk-QSWCFMEB.mjs +0 -385
  208. package/dist/lib/browser/chunk-QSWCFMEB.mjs.map +0 -7
  209. package/dist/lib/browser/chunk-RNFIFE2P.mjs +0 -213
  210. package/dist/lib/browser/chunk-RNFIFE2P.mjs.map +0 -7
  211. package/dist/lib/browser/chunk-TLYZSC7O.mjs.map +0 -7
  212. package/dist/lib/browser/index.mjs +0 -101
  213. package/dist/lib/browser/index.mjs.map +0 -7
  214. package/dist/lib/browser/meta.json +0 -1
  215. package/dist/lib/browser/operation-resolver-BRA2OHUE.mjs +0 -162
  216. package/dist/lib/browser/operation-resolver-BRA2OHUE.mjs.map +0 -7
  217. package/dist/lib/browser/react-surface-FOMOGFVW.mjs.map +0 -7
  218. package/dist/lib/node-esm/KanbanContainer-EHRTLE7M.mjs +0 -305
  219. package/dist/lib/node-esm/KanbanContainer-EHRTLE7M.mjs.map +0 -7
  220. package/dist/lib/node-esm/KanbanViewEditor-WDACFC35.mjs +0 -67
  221. package/dist/lib/node-esm/KanbanViewEditor-WDACFC35.mjs.map +0 -7
  222. package/dist/lib/node-esm/blueprint-definition-NARBX32U.mjs +0 -18
  223. package/dist/lib/node-esm/blueprint-definition-NARBX32U.mjs.map +0 -7
  224. package/dist/lib/node-esm/blueprints/index.mjs +0 -9
  225. package/dist/lib/node-esm/chunk-4AWDHQVY.mjs +0 -214
  226. package/dist/lib/node-esm/chunk-4AWDHQVY.mjs.map +0 -7
  227. package/dist/lib/node-esm/chunk-CSL3HF2X.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  229. package/dist/lib/node-esm/chunk-X3UJUQIV.mjs +0 -31
  230. package/dist/lib/node-esm/chunk-X3UJUQIV.mjs.map +0 -7
  231. package/dist/lib/node-esm/index.mjs +0 -102
  232. package/dist/lib/node-esm/index.mjs.map +0 -7
  233. package/dist/lib/node-esm/meta.json +0 -1
  234. package/dist/lib/node-esm/operation-resolver-UEQ64LCN.mjs +0 -163
  235. package/dist/lib/node-esm/operation-resolver-UEQ64LCN.mjs.map +0 -7
  236. package/dist/lib/node-esm/react-surface-AITKFRBE.mjs +0 -100
  237. package/dist/lib/node-esm/react-surface-AITKFRBE.mjs.map +0 -7
  238. package/dist/lib/node-esm/types/index.mjs +0 -21
  239. package/dist/types/src/capabilities/artifact-definition/artifact-definition.d.ts.map +0 -1
  240. package/dist/types/src/capabilities/artifact-definition/index.d.ts +0 -3
  241. package/dist/types/src/capabilities/artifact-definition/index.d.ts.map +0 -1
  242. package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts +0 -6
  243. package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts.map +0 -1
  244. package/dist/types/src/capabilities/blueprint-definition/index.d.ts +0 -3
  245. package/dist/types/src/capabilities/blueprint-definition/index.d.ts.map +0 -1
  246. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  247. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  248. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  249. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  250. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  251. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  252. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  253. package/dist/types/src/containers/KanbanViewEditor/KanbanViewEditor.d.ts +0 -6
  254. package/dist/types/src/containers/KanbanViewEditor/KanbanViewEditor.d.ts.map +0 -1
  255. package/dist/types/src/containers/KanbanViewEditor/index.d.ts +0 -3
  256. package/dist/types/src/containers/KanbanViewEditor/index.d.ts.map +0 -1
  257. package/dist/types/src/hooks/useKanbanBoardModel.test.d.ts +0 -2
  258. package/dist/types/src/hooks/useKanbanBoardModel.test.d.ts.map +0 -1
  259. package/src/capabilities/artifact-definition/index.ts +0 -7
  260. package/src/capabilities/blueprint-definition/index.ts +0 -7
  261. package/src/capabilities/operation-resolver/index.ts +0 -7
  262. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -133
  263. package/src/capabilities/react-surface/index.ts +0 -7
  264. package/src/containers/KanbanViewEditor/KanbanViewEditor.tsx +0 -63
  265. package/src/containers/KanbanViewEditor/index.ts +0 -7
  266. /package/dist/lib/{browser → neutral}/blueprints/index.mjs.map +0 -0
  267. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  268. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
  269. /package/dist/lib/{browser/types → neutral}/index.mjs.map +0 -0
  270. /package/dist/lib/{node-esm/blueprints/index.mjs.map → neutral/meta.mjs.map} +0 -0
  271. /package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
  272. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/types/index.mjs.map} +0 -0
  273. /package/dist/types/src/capabilities/{artifact-definition/artifact-definition.d.ts → artifact-definition.d.ts} +0 -0
  274. /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
@@ -10,36 +10,41 @@ import { expect, waitFor, within } from 'storybook/test';
10
10
 
11
11
  import { withPluginManager } from '@dxos/app-framework/testing';
12
12
  import { Surface } from '@dxos/app-framework/ui';
13
+ import { AppSurface } from '@dxos/app-toolkit/ui';
13
14
  import { Obj, type QueryAST, Type } from '@dxos/echo';
14
15
  import { View } from '@dxos/echo';
15
16
  import { type Mutable } from '@dxos/echo/internal';
16
17
  import { invariant } from '@dxos/invariant';
17
- import { ClientPlugin } from '@dxos/plugin-client';
18
- import { PreviewPlugin } from '@dxos/plugin-preview';
19
- import { SpacePlugin } from '@dxos/plugin-space';
18
+ // `/plugin` entrypoints used here for the same reason as `corePlugins()` —
19
+ // see `@dxos/plugin-testing/src/core.ts` for the rationale.
20
+ import { ClientPlugin } from '@dxos/plugin-client/testing';
21
+ import { initializeIdentity } from '@dxos/plugin-client/testing';
22
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
23
+ import { SpacePlugin } from '@dxos/plugin-space/testing';
20
24
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
21
- import { faker } from '@dxos/random';
25
+ import { random } from '@dxos/random';
22
26
  import { Filter, type Space, useQuery, useSchema, useSpaces } from '@dxos/react-client/echo';
23
- import { withLayout, withTheme } from '@dxos/react-ui/testing';
24
27
  import { ViewEditor } from '@dxos/react-ui-form';
25
- import { JsonFilter } from '@dxos/react-ui-syntax-highlighter';
28
+ import { Syntax } from '@dxos/react-ui-syntax-highlighter';
29
+ import { withLayout } from '@dxos/react-ui/testing';
26
30
  import { ViewModel, getTypenameFromQuery } from '@dxos/schema';
27
31
  // TODO(wittjosiah): Replace with echo/testing.
28
32
  import { Organization, Person } from '@dxos/types';
29
33
 
30
- import { useProjectionModel } from '../../hooks';
34
+ import { useProjectionModel } from '#hooks';
35
+ import { translations } from '#translations';
36
+ import { Kanban } from '#types';
37
+
31
38
  import { KanbanPlugin } from '../../KanbanPlugin';
32
- import { translations } from '../../translations';
33
- import { Kanban } from '../../types';
34
39
 
35
- faker.seed(0);
40
+ random.seed(0);
36
41
 
37
42
  const createOrg = (status?: Organization.Organization['status']) => ({
38
- name: faker.commerce.productName(),
39
- description: faker.lorem.paragraph(),
40
- image: faker.image.url(),
41
- website: faker.internet.url(),
42
- status: (status ?? faker.helpers.arrayElement(Organization.StatusOptions).id) as Organization.Organization['status'],
43
+ name: random.commerce.productName(),
44
+ description: random.lorem.paragraph(),
45
+ image: random.image.url(),
46
+ website: random.internet.url(),
47
+ status: (status ?? random.helpers.arrayElement(Organization.StatusOptions).id) as Organization.Organization['status'],
43
48
  });
44
49
 
45
50
  //
@@ -63,7 +68,7 @@ const withKanbanPlugins = ({ types = [], onSpaceCreated }: ClientSetupOptions):
63
68
  types: [...types, View.View, Kanban.Kanban],
64
69
  onClientInitialized: ({ client }) =>
65
70
  Effect.gen(function* () {
66
- yield* Effect.promise(() => client.halo.createIdentity());
71
+ yield* initializeIdentity(client);
67
72
  const space = yield* Effect.promise(() => client.spaces.create());
68
73
  yield* Effect.promise(() => space.waitUntilReady());
69
74
  yield* Effect.promise(() => onSpaceCreated?.(space) ?? Promise.resolve());
@@ -78,31 +83,33 @@ const withKanbanPlugins = ({ types = [], onSpaceCreated }: ClientSetupOptions):
78
83
 
79
84
  /**
80
85
  * Renders the first Kanban in the space via Surface (resolves to KanbanContainer),
81
- * with a sidebar containing ViewEditor and JsonFilter.
86
+ * with a sidebar containing ViewEditor and Json filter.
82
87
  */
83
88
  const DefaultComponent = () => {
84
89
  const registry = useContext(RegistryContext);
85
90
  const spaces = useSpaces();
86
91
  const space = spaces[spaces.length - 1];
87
92
  const [kanban] = useQuery(space?.db, Filter.type(Kanban.Kanban));
88
- const typename = kanban?.view.target?.query ? getTypenameFromQuery(kanban.view.target.query.ast) : undefined;
93
+ const viewRef = kanban && kanban.spec.kind === 'view' ? kanban.spec.view : undefined;
94
+ const view = viewRef?.target;
95
+ const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
89
96
  const schema = useSchema(space?.db, typename);
90
97
  const projection = useProjectionModel(schema, kanban, registry);
91
98
 
92
- const data = useMemo(() => (kanban ? { subject: kanban } : {}), [kanban]);
99
+ const data = useMemo(() => (kanban ? { subject: kanban, attendableId: 'story' } : undefined), [kanban]);
93
100
 
94
101
  const handleUpdateQuery = useCallback(
95
102
  (newQuery: QueryAST.Query) => {
96
103
  invariant(schema);
97
- invariant(kanban?.view.target);
104
+ invariant(view);
98
105
  if (Type.isMutable(schema)) {
99
106
  schema.updateTypename(getTypenameFromQuery(newQuery));
100
107
  }
101
- Obj.change(kanban.view.target, (view) => {
108
+ Obj.update(view, (view) => {
102
109
  view.query.ast = newQuery as Mutable<QueryAST.Query>;
103
110
  });
104
111
  },
105
- [kanban, schema],
112
+ [view, schema],
106
113
  );
107
114
 
108
115
  const handleDeleteField = useCallback(
@@ -114,22 +121,29 @@ const DefaultComponent = () => {
114
121
  [schema, projection],
115
122
  );
116
123
 
117
- if (!schema || !kanban?.view.target) {
124
+ if (!schema || !view) {
118
125
  return null;
119
126
  }
120
127
 
121
128
  return (
122
129
  <div className='grow grid grid-cols-[1fr_350px] overflow-hidden h-full w-full'>
123
- <Surface.Surface role='article' data={data} limit={1} />
130
+ <Surface.Surface type={AppSurface.Article} data={data} limit={1} />
124
131
  <div className='flex flex-col h-full overflow-hidden border-l border-separator'>
125
132
  <ViewEditor
126
133
  registry={space?.db.schemaRegistry}
127
134
  schema={schema}
128
- view={kanban.view.target}
135
+ view={view}
129
136
  onQueryChanged={handleUpdateQuery}
130
137
  onDelete={schema && Type.isMutable(schema) ? handleDeleteField : undefined}
131
138
  />
132
- <JsonFilter data={{ view: kanban.view.target, schema }} classNames='text-xs' />
139
+ <Syntax.Root data={{ view, schema }}>
140
+ <Syntax.Content>
141
+ <Syntax.Filter />
142
+ <Syntax.Viewport>
143
+ <Syntax.Code classNames='text-xs' />
144
+ </Syntax.Viewport>
145
+ </Syntax.Content>
146
+ </Syntax.Root>
133
147
  </div>
134
148
  </div>
135
149
  );
@@ -143,7 +157,7 @@ const meta = {
143
157
  title: 'plugins/plugin-kanban/containers/Kanban',
144
158
  component: DefaultComponent,
145
159
  render: () => <DefaultComponent />,
146
- decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
160
+ decorators: [withLayout({ layout: 'fullscreen' })],
147
161
  parameters: {
148
162
  layout: 'fullscreen',
149
163
  translations,
@@ -182,9 +196,9 @@ export const Default: Story = {
182
196
 
183
197
  // Wait for the kanban columns to render by finding the status tags.
184
198
  // Organization.StatusOptions: prospect, qualified, active, commit, reject.
185
- const activeTag = await canvas.findByText('Active', undefined, { timeout: 30_000 });
186
- const prospectTag = await canvas.findByText('Prospect', undefined, { timeout: 10_000 });
187
- const commitTag = await canvas.findByText('Commit', undefined, { timeout: 10_000 });
199
+ const activeTag = await canvas.findByText('Active', undefined, { timeout: 12_000 });
200
+ const prospectTag = await canvas.findByText('Prospect', undefined, { timeout: 12_000 });
201
+ const commitTag = await canvas.findByText('Commit', undefined, { timeout: 12_000 });
188
202
 
189
203
  // Verify all expected columns are rendered.
190
204
  await expect(activeTag).toBeTruthy();
@@ -2,31 +2,44 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { RegistryContext } from '@effect-atom/atom-react';
5
+ import { Atom, RegistryContext } from '@effect-atom/atom-react';
6
6
  import React, { useCallback, useContext, useMemo } from 'react';
7
7
 
8
8
  import { useCapabilities, useOperationInvoker } from '@dxos/app-framework/ui';
9
9
  import { AppCapabilities } from '@dxos/app-toolkit';
10
- import { type SurfaceComponentProps } from '@dxos/app-toolkit/ui';
11
- import { Filter, Obj, Type } from '@dxos/echo';
12
- import { AtomQuery } from '@dxos/echo-atom';
10
+ import { type AppSurface } from '@dxos/app-toolkit/ui';
11
+ import { Filter, Obj, Query, type Ref, Type } from '@dxos/echo';
12
+ import { AtomObj, AtomQuery } from '@dxos/echo-atom';
13
13
  import { useObject, useSchema } from '@dxos/react-client/echo';
14
- import { Panel } from '@dxos/react-ui';
15
- import { getTypenameFromQuery } from '@dxos/schema';
14
+ import { Panel, Toolbar } from '@dxos/react-ui';
15
+ import { getTagFromQuery, getTypenameFromQuery } from '@dxos/schema';
16
16
 
17
- import { KanbanBoard } from '../../components';
18
- import { useEchoChangeCallback, useProjectionModel } from '../../hooks';
19
- import { type Kanban, KanbanOperation } from '../../types';
17
+ import { KanbanBoard } from '#components';
18
+ import { useEchoChangeCallback, useItemsProjection, useProjectionModel } from '#hooks';
19
+ import { KanbanOperation } from '#types';
20
+ import { Kanban } from '#types';
20
21
 
21
- export type KanbanContainerProps = SurfaceComponentProps<Kanban.Kanban>;
22
+ export type KanbanContainerProps = AppSurface.ObjectArticleProps<Kanban.Kanban>;
22
23
 
23
- export const KanbanContainer = ({ role, subject: object }: KanbanContainerProps) => {
24
+ export const KanbanContainer = (props: KanbanContainerProps) => {
25
+ // Branch on `kanban.spec.kind`: view-variant runs a typename query through
26
+ // `useProjectionModel`; items-variant dereferences `kanban.spec.items` and
27
+ // uses a stub projection from `useItemsProjection`.
28
+ return Kanban.isKanbanItems(props.subject) ? (
29
+ <ItemsKanbanContainer {...props} subject={props.subject} />
30
+ ) : (
31
+ <ViewKanbanContainer {...props} />
32
+ );
33
+ };
34
+
35
+ const ViewKanbanContainer = ({ role, subject: object }: KanbanContainerProps) => {
24
36
  const registry = useContext(RegistryContext);
25
37
  const schemas = useCapabilities(AppCapabilities.Schema);
26
38
  const db = Obj.getDatabase(object);
27
39
  const { invokePromise } = useOperationInvoker();
28
- const [view] = useObject(object.view);
40
+ const [view] = useObject(object.spec.kind === 'view' ? object.spec.view : undefined);
29
41
  const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
42
+ const tag = view?.query ? getTagFromQuery(view.query.ast) : undefined;
30
43
 
31
44
  const schemaFromDb = useSchema(db, typename);
32
45
  const cardSchema = useMemo(
@@ -34,10 +47,14 @@ export const KanbanContainer = ({ role, subject: object }: KanbanContainerProps)
34
47
  [schemaFromDb, schemas, typename],
35
48
  );
36
49
 
37
- const items = useMemo(
38
- () => (db ? AtomQuery.make(db, cardSchema ? Filter.type(cardSchema) : Filter.nothing()) : null),
39
- [db, cardSchema],
40
- );
50
+ const items = useMemo(() => {
51
+ if (!db) {
52
+ return null;
53
+ }
54
+ const baseFilter = cardSchema ? Filter.type(cardSchema) : Filter.nothing();
55
+ const query = tag ? Query.select(baseFilter).select(Filter.tag(tag)) : Query.select(baseFilter);
56
+ return AtomQuery.make(db, query);
57
+ }, [db, cardSchema, tag]);
41
58
 
42
59
  const projection = useProjectionModel(cardSchema, object, registry);
43
60
  const change = useEchoChangeCallback(object);
@@ -70,18 +87,92 @@ export const KanbanContainer = ({ role, subject: object }: KanbanContainerProps)
70
87
 
71
88
  return (
72
89
  <Panel.Root role={role}>
73
- <Panel.Content asChild>
74
- <KanbanBoard.Root
75
- kanban={object}
76
- projection={projection}
77
- items={items}
78
- change={change}
79
- onCardAdd={handleCardAdd}
80
- onCardRemove={handleCardRemove}
81
- >
90
+ <Panel.Toolbar asChild>
91
+ <Toolbar.Root />
92
+ </Panel.Toolbar>
93
+ <KanbanBoard.Root
94
+ kanban={object}
95
+ projection={projection}
96
+ items={items}
97
+ change={change}
98
+ onCardAdd={handleCardAdd}
99
+ onCardRemove={handleCardRemove}
100
+ >
101
+ <Panel.Content asChild>
102
+ <KanbanBoard.Content />
103
+ </Panel.Content>
104
+ </KanbanBoard.Root>
105
+ </Panel.Root>
106
+ );
107
+ };
108
+
109
+ type ItemsKanbanContainerProps = Omit<KanbanContainerProps, 'subject'> & { subject: Kanban.KanbanItems };
110
+
111
+ const ItemsKanbanContainer = ({ role, subject: object }: ItemsKanbanContainerProps) => {
112
+ const db = Obj.getDatabase(object);
113
+ const projection = useItemsProjection(object);
114
+ const change = useEchoChangeCallback(object);
115
+
116
+ // TODO(wittjosiah): pass refs (not loaded objects) through to the kanban
117
+ // board and let `KanbanCard` subscribe to its own ref via `useObject`.
118
+ // Today this atom subscribes to *every* item — any one changing causes the
119
+ // container (and the model's per-column atoms) to recompute. With cards
120
+ // subscribing themselves, the container only needs the refs and the
121
+ // per-card render is independent. Requires:
122
+ // - `KanbanCard` to accept `Ref<Obj.Unknown>` as `data` and call
123
+ // `useObject(ref)` internally.
124
+ // - The model to handle a ref-bearing item shape (id from
125
+ // `ref.dxn.asEchoDXN()?.echoId`) and use arrangement-only ordering
126
+ // for items-variant (no pivot-value fallback, since refs don't expose
127
+ // the pivot field without loading).
128
+ // - `Mosaic.isItem` to accept the ref wrapper alongside `Obj.isObject`.
129
+ const itemsAtom = useMemo(
130
+ () =>
131
+ Atom.make((get) => {
132
+ const out: Obj.Unknown[] = [];
133
+ for (const ref of object.spec.items as ReadonlyArray<Ref.Ref<Obj.Unknown>>) {
134
+ const target = get(AtomObj.make(ref));
135
+ if (target == null) {
136
+ continue;
137
+ }
138
+ // Drop soft-deleted cards (e.g. Trello-closed cards). The ref
139
+ // stays in `spec.items` so arrangement is preserved, but the card
140
+ // shouldn't render.
141
+ if (Obj.isDeleted(target)) {
142
+ continue;
143
+ }
144
+ out.push(target as unknown as Obj.Unknown);
145
+ }
146
+ return out;
147
+ }),
148
+ [object.spec.items],
149
+ );
150
+
151
+ const handleCardRemove = useCallback(() => undefined, []);
152
+
153
+ if (!object || !db || !change) {
154
+ return null;
155
+ }
156
+
157
+ // TODO(wittjosiah): wire `onCardAdd` to the create-object flow so
158
+ // users can add items directly from the kanban (currently the column's
159
+ // "+" button is hidden because `onCardAdd` is undefined).
160
+ return (
161
+ <Panel.Root role={role}>
162
+ <Panel.Toolbar asChild>
163
+ <Toolbar.Root />
164
+ </Panel.Toolbar>
165
+ <KanbanBoard.Root
166
+ kanban={object}
167
+ projection={projection}
168
+ items={itemsAtom}
169
+ change={change}
170
+ onCardRemove={handleCardRemove}
171
+ >
172
+ <Panel.Content asChild>
82
173
  <KanbanBoard.Content />
83
- </KanbanBoard.Root>
84
- </Panel.Content>
174
+ </Panel.Content>
175
+ </KanbanBoard.Root>
85
176
  </Panel.Root>
86
177
  );
87
178
  };
@@ -2,6 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { KanbanContainer } from './KanbanContainer';
6
-
7
- export default KanbanContainer;
5
+ export { KanbanContainer as default } from './KanbanContainer';
@@ -0,0 +1,94 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { RegistryContext } from '@effect-atom/atom-react';
6
+ import React, { useCallback, useContext, useMemo } from 'react';
7
+
8
+ import { type AppSurface } from '@dxos/app-toolkit/ui';
9
+ import { Obj } from '@dxos/echo';
10
+ import { Format } from '@dxos/echo/internal';
11
+ import { useObject, useSchema } from '@dxos/react-client/echo';
12
+ import { Form, type FormFieldMap, SelectField } from '@dxos/react-ui-form';
13
+ import { getTypenameFromQuery } from '@dxos/schema';
14
+
15
+ import { useProjectionModel } from '#hooks';
16
+ import { type Kanban, KanbanSettingsSchema, KanbanViewSettingsSchema, UNCATEGORIZED_VALUE } from '#types';
17
+
18
+ export type KanbanSettingsProps = AppSurface.ObjectPropertiesProps<Kanban.Kanban>;
19
+
20
+ /**
21
+ * Settings panel for a Kanban. Renders fields common to every kanban
22
+ * (currently the "Hide uncategorized column" toggle); for view-variant
23
+ * kanbans an additional "Column field" picker drives the View's pivot
24
+ * field. Items-variant kanbans use a hardcoded `spec.pivotField`, so that
25
+ * field is omitted there.
26
+ */
27
+ export const KanbanSettings = ({ subject: object }: KanbanSettingsProps) => {
28
+ const registry = useContext(RegistryContext);
29
+ const db = Obj.getDatabase(object);
30
+ const isView = object.spec.kind === 'view';
31
+ const [view, updateView] = useObject(object.spec.kind === 'view' ? object.spec.view : undefined);
32
+ const [, updateKanban] = useObject(object);
33
+ const currentTypename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
34
+ const schema = useSchema(db, currentTypename);
35
+ const projection = useProjectionModel(schema, object, registry);
36
+
37
+ const fieldProjections = projection?.getFieldProjections() ?? [];
38
+ const selectFields = useMemo(
39
+ () =>
40
+ fieldProjections
41
+ .filter((field) => field.props.format === Format.TypeFormat.SingleSelect)
42
+ .map(({ field }) => ({ value: field.id, label: field.path })),
43
+ [fieldProjections],
44
+ );
45
+
46
+ const hideUncategorized = object.arrangement.columns[UNCATEGORIZED_VALUE]?.hidden ?? false;
47
+
48
+ const handleValuesChanged = useCallback(
49
+ (values: Partial<{ columnFieldId: string; hideUncategorized: boolean }>) => {
50
+ if (isView && values.columnFieldId != null) {
51
+ updateView((view) => {
52
+ view.projection.pivotFieldId = values.columnFieldId!;
53
+ });
54
+ }
55
+ if (values.hideUncategorized !== undefined) {
56
+ updateKanban((kanban) => {
57
+ const existing = kanban.arrangement.columns[UNCATEGORIZED_VALUE];
58
+ if (existing) {
59
+ existing.hidden = values.hideUncategorized;
60
+ } else {
61
+ kanban.arrangement.columns[UNCATEGORIZED_VALUE] = {
62
+ ids: [],
63
+ hidden: values.hideUncategorized,
64
+ };
65
+ }
66
+ });
67
+ }
68
+ },
69
+ [isView, updateView, updateKanban],
70
+ );
71
+
72
+ const initialValues = useMemo(
73
+ () => ({
74
+ ...(isView ? { columnFieldId: view?.projection.pivotFieldId } : {}),
75
+ hideUncategorized,
76
+ }),
77
+ [isView, view?.projection.pivotFieldId, hideUncategorized],
78
+ );
79
+
80
+ const fieldMap: FormFieldMap = useMemo(
81
+ () => ({ columnFieldId: (props) => <SelectField {...props} options={selectFields} /> }),
82
+ [selectFields],
83
+ );
84
+
85
+ // Schema is picked by `kanban.spec.kind` — they have different shapes,
86
+ // so cast for `Form.Root`'s single-schema prop.
87
+ const settingsSchema = (isView ? KanbanViewSettingsSchema : KanbanSettingsSchema) as any;
88
+
89
+ return (
90
+ <Form.Root schema={settingsSchema} values={initialValues} fieldMap={fieldMap} onValuesChanged={handleValuesChanged}>
91
+ <Form.FieldSet />
92
+ </Form.Root>
93
+ );
94
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export { KanbanSettings as default } from './KanbanSettings';
@@ -5,4 +5,4 @@
5
5
  import { type ComponentType, lazy } from 'react';
6
6
 
7
7
  export const KanbanContainer: ComponentType<any> = lazy(() => import('./KanbanContainer'));
8
- export const KanbanViewEditor: ComponentType<any> = lazy(() => import('./KanbanViewEditor'));
8
+ export const KanbanSettings: ComponentType<any> = lazy(() => import('./KanbanSettings'));
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './useEchoChangeCallback';
6
+ export * from './useItemsProjection';
6
7
  export * from './useKanbanBoardModel';
7
8
  export * from './useKanbanColumnEventHandler';
8
9
  export * from './useKanbanItemEventHandler';
@@ -6,16 +6,16 @@ import { useMemo } from 'react';
6
6
 
7
7
  import { Obj } from '@dxos/echo';
8
8
 
9
- import { type Kanban, type KanbanChangeCallback } from '../types';
9
+ import { type Kanban, type KanbanChangeCallback } from '#types';
10
10
 
11
11
  /**
12
12
  * Creates a change callback for ECHO-backed kanban and items (plain function, no hooks).
13
13
  * Use this when the kanban and items are stored in the ECHO database.
14
14
  */
15
15
  export const createEchoChangeCallback = <T extends Obj.Unknown>(kanban: Kanban.Kanban): KanbanChangeCallback<T> => ({
16
- kanban: (mutate) => Obj.change(kanban, (kanban) => mutate(kanban)),
16
+ kanban: (mutate) => Obj.update(kanban, (kanban) => mutate(kanban)),
17
17
  setItemField: (item, field, value) => {
18
- Obj.change(item, (item: any) => {
18
+ Obj.update(item, (item: any) => {
19
19
  item[field] = value;
20
20
  });
21
21
  },
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Atom } from '@effect-atom/atom-react';
6
+ import { useMemo } from 'react';
7
+
8
+ import type { ProjectionModel } from '@dxos/schema';
9
+
10
+ import { type Kanban } from '#types';
11
+
12
+ /**
13
+ * Minimal `ProjectionModel` for `spec.kind === 'items'` (no View). Supplies `pivotField`
14
+ * and column options from `arrangement.columns` keys—written by sync so columns exist
15
+ * before refs hydrate. Stubs `getFieldProjections` / `getHiddenProperties` for shared
16
+ * board/card UI; hides the pivot on the card body (column shows it); Expando cards render title only.
17
+ */
18
+ export const useItemsProjection = (kanban: Kanban.KanbanItems): ProjectionModel => {
19
+ return useMemo(() => {
20
+ const pivotField = kanban.spec.pivotField;
21
+
22
+ const optionIds = Object.keys(kanban.arrangement?.columns ?? {});
23
+ const options = optionIds.map((id) => ({ id, title: id, color: 'neutral' as const }));
24
+
25
+ const fieldProjection: any = {
26
+ field: { id: pivotField, path: pivotField },
27
+ props: { property: pivotField, options },
28
+ };
29
+
30
+ const fields = Atom.make(() => [fieldProjection.field]);
31
+
32
+ const stub: Pick<ProjectionModel, 'tryGetFieldProjection' | 'getFieldProjections' | 'getHiddenProperties'> & {
33
+ fields: typeof fields;
34
+ } = {
35
+ fields,
36
+ tryGetFieldProjection: (id: string) => (id === pivotField ? fieldProjection : undefined),
37
+ getFieldProjections: () => [],
38
+ getHiddenProperties: () => [pivotField],
39
+ };
40
+
41
+ // TODO(wittjosiah): Refactor ProjectionModel to be an interface that we can fulfill.
42
+ return stub as unknown as ProjectionModel;
43
+ }, [kanban.arrangement?.columns, kanban.spec.pivotField]);
44
+ };
@@ -13,7 +13,7 @@ import { Format, FormatAnnotation, PropertyMetaAnnotationId } from '@dxos/echo/i
13
13
  import { ObjectId } from '@dxos/keys';
14
14
  import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
15
15
 
16
- import { Kanban } from '../types';
16
+ import { Kanban } from '#types';
17
17
 
18
18
  import { useKanbanBoardModel } from './useKanbanBoardModel';
19
19
 
@@ -38,7 +38,7 @@ const KanbanTaskSchema = Schema.Struct({
38
38
  ),
39
39
  }).pipe(
40
40
  Type.object({
41
- typename: 'example.com/type/KanbanTask',
41
+ typename: 'com.example.type.kanban-task',
42
42
  version: '0.1.0',
43
43
  }),
44
44
  );
@@ -146,7 +146,7 @@ describe('useKanbanBoardModel', () => {
146
146
  expect(orderBefore).toEqual(['__uncategorized__', 'a', 'b']);
147
147
 
148
148
  act(() => {
149
- Obj.change(kanban, (kanban) => {
149
+ Obj.update(kanban, (kanban) => {
150
150
  kanban.arrangement.order = ['b', 'a'];
151
151
  });
152
152
  });
@@ -187,7 +187,7 @@ describe('useKanbanBoardModel', () => {
187
187
  });
188
188
 
189
189
  act(() => {
190
- Obj.change(kanban, (kanban) => {
190
+ Obj.update(kanban, (kanban) => {
191
191
  kanban.arrangement.columns['a'] = {
192
192
  ids: [item3.id, item1.id, item2.id],
193
193
  };
@@ -225,7 +225,7 @@ describe('useKanbanBoardModel', () => {
225
225
  });
226
226
 
227
227
  act(() => {
228
- Obj.change(kanban, (kanban) => {
228
+ Obj.update(kanban, (kanban) => {
229
229
  kanban.arrangement.columns['b'] = { ids: [itemB.id] };
230
230
  });
231
231
  });
@@ -10,7 +10,8 @@ import { AtomObj } from '@dxos/echo-atom';
10
10
  import type { BoardModel } from '@dxos/react-ui-mosaic';
11
11
  import type { ProjectionModel } from '@dxos/schema';
12
12
 
13
- import { type BaseKanbanItem, type ColumnStructure, type Kanban } from '../types';
13
+ import { type BaseKanbanItem, type ColumnStructure, type Kanban } from '#types';
14
+
14
15
  import {
15
16
  computeColumnStructure,
16
17
  getOrderByColumnFromArrangement,
@@ -37,14 +38,27 @@ export function useKanbanBoardModel<T extends BaseKanbanItem = BaseKanbanItem>(
37
38
  // Source atoms: reactive reads from the kanban object; items come from the passed-in atom (e.g. AtomQuery or in-memory).
38
39
  const arrangementAtom = useMemo(() => AtomObj.makeProperty(kanban, 'arrangement'), [kanban]);
39
40
  const viewSnapshotAtom = useMemo(
40
- () => (kanban?.view ? AtomObj.make(kanban.view) : Atom.make<undefined>(() => undefined)),
41
- [kanban?.view],
41
+ () =>
42
+ kanban?.spec?.kind === 'view' && kanban.spec.view
43
+ ? AtomObj.make(kanban.spec.view)
44
+ : Atom.make<undefined>(() => undefined),
45
+ [kanban?.spec],
42
46
  );
43
47
 
44
- /** Only changes when view.projection.pivotFieldId changes; keeps columns from firing on other view updates. */
48
+ /**
49
+ * Only changes when the discriminator-relevant pivot input changes.
50
+ * View-variant: derived from `view.projection.pivotFieldId`.
51
+ * Items-variant: the kanban's `spec.pivotField` (the property name itself acts as the field id).
52
+ */
45
53
  const pivotFieldIdAtom = useMemo(
46
- () => Atom.make((get) => get(viewSnapshotAtom)?.projection?.pivotFieldId as string | undefined),
47
- [viewSnapshotAtom],
54
+ () =>
55
+ Atom.make((get) => {
56
+ if (kanban?.spec.kind === 'items') {
57
+ return kanban.spec.pivotField;
58
+ }
59
+ return get(viewSnapshotAtom)?.projection?.pivotFieldId as string | undefined;
60
+ }),
61
+ [kanban?.spec, viewSnapshotAtom],
48
62
  );
49
63
 
50
64
  // Effective per-column ids: from kanban.arrangement.columns; empty when arrangement has no columns.
@@ -8,7 +8,7 @@ import type { BoardModel, MosaicEventHandler, MosaicTileData } from '@dxos/react
8
8
  import type { ProjectionModel } from '@dxos/schema';
9
9
  import { arrayMove } from '@dxos/util';
10
10
 
11
- import { type BaseKanbanItem, type ColumnStructure, type KanbanChangeCallback, UNCATEGORIZED_VALUE } from '../types';
11
+ import { type BaseKanbanItem, type ColumnStructure, type KanbanChangeCallback, UNCATEGORIZED_VALUE } from '#types';
12
12
 
13
13
  /**
14
14
  * Builds the column drag-and-drop handler for the kanban board (reorder columns).
@@ -12,7 +12,7 @@ import {
12
12
  type ColumnStructure,
13
13
  type KanbanChangeCallback,
14
14
  UNCATEGORIZED_VALUE,
15
- } from '../types';
15
+ } from '#types';
16
16
 
17
17
  function findColumn<T extends BaseKanbanItem>(
18
18
  id: string,
@@ -10,7 +10,7 @@ import { log } from '@dxos/log';
10
10
  import { useAsyncEffect } from '@dxos/react-ui';
11
11
  import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
12
12
 
13
- import { type Kanban } from '../types';
13
+ import { type Kanban } from '#types';
14
14
 
15
15
  /**
16
16
  * Loads the kanban view and builds a ProjectionModel for field projections and pivot.
@@ -30,11 +30,11 @@ export const useProjectionModel = <S extends Type.AnyEntity>(
30
30
 
31
31
  useAsyncEffect(
32
32
  async (controller) => {
33
- if (!schema || !kanban) {
33
+ if (!schema || !kanban || kanban.spec.kind !== 'view') {
34
34
  return;
35
35
  }
36
36
  try {
37
- const view = await kanban.view.load();
37
+ const view = await kanban.spec.view.load();
38
38
  if (controller.signal.aborted) {
39
39
  return;
40
40
  }