@dxos/plugin-kanban 0.8.4-main.72ec0f3 → 0.8.4-main.7ace549

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 (49) hide show
  1. package/dist/lib/browser/{chunk-HO3LQ5T7.mjs → chunk-B7EZTXV2.mjs} +24 -9
  2. package/dist/lib/browser/chunk-B7EZTXV2.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +22 -24
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/{intent-resolver-OPYUDNSU.mjs → intent-resolver-ANBDVEJ2.mjs} +11 -8
  6. package/dist/lib/browser/intent-resolver-ANBDVEJ2.mjs.map +7 -0
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/{react-surface-X54TNDDN.mjs → react-surface-7ONRKYBB.mjs} +57 -72
  9. package/dist/lib/browser/react-surface-7ONRKYBB.mjs.map +7 -0
  10. package/dist/lib/browser/types/index.mjs +5 -3
  11. package/dist/lib/node-esm/{chunk-QZMZU5HM.mjs → chunk-WBN5YQGD.mjs} +24 -9
  12. package/dist/lib/node-esm/chunk-WBN5YQGD.mjs.map +7 -0
  13. package/dist/lib/node-esm/index.mjs +22 -24
  14. package/dist/lib/node-esm/index.mjs.map +3 -3
  15. package/dist/lib/node-esm/{intent-resolver-BDKNXV3R.mjs → intent-resolver-X4UZDRI7.mjs} +11 -8
  16. package/dist/lib/node-esm/intent-resolver-X4UZDRI7.mjs.map +7 -0
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/{react-surface-CVDMIHSC.mjs → react-surface-D7XTGTZL.mjs} +57 -72
  19. package/dist/lib/node-esm/react-surface-D7XTGTZL.mjs.map +7 -0
  20. package/dist/lib/node-esm/types/index.mjs +5 -3
  21. package/dist/types/src/KanbanPlugin.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  23. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  24. package/dist/types/src/components/KanbanContainer.d.ts +3 -3
  25. package/dist/types/src/components/KanbanContainer.d.ts.map +1 -1
  26. package/dist/types/src/components/KanbanContainer.stories.d.ts +21 -8
  27. package/dist/types/src/components/KanbanContainer.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/KanbanViewEditor.d.ts +3 -3
  29. package/dist/types/src/components/KanbanViewEditor.d.ts.map +1 -1
  30. package/dist/types/src/translations.d.ts +21 -9
  31. package/dist/types/src/translations.d.ts.map +1 -1
  32. package/dist/types/src/types/schema.d.ts +5 -1
  33. package/dist/types/src/types/schema.d.ts.map +1 -1
  34. package/dist/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +32 -32
  36. package/src/KanbanPlugin.tsx +9 -15
  37. package/src/capabilities/intent-resolver.ts +4 -9
  38. package/src/capabilities/react-surface.tsx +12 -18
  39. package/src/components/KanbanContainer.stories.tsx +17 -34
  40. package/src/components/KanbanContainer.tsx +11 -25
  41. package/src/components/KanbanViewEditor.tsx +18 -21
  42. package/src/translations.ts +5 -5
  43. package/src/types/schema.ts +17 -6
  44. package/dist/lib/browser/chunk-HO3LQ5T7.mjs.map +0 -7
  45. package/dist/lib/browser/intent-resolver-OPYUDNSU.mjs.map +0 -7
  46. package/dist/lib/browser/react-surface-X54TNDDN.mjs.map +0 -7
  47. package/dist/lib/node-esm/chunk-QZMZU5HM.mjs.map +0 -7
  48. package/dist/lib/node-esm/intent-resolver-BDKNXV3R.mjs.map +0 -7
  49. package/dist/lib/node-esm/react-surface-CVDMIHSC.mjs.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-kanban",
3
- "version": "0.8.4-main.72ec0f3",
3
+ "version": "0.8.4-main.7ace549",
4
4
  "description": "Kanban DXOS Surface plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -38,29 +38,29 @@
38
38
  "@preact-signals/safe-react": "^0.9.0",
39
39
  "@preact/signals-core": "^1.12.1",
40
40
  "effect": "3.18.3",
