@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.
- package/dist/lib/neutral/KanbanContainer-QK6LNCYT.mjs +132 -0
- package/dist/lib/neutral/KanbanContainer-QK6LNCYT.mjs.map +7 -0
- package/dist/lib/neutral/KanbanPlugin.mjs +36 -0
- package/dist/lib/neutral/KanbanPlugin.mjs.map +7 -0
- package/dist/lib/neutral/KanbanPlugin.node.mjs +27 -0
- package/dist/lib/neutral/KanbanPlugin.node.mjs.map +7 -0
- package/dist/lib/{browser/KanbanViewEditor-IH5CJ6BW.mjs → neutral/KanbanSettings-G6M47NSK.mjs} +36 -19
- package/dist/lib/neutral/KanbanSettings-G6M47NSK.mjs.map +7 -0
- package/dist/lib/{browser/blueprint-definition-HFEKGFJK.mjs → neutral/blueprint-definition-FHVIEKTQ.mjs} +4 -6
- package/dist/lib/neutral/blueprint-definition-FHVIEKTQ.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/blueprints/index.mjs +1 -1
- package/dist/lib/neutral/capabilities/index.mjs +19 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-E65AME5F.mjs +255 -0
- package/dist/lib/neutral/chunk-E65AME5F.mjs.map +7 -0
- package/dist/lib/neutral/chunk-KDPM77BD.mjs +21 -0
- package/dist/lib/neutral/chunk-KDPM77BD.mjs.map +7 -0
- package/dist/lib/neutral/chunk-Z7O5CETK.mjs +8 -0
- package/dist/lib/neutral/chunk-Z7O5CETK.mjs.map +7 -0
- package/dist/lib/{browser/chunk-TLYZSC7O.mjs → neutral/chunk-ZTQW5KQS.mjs} +3 -6
- package/dist/lib/neutral/chunk-ZTQW5KQS.mjs.map +7 -0
- package/dist/lib/{browser/KanbanContainer-BCXSJ6KS.mjs → neutral/components/index.mjs} +78 -139
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/containers/index.mjs +11 -0
- package/dist/lib/neutral/containers/index.mjs.map +7 -0
- package/dist/lib/neutral/create-object-DKBSI46K.mjs +40 -0
- package/dist/lib/neutral/create-object-DKBSI46K.mjs.map +7 -0
- package/dist/lib/neutral/delete-card-QKT6OXGP.mjs +24 -0
- package/dist/lib/neutral/delete-card-QKT6OXGP.mjs.map +7 -0
- package/dist/lib/neutral/delete-card-field-XQKM7ZXE.mjs +42 -0
- package/dist/lib/neutral/delete-card-field-XQKM7ZXE.mjs.map +7 -0
- package/dist/lib/{node-esm/chunk-CSL3HF2X.mjs → neutral/hooks/index.mjs} +128 -82
- package/dist/lib/neutral/hooks/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +34 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/neutral/migrations-4NS6H7U2.mjs +31 -0
- package/dist/lib/neutral/migrations-4NS6H7U2.mjs.map +7 -0
- package/dist/lib/neutral/operation-handler-B7IW6MXU.mjs +13 -0
- package/dist/lib/neutral/operation-handler-B7IW6MXU.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +8 -0
- package/dist/lib/neutral/plugin.mjs +16 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/{browser/react-surface-FOMOGFVW.mjs → neutral/react-surface-6RVSCHMJ.mjs} +21 -27
- package/dist/lib/neutral/react-surface-6RVSCHMJ.mjs.map +7 -0
- package/dist/lib/neutral/restore-card-XW7AHMPO.mjs +21 -0
- package/dist/lib/neutral/restore-card-XW7AHMPO.mjs.map +7 -0
- package/dist/lib/neutral/restore-card-field-QOAUY3RJ.mjs +40 -0
- package/dist/lib/neutral/restore-card-field-QOAUY3RJ.mjs.map +7 -0
- package/dist/lib/neutral/testing/index.mjs +60 -0
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +44 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/types/index.mjs +7 -5
- package/dist/lib/neutral/undo-mappings-6CHW6BOF.mjs +42 -0
- package/dist/lib/neutral/undo-mappings-6CHW6BOF.mjs.map +7 -0
- package/dist/types/src/KanbanPlugin.d.ts +1 -0
- package/dist/types/src/KanbanPlugin.d.ts.map +1 -1
- package/dist/types/src/KanbanPlugin.node.d.ts +4 -0
- package/dist/types/src/KanbanPlugin.node.d.ts.map +1 -0
- package/dist/types/src/KanbanPlugin.test.d.ts +2 -0
- package/dist/types/src/KanbanPlugin.test.d.ts.map +1 -0
- package/dist/types/src/blueprints/kanban-blueprint.d.ts +2 -2
- package/dist/types/src/blueprints/kanban-blueprint.d.ts.map +1 -1
- package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -0
- package/dist/types/src/capabilities/blueprint-definition.d.ts +6 -0
- package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
- package/dist/types/src/capabilities/create-object.d.ts +11 -0
- package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +13 -3
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/migrations.d.ts +5 -0
- package/dist/types/src/capabilities/migrations.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/undo-mappings.d.ts +5 -0
- package/dist/types/src/capabilities/undo-mappings.d.ts.map +1 -0
- package/dist/types/src/components/KanbanBoard/KanbanBoard.d.ts +17 -38
- package/dist/types/src/components/KanbanBoard/KanbanBoard.d.ts.map +1 -1
- package/dist/types/src/components/KanbanBoard/KanbanBoard.stories.d.ts +48 -46
- package/dist/types/src/components/KanbanBoard/KanbanBoard.stories.d.ts.map +1 -1
- package/dist/types/src/components/KanbanBoard/KanbanCard.d.ts +2 -3
- package/dist/types/src/components/KanbanBoard/KanbanCard.d.ts.map +1 -1
- package/dist/types/src/components/KanbanBoard/KanbanColumn.d.ts +2 -3
- package/dist/types/src/components/KanbanBoard/KanbanColumn.d.ts.map +1 -1
- package/dist/types/src/components/KanbanBoard/context.d.ts +38 -0
- package/dist/types/src/components/KanbanBoard/context.d.ts.map +1 -0
- package/dist/types/src/containers/KanbanContainer/KanbanContainer.d.ts +4 -4
- package/dist/types/src/containers/KanbanContainer/KanbanContainer.d.ts.map +1 -1
- package/dist/types/src/containers/KanbanContainer/KanbanContainer.stories.d.ts +48 -46
- package/dist/types/src/containers/KanbanContainer/KanbanContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/KanbanContainer/index.d.ts +1 -2
- package/dist/types/src/containers/KanbanContainer/index.d.ts.map +1 -1
- package/dist/types/src/containers/KanbanSettings/KanbanSettings.d.ts +13 -0
- package/dist/types/src/containers/KanbanSettings/KanbanSettings.d.ts.map +1 -0
- package/dist/types/src/containers/KanbanSettings/index.d.ts +2 -0
- package/dist/types/src/containers/KanbanSettings/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +1 -1
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useEchoChangeCallback.d.ts +1 -1
- package/dist/types/src/hooks/useEchoChangeCallback.d.ts.map +1 -1
- package/dist/types/src/hooks/useItemsProjection.d.ts +10 -0
- package/dist/types/src/hooks/useItemsProjection.d.ts.map +1 -0
- package/dist/types/src/hooks/useKanbanBoardModel.browser.test.d.ts +2 -0
- package/dist/types/src/hooks/useKanbanBoardModel.browser.test.d.ts.map +1 -0
- package/dist/types/src/hooks/useKanbanBoardModel.d.ts +1 -1
- package/dist/types/src/hooks/useKanbanBoardModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useKanbanColumnEventHandler.d.ts +2 -2
- package/dist/types/src/hooks/useKanbanColumnEventHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useKanbanItemEventHandler.d.ts +2 -2
- package/dist/types/src/hooks/useKanbanItemEventHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/operations/delete-card-field.d.ts +5 -0
- package/dist/types/src/operations/delete-card-field.d.ts.map +1 -0
- package/dist/types/src/operations/delete-card.d.ts +5 -0
- package/dist/types/src/operations/delete-card.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/restore-card-field.d.ts +5 -0
- package/dist/types/src/operations/restore-card-field.d.ts.map +1 -0
- package/dist/types/src/operations/restore-card.d.ts +5 -0
- package/dist/types/src/operations/restore-card.d.ts.map +1 -0
- package/dist/types/src/playwright/board-manager.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/testing/KanbanCardTileSimple.d.ts +4 -2
- package/dist/types/src/testing/KanbanCardTileSimple.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +48 -46
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Kanban.d.ts +78 -6
- package/dist/types/src/types/Kanban.d.ts.map +1 -1
- package/dist/types/src/types/KanbanOperation.d.ts +52 -0
- package/dist/types/src/types/KanbanOperation.d.ts.map +1 -0
- package/dist/types/src/types/constants.d.ts +3 -3
- package/dist/types/src/types/constants.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +2 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/types/migrations.test.d.ts +2 -0
- package/dist/types/src/types/migrations.test.d.ts.map +1 -0
- package/dist/types/src/types/schema.d.ts +15 -104
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +2 -2
- package/dist/types/src/util/arrangement.d.ts +8 -4
- package/dist/types/src/util/arrangement.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +108 -65
- package/src/KanbanPlugin.node.ts +21 -0
- package/src/KanbanPlugin.test.ts +31 -0
- package/src/KanbanPlugin.tsx +21 -29
- package/src/blueprints/kanban-blueprint.ts +4 -8
- package/src/capabilities/{artifact-definition/artifact-definition.ts → artifact-definition.ts} +4 -4
- package/src/capabilities/{blueprint-definition/blueprint-definition.ts → blueprint-definition.ts} +3 -1
- package/src/capabilities/create-object.ts +40 -0
- package/src/capabilities/index.ts +13 -3
- package/src/capabilities/migrations.ts +35 -0
- package/src/capabilities/operation-handler.ts +14 -0
- package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +22 -13
- package/src/capabilities/undo-mappings.ts +34 -0
- package/src/components/KanbanBoard/KanbanBoard.stories.tsx +20 -16
- package/src/components/KanbanBoard/KanbanBoard.tsx +44 -64
- package/src/components/KanbanBoard/KanbanCard.tsx +77 -63
- package/src/components/KanbanBoard/KanbanColumn.tsx +19 -16
- package/src/components/KanbanBoard/context.ts +54 -0
- package/src/containers/KanbanContainer/KanbanContainer.stories.tsx +44 -30
- package/src/containers/KanbanContainer/KanbanContainer.tsx +118 -27
- package/src/containers/KanbanContainer/index.ts +1 -3
- package/src/containers/KanbanSettings/KanbanSettings.tsx +94 -0
- package/src/containers/KanbanSettings/index.ts +5 -0
- package/src/containers/index.ts +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useEchoChangeCallback.ts +3 -3
- package/src/hooks/useItemsProjection.ts +44 -0
- package/src/hooks/{useKanbanBoardModel.test.ts → useKanbanBoardModel.browser.test.ts} +5 -5
- package/src/hooks/useKanbanBoardModel.ts +20 -6
- package/src/hooks/useKanbanColumnEventHandler.ts +1 -1
- package/src/hooks/useKanbanItemEventHandler.ts +1 -1
- package/src/hooks/useProjectionModel.ts +3 -3
- package/src/index.ts +3 -2
- package/src/meta.ts +1 -1
- package/src/operations/delete-card-field.ts +47 -0
- package/src/operations/delete-card.ts +23 -0
- package/src/operations/index.ts +10 -0
- package/src/operations/restore-card-field.ts +41 -0
- package/src/operations/restore-card.ts +21 -0
- package/src/playwright/smoke.spec.ts +3 -3
- package/src/plugin.ts +11 -0
- package/src/testing/KanbanCardTileSimple.tsx +25 -19
- package/src/translations.ts +26 -25
- package/src/types/Kanban.ts +98 -14
- package/src/types/KanbanOperation.ts +67 -0
- package/src/types/index.ts +3 -1
- package/src/types/migrations.test.ts +83 -0
- package/src/types/schema.ts +20 -78
- package/src/types/types.ts +2 -2
- package/src/util/arrangement.test.ts +11 -1
- package/src/util/arrangement.ts +25 -15
- package/dist/lib/browser/KanbanContainer-BCXSJ6KS.mjs.map +0 -7
- package/dist/lib/browser/KanbanViewEditor-IH5CJ6BW.mjs.map +0 -7
- package/dist/lib/browser/blueprint-definition-HFEKGFJK.mjs.map +0 -7
- package/dist/lib/browser/chunk-QSWCFMEB.mjs +0 -385
- package/dist/lib/browser/chunk-QSWCFMEB.mjs.map +0 -7
- package/dist/lib/browser/chunk-RNFIFE2P.mjs +0 -213
- package/dist/lib/browser/chunk-RNFIFE2P.mjs.map +0 -7
- package/dist/lib/browser/chunk-TLYZSC7O.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -101
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/operation-resolver-BRA2OHUE.mjs +0 -162
- package/dist/lib/browser/operation-resolver-BRA2OHUE.mjs.map +0 -7
- package/dist/lib/browser/react-surface-FOMOGFVW.mjs.map +0 -7
- package/dist/lib/node-esm/KanbanContainer-EHRTLE7M.mjs +0 -305
- package/dist/lib/node-esm/KanbanContainer-EHRTLE7M.mjs.map +0 -7
- package/dist/lib/node-esm/KanbanViewEditor-WDACFC35.mjs +0 -67
- package/dist/lib/node-esm/KanbanViewEditor-WDACFC35.mjs.map +0 -7
- package/dist/lib/node-esm/blueprint-definition-NARBX32U.mjs +0 -18
- package/dist/lib/node-esm/blueprint-definition-NARBX32U.mjs.map +0 -7
- package/dist/lib/node-esm/blueprints/index.mjs +0 -9
- package/dist/lib/node-esm/chunk-4AWDHQVY.mjs +0 -214
- package/dist/lib/node-esm/chunk-4AWDHQVY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CSL3HF2X.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-X3UJUQIV.mjs +0 -31
- package/dist/lib/node-esm/chunk-X3UJUQIV.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -102
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/operation-resolver-UEQ64LCN.mjs +0 -163
- package/dist/lib/node-esm/operation-resolver-UEQ64LCN.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-AITKFRBE.mjs +0 -100
- package/dist/lib/node-esm/react-surface-AITKFRBE.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -21
- package/dist/types/src/capabilities/artifact-definition/artifact-definition.d.ts.map +0 -1
- package/dist/types/src/capabilities/artifact-definition/index.d.ts +0 -3
- package/dist/types/src/capabilities/artifact-definition/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts +0 -6
- package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts.map +0 -1
- package/dist/types/src/capabilities/blueprint-definition/index.d.ts +0 -3
- package/dist/types/src/capabilities/blueprint-definition/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
- package/dist/types/src/containers/KanbanViewEditor/KanbanViewEditor.d.ts +0 -6
- package/dist/types/src/containers/KanbanViewEditor/KanbanViewEditor.d.ts.map +0 -1
- package/dist/types/src/containers/KanbanViewEditor/index.d.ts +0 -3
- package/dist/types/src/containers/KanbanViewEditor/index.d.ts.map +0 -1
- package/dist/types/src/hooks/useKanbanBoardModel.test.d.ts +0 -2
- package/dist/types/src/hooks/useKanbanBoardModel.test.d.ts.map +0 -1
- package/src/capabilities/artifact-definition/index.ts +0 -7
- package/src/capabilities/blueprint-definition/index.ts +0 -7
- package/src/capabilities/operation-resolver/index.ts +0 -7
- package/src/capabilities/operation-resolver/operation-resolver.ts +0 -133
- package/src/capabilities/react-surface/index.ts +0 -7
- package/src/containers/KanbanViewEditor/KanbanViewEditor.tsx +0 -63
- package/src/containers/KanbanViewEditor/index.ts +0 -7
- /package/dist/lib/{browser → neutral}/blueprints/index.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
- /package/dist/lib/{browser/types → neutral}/index.mjs.map +0 -0
- /package/dist/lib/{node-esm/blueprints/index.mjs.map → neutral/meta.mjs.map} +0 -0
- /package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
- /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/types/index.mjs.map} +0 -0
- /package/dist/types/src/capabilities/{artifact-definition/artifact-definition.d.ts → artifact-definition.d.ts} +0 -0
- /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
|
-
|
|
18
|
-
|
|
19
|
-
import {
|
|
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 {
|
|
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 {
|
|
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 '
|
|
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
|
-
|
|
40
|
+
random.seed(0);
|
|
36
41
|
|
|
37
42
|
const createOrg = (status?: Organization.Organization['status']) => ({
|
|
38
|
-
name:
|
|
39
|
-
description:
|
|
40
|
-
image:
|
|
41
|
-
website:
|
|
42
|
-
status: (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*
|
|
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
|
|
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
|
|
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 } :
|
|
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(
|
|
104
|
+
invariant(view);
|
|
98
105
|
if (Type.isMutable(schema)) {
|
|
99
106
|
schema.updateTypename(getTypenameFromQuery(newQuery));
|
|
100
107
|
}
|
|
101
|
-
Obj.
|
|
108
|
+
Obj.update(view, (view) => {
|
|
102
109
|
view.query.ast = newQuery as Mutable<QueryAST.Query>;
|
|
103
110
|
});
|
|
104
111
|
},
|
|
105
|
-
[
|
|
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 || !
|
|
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
|
|
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={
|
|
135
|
+
view={view}
|
|
129
136
|
onQueryChanged={handleUpdateQuery}
|
|
130
137
|
onDelete={schema && Type.isMutable(schema) ? handleDeleteField : undefined}
|
|
131
138
|
/>
|
|
132
|
-
<
|
|
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: [
|
|
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:
|
|
186
|
-
const prospectTag = await canvas.findByText('Prospect', undefined, { timeout:
|
|
187
|
-
const commitTag = await canvas.findByText('Commit', undefined, { timeout:
|
|
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
|
|
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 '
|
|
18
|
-
import { useEchoChangeCallback, useProjectionModel } from '
|
|
19
|
-
import {
|
|
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 =
|
|
22
|
+
export type KanbanContainerProps = AppSurface.ObjectArticleProps<Kanban.Kanban>;
|
|
22
23
|
|
|
23
|
-
export const KanbanContainer = (
|
|
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
|
-
|
|
39
|
-
|
|
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.
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
</
|
|
84
|
-
</
|
|
174
|
+
</Panel.Content>
|
|
175
|
+
</KanbanBoard.Root>
|
|
85
176
|
</Panel.Root>
|
|
86
177
|
);
|
|
87
178
|
};
|
|
@@ -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
|
+
};
|
package/src/containers/index.ts
CHANGED
|
@@ -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
|
|
8
|
+
export const KanbanSettings: ComponentType<any> = lazy(() => import('./KanbanSettings'));
|
package/src/hooks/index.ts
CHANGED
|
@@ -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 '
|
|
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.
|
|
16
|
+
kanban: (mutate) => Obj.update(kanban, (kanban) => mutate(kanban)),
|
|
17
17
|
setItemField: (item, field, value) => {
|
|
18
|
-
Obj.
|
|
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 '
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 '
|
|
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
|
-
() =>
|
|
41
|
-
|
|
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
|
-
/**
|
|
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
|
-
() =>
|
|
47
|
-
|
|
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 '
|
|
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).
|
|
@@ -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 '
|
|
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
|
}
|