41
- "@dxos/ai": "0.8.4-main.72ec0f3",
42
- "@dxos/assistant": "0.8.4-main.72ec0f3",
43
- "@dxos/app-framework": "0.8.4-main.72ec0f3",
44
- "@dxos/echo": "0.8.4-main.72ec0f3",
45
- "@dxos/async": "0.8.4-main.72ec0f3",
46
- "@dxos/effect": "0.8.4-main.72ec0f3",
47
- "@dxos/functions": "0.8.4-main.72ec0f3",
48
- "@dxos/blueprints": "0.8.4-main.72ec0f3",
49
- "@dxos/log": "0.8.4-main.72ec0f3",
50
- "@dxos/plugin-client": "0.8.4-main.72ec0f3",
51
- "@dxos/invariant": "0.8.4-main.72ec0f3",
52
- "@dxos/plugin-space": "0.8.4-main.72ec0f3",
53
- "@dxos/plugin-graph": "0.8.4-main.72ec0f3",
54
- "@dxos/plugin-search": "0.8.4-main.72ec0f3",
55
- "@dxos/random": "0.8.4-main.72ec0f3",
56
- "@dxos/react-client": "0.8.4-main.72ec0f3",
57
- "@dxos/react-ui": "0.8.4-main.72ec0f3",
58
- "@dxos/react-ui-form": "0.8.4-main.72ec0f3",
59
- "@dxos/react-ui-kanban": "0.8.4-main.72ec0f3",
60
- "@dxos/react-ui-stack": "0.8.4-main.72ec0f3",
61
- "@dxos/schema": "0.8.4-main.72ec0f3",
62
- "@dxos/types": "0.8.4-main.72ec0f3",
63
- "@dxos/util": "0.8.4-main.72ec0f3"
41
+ "@dxos/ai": "0.8.4-main.7ace549",
42
+ "@dxos/app-framework": "0.8.4-main.7ace549",
43
+ "@dxos/assistant": "0.8.4-main.7ace549",
44
+ "@dxos/blueprints": "0.8.4-main.7ace549",
45
+ "@dxos/async": "0.8.4-main.7ace549",
46
+ "@dxos/echo": "0.8.4-main.7ace549",
47
+ "@dxos/effect": "0.8.4-main.7ace549",
48
+ "@dxos/functions": "0.8.4-main.7ace549",
49
+ "@dxos/plugin-client": "0.8.4-main.7ace549",
50
+ "@dxos/invariant": "0.8.4-main.7ace549",
51
+ "@dxos/plugin-graph": "0.8.4-main.7ace549",
52
+ "@dxos/plugin-search": "0.8.4-main.7ace549",
53
+ "@dxos/plugin-space": "0.8.4-main.7ace549",
54
+ "@dxos/react-client": "0.8.4-main.7ace549",
55
+ "@dxos/react-ui": "0.8.4-main.7ace549",
56
+ "@dxos/react-ui-form": "0.8.4-main.7ace549",
57
+ "@dxos/react-ui-kanban": "0.8.4-main.7ace549",
58
+ "@dxos/random": "0.8.4-main.7ace549",
59
+ "@dxos/react-ui-stack": "0.8.4-main.7ace549",
60
+ "@dxos/schema": "0.8.4-main.7ace549",
61
+ "@dxos/types": "0.8.4-main.7ace549",
62
+ "@dxos/log": "0.8.4-main.7ace549",
63
+ "@dxos/util": "0.8.4-main.7ace549"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/react": "~19.2.2",
@@ -68,19 +68,19 @@
68
68
  "react": "~19.2.0",
69
69
  "react-dom": "~19.2.0",
70
70
  "vite": "7.1.9",
71
- "@dxos/plugin-theme": "0.8.4-main.72ec0f3",
72
- "@dxos/plugin-preview": "0.8.4-main.72ec0f3",
73
- "@dxos/react-ui-theme": "0.8.4-main.72ec0f3",
74
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.72ec0f3",
75
- "@dxos/storybook-utils": "0.8.4-main.72ec0f3",
76
- "@dxos/test-utils": "0.8.4-main.72ec0f3"
71
+ "@dxos/plugin-preview": "0.8.4-main.7ace549",
72
+ "@dxos/plugin-theme": "0.8.4-main.7ace549",
73
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.7ace549",
74
+ "@dxos/storybook-utils": "0.8.4-main.7ace549",
75
+ "@dxos/react-ui-theme": "0.8.4-main.7ace549",
76
+ "@dxos/test-utils": "0.8.4-main.7ace549"
77
77
  },
78
78
  "peerDependencies": {
79
79
  "effect": "^3.13.3",
80
80
  "react": "^19.0.0",
81
81
  "react-dom": "^19.0.0",
82
- "@dxos/react-ui": "0.8.4-main.72ec0f3",
83
- "@dxos/react-ui-theme": "0.8.4-main.72ec0f3"
82
+ "@dxos/react-ui": "0.8.4-main.7ace549",
83
+ "@dxos/react-ui-theme": "0.8.4-main.7ace549"
84
84
  },
85
85
  "publishConfig": {
86
86
  "access": "public"
@@ -3,9 +3,9 @@
3
3
  //
4
4
 
5
5
  import { Capabilities, Events, contributes, createIntent, defineModule, definePlugin } from '@dxos/app-framework';
6
- import { ClientEvents } from '@dxos/plugin-client';
7
- import { SpaceCapabilities } from '@dxos/plugin-space';
8
- import { defineObjectForm } from '@dxos/plugin-space/types';
6
+ import { Type } from '@dxos/echo';
7
+ import { ClientCapabilities, ClientEvents } from '@dxos/plugin-client';
8
+ import { type CreateObjectIntent } from '@dxos/plugin-space/types';
9
9
  import { translations as kanbanTranslations } from '@dxos/react-ui-kanban';
10
10
  import { Kanban } from '@dxos/react-ui-kanban/types';
11
11
 
@@ -25,26 +25,20 @@ export const KanbanPlugin = definePlugin(meta, () => [
25
25
  activatesOn: Events.SetupMetadata,
26
26
  activate: () =>
27
27
  contributes(Capabilities.Metadata, {
28
- id: Kanban.Kanban.typename,
28
+ id: Type.getTypename(Kanban.Kanban),
29
29
  metadata: {
30
30
  icon: 'ph--kanban--regular',
31
31
  iconHue: 'green',
32
+ inputSchema: CreateKanbanSchema,
33
+ createObjectIntent: ((props, options) =>
34
+ createIntent(KanbanAction.Create, { ...props, space: options.space })) satisfies CreateObjectIntent,
32
35
  },
33
36
  }),
34
37
  }),
35
38
  defineModule({
36
- id: `${meta.id}/module/object-form`,
39
+ id: `${meta.id}/module/schema`,
37
40
  activatesOn: ClientEvents.SetupSchema,
38
- activate: () =>
39
- contributes(
40
- SpaceCapabilities.ObjectForm,
41
- defineObjectForm({
42
- objectSchema: Kanban.Kanban,
43
- formSchema: CreateKanbanSchema,
44
- hidden: true,
45
- getIntent: (props, options) => createIntent(KanbanAction.Create, { ...props, space: options.space }),
46
- }),
47
- ),
41
+ activate: () => contributes(ClientCapabilities.Schema, [Kanban.Kanban]),
48
42
  }),
49
43
  defineModule({
50
44
  id: `${meta.id}/module/react-surface`,
@@ -7,7 +7,7 @@ import { invariant } from '@dxos/invariant';
7
7
  import { ClientCapabilities } from '@dxos/plugin-client';
8
8
  import { getSpace } from '@dxos/react-client/echo';
9
9
  import { Kanban } from '@dxos/react-ui-kanban/types';
10
- import { ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
10
+ import { ProjectionModel, View, getTypenameFromQuery } from '@dxos/schema';
11
11
 
12
12
  import { meta } from '../meta';
13
13
  import { KanbanAction } from '../types';
@@ -18,14 +18,9 @@ export default (context: PluginContext) =>
18
18
  intent: KanbanAction.Create,
19
19
  resolve: async ({ space, name, typename, initialPivotColumn }) => {
20
20
  const client = context.getCapability(ClientCapabilities.Client);
21
- const { view } = await Kanban.makeView({
22
- client,
23
- space,
24
- name,
25
- typename,
26
- pivotFieldName: initialPivotColumn,
27
- });
28
- return { data: { object: view } };
21
+ const { view } = await View.makeFromSpace({ client, space, typename, pivotFieldName: initialPivotColumn });
22
+ const kanban = Kanban.make({ name, view });
23
+ return { data: { object: kanban } };
29
24
  },
30
25
  }),
31
26
  createResolver({
@@ -6,14 +6,12 @@ import type * as Schema from 'effect/Schema';
6
6
  import React, { useMemo } from 'react';
7
7
 
8
8
  import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
9
- import { useCapabilities } from '@dxos/app-framework/react';
10
9
  import { Obj, Type } from '@dxos/echo';
11
10
  import { findAnnotation } from '@dxos/effect';
12
- import { ClientCapabilities } from '@dxos/plugin-client';
13
11
  import { type Space, getSpace, isSpace } from '@dxos/react-client/echo';
14
12
  import { type InputProps, SelectInput, useFormValues } from '@dxos/react-ui-form';
15
13
  import { Kanban } from '@dxos/react-ui-kanban/types';
16
- import { type Collection, View } from '@dxos/schema';
14
+ import { type Collection } from '@dxos/schema';
17
15
 
18
16
  import { KanbanContainer, KanbanViewEditor } from '../components';
19
17
  import { meta } from '../meta';
@@ -24,17 +22,15 @@ export default () =>
24
22
  createSurface({
25
23
  id: meta.id,
26
24
  role: ['article', 'section'],
27
- filter: (data): data is { subject: View.View } =>
28
- Obj.instanceOf(View.View, data.subject) && Obj.instanceOf(Kanban.Kanban, data.subject.presentation.target),
29
- component: ({ data, role }) => <KanbanContainer view={data.subject} role={role} />,
25
+ filter: (data): data is { subject: Kanban.Kanban } => Obj.instanceOf(Kanban.Kanban, data.subject),
26
+ component: ({ data, role }) => <KanbanContainer object={data.subject} role={role} />,
30
27
  }),
31
28
  createSurface({
32
29
  id: `${meta.id}/object-settings`,
33
30
  role: 'object-settings',
34
31
  position: 'hoist',
35
- filter: (data): data is { subject: View.View } =>
36
- Obj.instanceOf(View.View, data.subject) && Obj.instanceOf(Kanban.Kanban, data.subject.presentation.target),
37
- component: ({ data }) => <KanbanViewEditor view={data.subject} />,
32
+ filter: (data): data is { subject: Kanban.Kanban } => Obj.instanceOf(Kanban.Kanban, data.subject),
33
+ component: ({ data }) => <KanbanViewEditor object={data.subject} />,
38
34
  }),
39
35
  createSurface({
40
36
  id: `${meta.id}/create-initial-schema-form-[pivot-column]`,
@@ -55,16 +51,14 @@ export default () =>
55
51
  if (!space) {
56
52
  return null;
57
53
  }
58
- const { typename } = useFormValues();
59
- // TODO(wittjosiah): Unify this schema lookup.
60
- const schemaWhitelists = useCapabilities(ClientCapabilities.SchemaWhiteList);
61
- const staticSchema = schemaWhitelists.flat().find((schema) => Type.getTypename(schema) === typename);
62
- const [selectedSchema] = space?.db.schemaRegistry.query({ typename }).runSync();
63
54
 
55
+ const { typename } = useFormValues();
56
+ const [selectedSchema] = useMemo(
57
+ () => space?.db.schemaRegistry.query({ location: ['database', 'runtime'], typename }).runSync(),
58
+ [space, typename],
59
+ );
64
60
  const singleSelectColumns = useMemo(() => {
65
- const properties = staticSchema
66
- ? Type.toJsonSchema(staticSchema).properties
67
- : selectedSchema?.jsonSchema?.properties;
61
+ const properties = Type.toJsonSchema(selectedSchema).properties;
68
62
  if (!properties) {
69
63
  return [];
70
64
  }
@@ -77,7 +71,7 @@ export default () =>
77
71
  }, []);
78
72
 
79
73
  return columns;
80
- }, [selectedSchema?.jsonSchema, staticSchema]);
74
+ }, [selectedSchema]);
81
75
 
82
76
  if (!typename) {
83
77
  return null;
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import React, { useCallback, useEffect, useState } from 'react';
6
+ import React, { useCallback } from 'react';
7
7
 
8
8
  import { IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
9
9
  import { withPluginManager } from '@dxos/app-framework/testing';
@@ -20,11 +20,11 @@ import { useClient } from '@dxos/react-client';
20
20
  import { Filter, useQuery, useSchema, useSpaces } from '@dxos/react-client/echo';
21
21
  import { withTheme } from '@dxos/react-ui/testing';
22
22
  import { ViewEditor } from '@dxos/react-ui-form';
23
- import { Kanban as KanbanComponent, useKanbanModel } from '@dxos/react-ui-kanban';
23
+ import { Kanban as KanbanComponent, useKanbanModel, useProjectionModel } from '@dxos/react-ui-kanban';
24
24
  import { Kanban } from '@dxos/react-ui-kanban/types';
25
25
  import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
26
26
  import { defaultTx } from '@dxos/react-ui-theme';
27
- import { ProjectionModel, View, getTypenameFromQuery } from '@dxos/schema';
27
+ import { View, getTypenameFromQuery } from '@dxos/schema';
28
28
  import { Organization, Person } from '@dxos/types';
29
29
 
30
30
  import { translations } from '../translations';
@@ -47,34 +47,16 @@ const StorybookKanban = () => {
47
47
  const client = useClient();
48
48
  const spaces = useSpaces();
49
49
  const space = spaces[spaces.length - 1];
50
- const views = useQuery(space, Filter.type(View.View));
51
- const [view, setView] = useState<View.View>();
52
- const [projection, setProjection] = useState<ProjectionModel>();
53
- const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
50
+ const [object] = useQuery(space, Filter.type(Kanban.Kanban));
51
+ const typename = object?.view.target?.query ? getTypenameFromQuery(object.view.target.query.ast) : undefined;
54
52
  const schema = useSchema(client, space, typename);
55
53
 
56
- useEffect(() => {
57
- if (views.length && !view) {
58
- const view = views[0];
59
- setView(view);
60
- }
61
- }, [views]);
62
-
63
- useEffect(() => {
64
- if (view?.projection && schema) {
65
- const jsonSchema = Type.toJsonSchema(schema);
66
- setProjection(new ProjectionModel(jsonSchema, view.projection));
67
- }
68
- // TODO(ZaymonFC): Is there a better way to get notified about deep changes in the json schema?
69
- // @dmaretskyi? Once resolved, update in multiple places (e.g., storybooks).
70
- }, [view?.projection, schema, JSON.stringify(schema ? Type.toJsonSchema(schema) : {})]);
71
-
72
54
  const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
73
55
  const filteredObjects = useGlobalFilteredObjects(objects);
74
56
 
57
+ const projection = useProjectionModel(schema, object);
75
58
  const model = useKanbanModel({
76
- view,
77
- schema,
59
+ object,
78
60
  projection,
79
61
  items: filteredObjects,
80
62
  });
@@ -95,21 +77,21 @@ const StorybookKanban = () => {
95
77
  [space, schema, model],
96
78
  );
97
79
 
98
- const handleRemoveCard = useCallback((card: { id: string }) => space.db.remove(card), [space]);
80
+ const handleRemoveCard = useCallback((card: { id: string }) => Obj.isObject(card) && space?.db.remove(card), [space]);
99
81
 
100
82
  const handleUpdateQuery = useCallback(
101
83
  (newQuery: QueryAST.Query) => {
102
84
  invariant(schema);
103
85
  invariant(Type.isMutable(schema));
104
- invariant(view);
86
+ invariant(object.view.target);
105
87
 
106
88
  schema.updateTypename(getTypenameFromQuery(newQuery));
107
- view.query.ast = newQuery;
89
+ object.view.target.query.ast = newQuery;
108
90
  },
109
- [view, schema],
91
+ [object, schema],
110
92
  );
111
93
 
112
- if (!schema || !view) {
94
+ if (!schema || !object.view.target) {
113
95
  return null;
114
96
  }
115
97
 
@@ -120,14 +102,14 @@ const StorybookKanban = () => {
120
102
  <ViewEditor
121
103
  registry={space?.db.schemaRegistry}
122
104
  schema={schema}
123
- view={view}
105
+ view={object.view.target}
124
106
  onQueryChanged={handleUpdateQuery}
125
107
  onDelete={(fieldId: string) => {
126
108
  console.log('[ViewEditor]', 'onDelete', fieldId);
127
109
  }}
128
110
  />
129
111
  <SyntaxHighlighter language='json' className='text-xs'>
130
- {JSON.stringify({ view, schema }, null, 2)}
112
+ {JSON.stringify({ view: object.view.target, schema }, null, 2)}
131
113
  </SyntaxHighlighter>
132
114
  </div>
133
115
  </div>
@@ -156,13 +138,14 @@ const meta = {
156
138
  await client.halo.createIdentity();
157
139
  const space = await client.spaces.create();
158
140
  await space.waitUntilReady();
159
- const { view } = await Kanban.makeView({
141
+ const { view } = await View.makeFromSpace({
160
142
  client,
161
143
  space,
162
144
  typename: Organization.Organization.typename,
163
145
  pivotFieldName: 'status',
164
146
  });
165
- space.db.add(view);
147
+ const kanban = Kanban.make({ view });
148
+ space.db.add(kanban);
166
149
 
167
150
  // TODO(burdon): Replace with sdk/schema/testing.
168
151
  Array.from({ length: 80 }).map(() => {
@@ -2,35 +2,28 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
5
+ import React, { useCallback, useEffect, useState } from 'react';
6
6
 
7
7
  import { createIntent } from '@dxos/app-framework';
8
8
  import { useIntentDispatcher } from '@dxos/app-framework/react';
9
9
  import { Filter, Obj, Type } from '@dxos/echo';
10
- import { EchoSchema, type TypedObject } from '@dxos/echo/internal';
10
+ import { type TypedObject } from '@dxos/echo/internal';
11
11
  import { useGlobalFilteredObjects } from '@dxos/plugin-search';
12
12
  import { useClient } from '@dxos/react-client';
13
13
  import { getSpace, useQuery } from '@dxos/react-client/echo';
14
- import { Kanban, useKanbanModel } from '@dxos/react-ui-kanban';
14
+ import { Kanban as KanbanComponent, useKanbanModel, useProjectionModel } from '@dxos/react-ui-kanban';
15
+ import { type Kanban } from '@dxos/react-ui-kanban/types';
15
16
  import { StackItem } from '@dxos/react-ui-stack';
16
- import { ProjectionModel, type View, getTypenameFromQuery } from '@dxos/schema';
17
+ import { getTypenameFromQuery } from '@dxos/schema';
17
18
 
18
19
  import { KanbanAction } from '../types';
19
20
 
20
- export const KanbanContainer = ({ view }: { view: View.View; role: string }) => {
21
+ export const KanbanContainer = ({ object }: { object: Kanban.Kanban; role: string }) => {
21
22
  const client = useClient();
22
23
  const [cardSchema, setCardSchema] = useState<TypedObject<any, any>>();
23
- const [projection, setProjection] = useState<ProjectionModel>();
24
- const space = getSpace(view);
24
+ const space = getSpace(object);
25
25
  const { dispatchPromise: dispatch } = useIntentDispatcher();
26
- const typename = view.query ? getTypenameFromQuery(view.query.ast) : undefined;
27
-
28
- const jsonSchema = useMemo(() => {
29
- if (!cardSchema) {
30
- return undefined;
31
- }
32
- return cardSchema instanceof EchoSchema ? cardSchema.jsonSchema : Type.toJsonSchema(cardSchema);
33
- }, [cardSchema]);
26
+ const typename = object.view.target?.query ? getTypenameFromQuery(object.view.target.query.ast) : undefined;
34
27
 
35
28
  useEffect(() => {
36
29
  const staticSchema = client.graph.schemaRegistry.schemas.find((schema) => Type.getTypename(schema) === typename);
@@ -52,19 +45,12 @@ export const KanbanContainer = ({ view }: { view: View.View; role: string }) =>
52
45
  }
53
46
  }, [typename, space]);
54
47
 
55
- useEffect(() => {
56
- if (jsonSchema) {
57
- setProjection(new ProjectionModel(jsonSchema, view.projection));
58
- }
59
- // TODO(ZaymonFC): Is there a better way to get notified about deep changes in the json schema?
60
- }, [view.projection, JSON.stringify(jsonSchema)]);
61
-
62
48
  const objects = useQuery(space, cardSchema ? Filter.type(cardSchema) : Filter.nothing());
63
49
  const filteredObjects = useGlobalFilteredObjects(objects);
64
50
 
51
+ const projection = useProjectionModel(cardSchema, object);
65
52
  const model = useKanbanModel({
66
- view,
67
- schema: cardSchema,
53
+ object,
68
54
  projection,
69
55
  items: filteredObjects,
70
56
  });
@@ -90,7 +76,7 @@ export const KanbanContainer = ({ view }: { view: View.View; role: string }) =>
90
76
 
91
77
  return (
92
78
  <StackItem.Content>
93
- {model && <Kanban model={model} onAddCard={handleAddCard} onRemoveCard={handleRemoveCard} />}
79
+ {model && <KanbanComponent model={model} onAddCard={handleAddCard} onRemoveCard={handleRemoveCard} />}
94
80
  </StackItem.Content>
95
81
  );
96
82
  };
@@ -4,46 +4,43 @@
4
4
 
5
5
  import React, { useCallback, useMemo } from 'react';
6
6
 
7
- import { Type } from '@dxos/echo';
8
- import { EchoSchema, FormatEnum } from '@dxos/echo/internal';
7
+ import { Format } from '@dxos/echo/internal';
8
+ import { invariant } from '@dxos/invariant';
9
9
  import { useClient } from '@dxos/react-client';
10
10
  import { getSpace, useSchema } from '@dxos/react-client/echo';
11
11
  import { type CustomInputMap, Form, SelectInput } from '@dxos/react-ui-form';
12
- import { Kanban } from '@dxos/react-ui-kanban/types';
13
- import { ProjectionModel, type View, getTypenameFromQuery } from '@dxos/schema';
12
+ import { useProjectionModel } from '@dxos/react-ui-kanban';
13
+ import { type Kanban } from '@dxos/react-ui-kanban/types';
14
+ import { getTypenameFromQuery } from '@dxos/schema';
14
15
 
15
- type KanbanViewEditorProps = { view: View.View };
16
+ import { SettingsSchema } from '../types';
16
17
 
17
- export const KanbanViewEditor = ({ view }: KanbanViewEditorProps) => {
18
+ type KanbanViewEditorProps = { object: Kanban.Kanban };
19
+
20
+ export const KanbanViewEditor = ({ object }: KanbanViewEditorProps) => {
18
21
  const client = useClient();
19
- const space = getSpace(view);
20
- const currentTypename = view.query ? getTypenameFromQuery(view.query.ast) : undefined;
22
+ const space = getSpace(object);
23
+ const view = object.view.target;
24
+ const currentTypename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
21
25
  const schema = useSchema(client, space, currentTypename);
22
-
23
- const projection = useMemo(() => {
24
- if (schema) {
25
- const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : Type.toJsonSchema(schema);
26
- const projection = new ProjectionModel(jsonSchema, view.projection);
27
- projection.normalizeView();
28
- return projection;
29
- }
30
- }, [view.projection, JSON.stringify(schema)]);
26
+ const projection = useProjectionModel(schema, object);
31
27
 
32
28
  const fieldProjections = projection?.getFieldProjections() || [];
33
29
  const selectFields = fieldProjections
34
- .filter((field) => field.props.format === FormatEnum.SingleSelect)
30
+ .filter((field) => field.props.format === Format.TypeFormat.SingleSelect)
35
31
  .map(({ field }) => ({ value: field.id, label: field.path }));
36
32
 
37
33
  const handleSave = useCallback(
38
34
  (values: Partial<{ columnFieldId: string }>) => {
35
+ invariant(view);
39
36
  view.projection.pivotFieldId = values.columnFieldId;
40
37
  },
41
38
  [view],
42
39
  );
43
40
 
44
41
  const initialValues = useMemo(
45
- () => ({ columnFieldId: view.projection.pivotFieldId }),
46
- [view.projection.pivotFieldId],
42
+ () => ({ columnFieldId: view?.projection.pivotFieldId }),
43
+ [view?.projection.pivotFieldId],
47
44
  );
48
45
  const custom: CustomInputMap = useMemo(
49
46
  () => ({ columnFieldId: (props) => <SelectInput {...props} options={selectFields} /> }),
@@ -53,7 +50,7 @@ export const KanbanViewEditor = ({ view }: KanbanViewEditorProps) => {
53
50
  return (
54
51
  <Form
55
52
  Custom={custom}
56
- schema={Kanban.SettingsSchema}
53
+ schema={SettingsSchema}
57
54
  values={initialValues}
58
55
  onSave={handleSave}
59
56
  autoSave
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Type } from '@dxos/echo';
5
6
  import { type Resource } from '@dxos/react-ui';
6
7
  import { Kanban } from '@dxos/react-ui-kanban/types';
7
8
 
@@ -10,24 +11,23 @@ import { meta } from './meta';
10
11
  export const translations = [
11
12
  {
12
13
  'en-US': {
13
- [Kanban.Kanban.typename]: {
14
+ [Type.getTypename(Kanban.Kanban)]: {
14
15
  'typename label': 'Kanban',
15
16
  'typename label_zero': 'Kanbans',
16
17
  'typename label_one': 'Kanban',
17
18
  'typename label_other': 'Kanbans',
18
19
  'object name placeholder': 'New kanban',
20
+ 'rename object label': 'Rename kanban',
21
+ 'delete object label': 'Delete kanban',
22
+ 'object deleted label': 'Kanban deleted',
19
23
  },
20
24
  [meta.id]: {
21
25
  'plugin name': 'Kanban',
22
26
  'kanban title label': 'Title',
23
27
  'column title label': 'Column title',
24
28
  'column title placeholder': 'New column',
25
- 'item title label': 'Item title',
26
- 'item title placeholder': 'New item',
27
29
  'add column label': 'Add column',
28
- 'add item label': 'Add card',
29
30
  'delete column label': 'Delete column',
30
- 'delete item label': 'Delete card',
31
31
  'card field deleted label': 'Card field deleted',
32
32
  'card deleted label': 'Card deleted',
33
33
  },
@@ -4,8 +4,10 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
+ import { TypeInputOptionsAnnotation } from '@dxos/plugin-space/types';
7
8
  import { SpaceSchema } from '@dxos/react-client/echo';
8
- import { FieldSchema, TypenameAnnotationId, View } from '@dxos/schema';
9
+ import { Kanban } from '@dxos/react-ui-kanban/types';
10
+ import { FieldSchema, View } from '@dxos/schema';
9
11
 
10
12
  import { meta } from '../meta';
11
13
 
@@ -21,14 +23,23 @@ import { meta } from '../meta';
21
23
  // TODO(wittjosiah): Factor out?
22
24
  export const PivotColumnAnnotationId = Symbol.for('@dxos/plugin-kanban/annotation/PivotColumn');
23
25
 
26
+ export const SettingsSchema = Schema.Struct({
27
+ columnFieldId: Schema.String.annotations({
28
+ title: 'Column field identifier',
29
+ }),
30
+ });
31
+
24
32
  export const CreateKanbanSchema = Schema.Struct({
25
33
  name: Schema.optional(Schema.String),
26
34
  // TODO(wittjosiah): This should be a query input instead.
27
- typename: Schema.optional(
28
- Schema.String.annotations({
29
- [TypenameAnnotationId]: ['used-static', 'dynamic'],
30
- title: 'Select card record type (leave empty to start fresh)',
35
+ typename: Schema.String.pipe(
36
+ Schema.annotations({ title: 'Select card type' }),
37
+ TypeInputOptionsAnnotation.set({
38
+ location: ['database', 'runtime'],
39
+ kind: ['user'],
40
+ registered: ['registered'],
31
41
  }),
42
+ Schema.optional,
32
43
  ),
33
44
  initialPivotColumn: Schema.optional(
34
45
  Schema.String.annotations({
@@ -44,7 +55,7 @@ export namespace KanbanAction {
44
55
  export class Create extends Schema.TaggedClass<Create>()(`${KANBAN_ACTION}/create`, {
45
56
  input: Schema.extend(Schema.Struct({ space: SpaceSchema }), CreateKanbanSchema),
46
57
  output: Schema.Struct({
47
- object: View.View,
58
+ object: Kanban.Kanban,
48
59
  }),
49
60
  }) {}
50
61
 
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/types/schema.ts", "../../../src/meta.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { SpaceSchema } from '@dxos/react-client/echo';\nimport { FieldSchema, TypenameAnnotationId, View } from '@dxos/schema';\n\nimport { meta } from '../meta';\n\n/**\n * Kanban data model.\n * A Kanban board is a collection of columns, each of which contains a collection of items.\n * The layout of columns and items is controlled by models.\n * The underlying data model may be represented by direct object relationships\n * (e.g., a column object containing an array of ordered items) or projections constructed\n * by the model (e.g., a query of items based on metadata within a column object).\n */\n\n// TODO(wittjosiah): Factor out?\nexport const PivotColumnAnnotationId = Symbol.for('@dxos/plugin-kanban/annotation/PivotColumn');\n\nexport const CreateKanbanSchema = Schema.Struct({\n name: Schema.optional(Schema.String),\n // TODO(wittjosiah): This should be a query input instead.\n typename: Schema.optional(\n Schema.String.annotations({\n [TypenameAnnotationId]: ['used-static', 'dynamic'],\n title: 'Select card record type (leave empty to start fresh)',\n }),\n ),\n initialPivotColumn: Schema.optional(\n Schema.String.annotations({\n [PivotColumnAnnotationId]: true,\n title: 'Pivot column',\n }),\n ),\n});\n\nexport namespace KanbanAction {\n const KANBAN_ACTION = `${meta.id}/action`;\n\n export class Create extends Schema.TaggedClass<Create>()(`${KANBAN_ACTION}/create`, {\n input: Schema.extend(Schema.Struct({ space: SpaceSchema }), CreateKanbanSchema),\n output: Schema.Struct({\n object: View.View,\n }),\n }) {}\n\n export class DeleteCardField extends Schema.TaggedClass<DeleteCardField>()(`${KANBAN_ACTION}/delete-card-field`, {\n input: Schema.Struct({\n view: View.View,\n fieldId: Schema.String,\n // TODO(wittjosiah): Separate fields for undo data?\n deletionData: Schema.optional(\n Schema.Struct({\n field: FieldSchema,\n // TODO(wittjosiah): This creates a type error.\n // props: PropertySchema,\n props: Schema.Any,\n index: Schema.Number,\n }),\n ),\n }),\n output: Schema.Void,\n }) {}\n\n export class DeleteCard extends Schema.TaggedClass<DeleteCard>()(`${KANBAN_ACTION}/delete-card`, {\n input: Schema.Struct({\n card: Schema.Any, // The card object to delete\n }),\n output: Schema.Void,\n }) {}\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type PluginMeta } from '@dxos/app-framework';\nimport { trim } from '@dxos/util';\n\nexport const meta: PluginMeta = {\n id: 'dxos.org/plugin/kanban',\n name: 'Kanban',\n description: trim`\n Visual project management using customizable kanban boards to track workflow progress.\n Organize table data into columns, drag and drop items between stages, and trigger automations based on status changes.\n `,\n icon: 'ph--kanban--regular',\n iconHue: 'green',\n source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-kanban',\n screenshots: ['https://dxos.network/plugin-details-kanban-dark.png'],\n};\n"],
5
- "mappings": ";AAIA,YAAYA,YAAY;AAExB,SAASC,mBAAmB;AAC5B,SAASC,aAAaC,sBAAsBC,YAAY;;;ACFxD,SAASC,YAAY;AAEd,IAAMC,OAAmB;EAC9BC,IAAI;EACJC,MAAM;EACNC,aAAaC;;;;EAIbC,MAAM;EACNC,SAAS;EACTC,QAAQ;EACRC,aAAa;IAAC;;AAChB;;;ADGO,IAAMC,0BAA0BC,OAAOC,IAAI,4CAAA;AAE3C,IAAMC,qBAA4BC,cAAO;EAC9CC,MAAaC,gBAAgBC,aAAM;;EAEnCC,UAAiBF,gBACRC,cAAOE,YAAY;IACxB,CAACC,oBAAAA,GAAuB;MAAC;MAAe;;IACxCC,OAAO;EACT,CAAA,CAAA;EAEFC,oBAA2BN,gBAClBC,cAAOE,YAAY;IACxB,CAACT,uBAAAA,GAA0B;IAC3BW,OAAO;EACT,CAAA,CAAA;AAEJ,CAAA;UAEiBE,eAAAA;AACf,QAAMC,gBAAgB,GAAGC,KAAKC,EAAE;EAEzB,MAAMC,eAAsBC,mBAAW,EAAW,GAAGJ,aAAAA,WAAwB;IAClFK,OAAcC,cAAchB,cAAO;MAAEiB,OAAOC;IAAY,CAAA,GAAInB,kBAAAA;IAC5DoB,QAAenB,cAAO;MACpBoB,QAAQC,KAAKA;IACf,CAAA;EACF,CAAA,EAAA;EAAI;gBALSR,SAAAA;EAON,MAAMS,wBAA+BR,mBAAW,EAAoB,GAAGJ,aAAAA,sBAAmC;IAC/GK,OAAcf,cAAO;MACnBuB,MAAMF,KAAKA;MACXG,SAAgBrB;;MAEhBsB,cAAqBvB,gBACZF,cAAO;QACZ0B,OAAOC;;;QAGPC,OAAcC;QACdC,OAAcC;MAChB,CAAA,CAAA;IAEJ,CAAA;IACAZ,QAAea;EACjB,CAAA,EAAA;EAAI;gBAhBSV,kBAAAA;EAkBN,MAAMW,mBAA0BnB,mBAAW,EAAe,GAAGJ,aAAAA,gBAA6B;IAC/FK,OAAcf,cAAO;MACnBkC,MAAaL;IACf,CAAA;IACAV,QAAea;EACjB,CAAA,EAAA;EAAI;gBALSC,aAAAA;AAMf,GAlCiBxB,iBAAAA,eAAAA,CAAAA,EAAAA;;",
6
- "names": ["Schema", "SpaceSchema", "FieldSchema", "TypenameAnnotationId", "View", "trim", "meta", "id", "name", "description", "trim", "icon", "iconHue", "source", "screenshots", "PivotColumnAnnotationId", "Symbol", "for", "CreateKanbanSchema", "Struct", "name", "optional", "String", "typename", "annotations", "TypenameAnnotationId", "title", "initialPivotColumn", "KanbanAction", "KANBAN_ACTION", "meta", "id", "Create", "TaggedClass", "input", "extend", "space", "SpaceSchema", "output", "object", "View", "DeleteCardField", "view", "fieldId", "deletionData", "field", "FieldSchema", "props", "Any", "index", "Number", "Void", "DeleteCard", "card"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/capabilities/intent-resolver.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Capabilities, type PluginContext, contributes, createResolver } from '@dxos/app-framework';\nimport { invariant } from '@dxos/invariant';\nimport { ClientCapabilities } from '@dxos/plugin-client';\nimport { getSpace } from '@dxos/react-client/echo';\nimport { Kanban } from '@dxos/react-ui-kanban/types';\nimport { ProjectionModel, getTypenameFromQuery } from '@dxos/schema';\n\nimport { meta } from '../meta';\nimport { KanbanAction } from '../types';\n\nexport default (context: PluginContext) =>\n contributes(Capabilities.IntentResolver, [\n createResolver({\n intent: KanbanAction.Create,\n resolve: async ({ space, name, typename, initialPivotColumn }) => {\n const client = context.getCapability(ClientCapabilities.Client);\n const { view } = await Kanban.makeView({\n client,\n space,\n name,\n typename,\n pivotFieldName: initialPivotColumn,\n });\n return { data: { object: view } };\n },\n }),\n createResolver({\n intent: KanbanAction.DeleteCardField,\n resolve: async ({ view, fieldId, deletionData }, undo) => {\n const schema = getSpace(view)?.db.schemaRegistry.getSchema(getTypenameFromQuery(view.query.ast)!);\n invariant(schema);\n const projection = new ProjectionModel(schema.jsonSchema, view.projection);\n\n if (!undo) {\n const { deleted, index } = projection.deleteFieldProjection(fieldId);\n return {\n undoable: {\n message: ['card field deleted label', { ns: meta.id }],\n data: { deletionData: { ...deleted, index } },\n },\n };\n } else if (undo && deletionData) {\n const { field, props, index } = deletionData;\n projection.setFieldProjection({ field, props }, index);\n }\n },\n }),\n createResolver({\n intent: KanbanAction.DeleteCard,\n resolve: ({ card }, undo) => {\n const space = getSpace(card);\n invariant(space);\n\n if (!undo) {\n space.db.remove(card);\n return {\n undoable: {\n message: ['card deleted label', { ns: meta.id }],\n data: { card },\n },\n };\n } else {\n space.db.add(card);\n }\n },\n }),\n ]);\n"],
5
- "mappings": ";;;;;;AAIA,SAASA,cAAkCC,aAAaC,sBAAsB;AAC9E,SAASC,iBAAiB;AAC1B,SAASC,0BAA0B;AACnC,SAASC,gBAAgB;AACzB,SAASC,cAAc;AACvB,SAASC,iBAAiBC,4BAA4B;;AAKtD,IAAA,2BAAe,CAACC,YACdC,YAAYC,aAAaC,gBAAgB;EACvCC,eAAe;IACbC,QAAQC,aAAaC;IACrBC,SAAS,OAAO,EAAEC,OAAOC,MAAMC,UAAUC,mBAAkB,MAAE;AAC3D,YAAMC,SAASb,QAAQc,cAAcC,mBAAmBC,MAAM;AAC9D,YAAM,EAAEC,KAAI,IAAK,MAAMC,OAAOC,SAAS;QACrCN;QACAJ;QACAC;QACAC;QACAS,gBAAgBR;MAClB,CAAA;AACA,aAAO;QAAES,MAAM;UAAEC,QAAQL;QAAK;MAAE;IAClC;EACF,CAAA;EACAb,eAAe;IACbC,QAAQC,aAAaiB;IACrBf,SAAS,OAAO,EAAES,MAAMO,SAASC,aAAY,GAAIC,SAAAA;AAC/C,YAAMC,SAASC,SAASX,IAAAA,GAAOY,GAAGC,eAAeC,UAAUC,qBAAqBf,KAAKgB,MAAMC,GAAG,CAAA;AAC9FC,gBAAUR,QAAAA,QAAAA;;;;;;;;;AACV,YAAMS,aAAa,IAAIC,gBAAgBV,OAAOW,YAAYrB,KAAKmB,UAAU;AAEzE,UAAI,CAACV,MAAM;AACT,cAAM,EAAEa,SAASC,MAAK,IAAKJ,WAAWK,sBAAsBjB,OAAAA;AAC5D,eAAO;UACLkB,UAAU;YACRC,SAAS;cAAC;cAA4B;gBAAEC,IAAIC,KAAKC;cAAG;;YACpDzB,MAAM;cAAEI,cAAc;gBAAE,GAAGc;gBAASC;cAAM;YAAE;UAC9C;QACF;MACF,WAAWd,QAAQD,cAAc;AAC/B,cAAM,EAAEsB,OAAOC,OAAOR,MAAK,IAAKf;AAChCW,mBAAWa,mBAAmB;UAAEF;UAAOC;QAAM,GAAGR,KAAAA;MAClD;IACF;EACF,CAAA;EACApC,eAAe;IACbC,QAAQC,aAAa4C;IACrB1C,SAAS,CAAC,EAAE2C,KAAI,GAAIzB,SAAAA;AAClB,YAAMjB,QAAQmB,SAASuB,IAAAA;AACvBhB,gBAAU1B,OAAAA,QAAAA;;;;;;;;;AAEV,UAAI,CAACiB,MAAM;AACTjB,cAAMoB,GAAGuB,OAAOD,IAAAA;AAChB,eAAO;UACLT,UAAU;YACRC,SAAS;cAAC;cAAsB;gBAAEC,IAAIC,KAAKC;cAAG;;YAC9CzB,MAAM;cAAE8B;YAAK;UACf;QACF;MACF,OAAO;AACL1C,cAAMoB,GAAGwB,IAAIF,IAAAA;MACf;IACF;EACF,CAAA;CACD;",
6
- "names": ["Capabilities", "contributes", "createResolver", "invariant", "ClientCapabilities", "getSpace", "Kanban", "ProjectionModel", "getTypenameFromQuery", "context", "contributes", "Capabilities", "IntentResolver", "createResolver", "intent", "KanbanAction", "Create", "resolve", "space", "name", "typename", "initialPivotColumn", "client", "getCapability", "ClientCapabilities", "Client", "view", "Kanban", "makeView", "pivotFieldName", "data", "object", "DeleteCardField", "fieldId", "deletionData", "undo", "schema", "getSpace", "db", "schemaRegistry", "getSchema", "getTypenameFromQuery", "query", "ast", "invariant", "projection", "ProjectionModel", "jsonSchema", "deleted", "index", "deleteFieldProjection", "undoable", "message", "ns", "meta", "id", "field", "props", "setFieldProjection", "DeleteCard", "card", "remove", "add"]
7
- }