@dxos/plugin-pipeline 0.8.3-main.ef1bc66f44
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/LICENSE +8 -0
- package/README.md +13 -0
- package/dist/lib/browser/PipelineContainer-TRO66W6L.mjs +216 -0
- package/dist/lib/browser/PipelineContainer-TRO66W6L.mjs.map +7 -0
- package/dist/lib/browser/PipelineSettings-EBR3UU5C.mjs +212 -0
- package/dist/lib/browser/PipelineSettings-EBR3UU5C.mjs.map +7 -0
- package/dist/lib/browser/app-graph-builder-NKUZQ6A7.mjs +64 -0
- package/dist/lib/browser/app-graph-builder-NKUZQ6A7.mjs.map +7 -0
- package/dist/lib/browser/chunk-J5YNPKHT.mjs +28 -0
- package/dist/lib/browser/chunk-J5YNPKHT.mjs.map +7 -0
- package/dist/lib/browser/chunk-XDHJKK7D.mjs +42 -0
- package/dist/lib/browser/chunk-XDHJKK7D.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +108 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/browser/react-surface-CH2SUJTQ.mjs +56 -0
- package/dist/lib/browser/react-surface-CH2SUJTQ.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +10 -0
- package/dist/lib/browser/types/index.mjs.map +7 -0
- package/dist/types/src/PipelinePlugin.d.ts +3 -0
- package/dist/types/src/PipelinePlugin.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +4 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +2 -0
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
- package/dist/types/src/components/PipelineColumn.d.ts +10 -0
- package/dist/types/src/components/PipelineColumn.d.ts.map +1 -0
- package/dist/types/src/components/PipelineComponent.d.ts +38 -0
- package/dist/types/src/components/PipelineComponent.d.ts.map +1 -0
- package/dist/types/src/components/PipelineComponent.stories.d.ts +42 -0
- package/dist/types/src/components/PipelineComponent.stories.d.ts.map +1 -0
- package/dist/types/src/components/PipelineContainer.d.ts +7 -0
- package/dist/types/src/components/PipelineContainer.d.ts.map +1 -0
- package/dist/types/src/components/PipelineContainer.stories.d.ts +56 -0
- package/dist/types/src/components/PipelineContainer.stories.d.ts.map +1 -0
- package/dist/types/src/components/PipelineSettings.d.ts +12 -0
- package/dist/types/src/components/PipelineSettings.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +3 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/templates/index.d.ts +2 -0
- package/dist/types/src/templates/index.d.ts.map +1 -0
- package/dist/types/src/templates/research.d.ts +4 -0
- package/dist/types/src/templates/research.d.ts.map +1 -0
- package/dist/types/src/templates/templates.d.ts +4 -0
- package/dist/types/src/templates/templates.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +31 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/PipelineAction.d.ts +25 -0
- package/dist/types/src/types/PipelineAction.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +3 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +98 -0
- package/src/PipelinePlugin.tsx +46 -0
- package/src/capabilities/app-graph-builder/app-graph-builder.ts +51 -0
- package/src/capabilities/app-graph-builder/index.ts +7 -0
- package/src/capabilities/index.ts +7 -0
- package/src/capabilities/operation-resolver/index.ts +5 -0
- package/src/capabilities/operation-resolver/operation-resolver.ts +29 -0
- package/src/capabilities/react-surface/index.ts +7 -0
- package/src/capabilities/react-surface/react-surface.tsx +50 -0
- package/src/components/PipelineColumn.tsx +145 -0
- package/src/components/PipelineComponent.stories.tsx +204 -0
- package/src/components/PipelineComponent.tsx +103 -0
- package/src/components/PipelineContainer.stories.tsx +226 -0
- package/src/components/PipelineContainer.tsx +57 -0
- package/src/components/PipelineSettings.tsx +230 -0
- package/src/components/index.ts +11 -0
- package/src/index.ts +6 -0
- package/src/meta.ts +19 -0
- package/src/templates/index.ts +5 -0
- package/src/templates/research.ts +71 -0
- package/src/templates/templates.ts +11 -0
- package/src/translations.ts +38 -0
- package/src/types/PipelineAction.ts +29 -0
- package/src/types/index.ts +6 -0
package/package.json
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/plugin-pipeline",
|
|
3
|
+
"version": "0.8.3-main.ef1bc66f44",
|
|
4
|
+
"description": "Pipeline plugin for Composer",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "DXOS.org",
|
|
13
|
+
"sideEffects": true,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"source": "./src/index.ts",
|
|
18
|
+
"types": "./dist/types/src/index.d.ts",
|
|
19
|
+
"browser": "./dist/lib/browser/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./types": {
|
|
22
|
+
"source": "./src/types/index.ts",
|
|
23
|
+
"types": "./dist/types/src/types/index.d.ts",
|
|
24
|
+
"browser": "./dist/lib/browser/types/index.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"types": "dist/types/src/index.d.ts",
|
|
28
|
+
"typesVersions": {
|
|
29
|
+
"*": {
|
|
30
|
+
"types": [
|
|
31
|
+
"dist/types/src/types/index.d.ts"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"src"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@radix-ui/react-compose-refs": "1.1.1",
|
|
41
|
+
"@radix-ui/react-context": "1.1.1",
|
|
42
|
+
"effect": "3.19.16",
|
|
43
|
+
"@dxos/app-toolkit": "0.8.3-main.ef1bc66f44",
|
|
44
|
+
"@dxos/devtools": "0.8.3-main.ef1bc66f44",
|
|
45
|
+
"@dxos/async": "0.8.3-main.ef1bc66f44",
|
|
46
|
+
"@dxos/assistant-toolkit": "0.8.3-main.ef1bc66f44",
|
|
47
|
+
"@dxos/echo": "0.8.3-main.ef1bc66f44",
|
|
48
|
+
"@dxos/operation": "0.8.3-main.ef1bc66f44",
|
|
49
|
+
"@dxos/functions": "0.8.3-main.ef1bc66f44",
|
|
50
|
+
"@dxos/plugin-deck": "0.8.3-main.ef1bc66f44",
|
|
51
|
+
"@dxos/plugin-client": "0.8.3-main.ef1bc66f44",
|
|
52
|
+
"@dxos/app-framework": "0.8.3-main.ef1bc66f44",
|
|
53
|
+
"@dxos/plugin-graph": "0.8.3-main.ef1bc66f44",
|
|
54
|
+
"@dxos/plugin-inbox": "0.8.3-main.ef1bc66f44",
|
|
55
|
+
"@dxos/plugin-markdown": "0.8.3-main.ef1bc66f44",
|
|
56
|
+
"@dxos/log": "0.8.3-main.ef1bc66f44",
|
|
57
|
+
"@dxos/react-client": "0.8.3-main.ef1bc66f44",
|
|
58
|
+
"@dxos/react-ui-list": "0.8.3-main.ef1bc66f44",
|
|
59
|
+
"@dxos/react-ui-components": "0.8.3-main.ef1bc66f44",
|
|
60
|
+
"@dxos/react-ui-attention": "0.8.3-main.ef1bc66f44",
|
|
61
|
+
"@dxos/react-ui-stack": "0.8.3-main.ef1bc66f44",
|
|
62
|
+
"@dxos/react-ui-mosaic": "0.8.3-main.ef1bc66f44",
|
|
63
|
+
"@dxos/echo-query": "0.8.3-main.ef1bc66f44",
|
|
64
|
+
"@dxos/schema": "0.8.3-main.ef1bc66f44",
|
|
65
|
+
"@dxos/react-ui-form": "0.8.3-main.ef1bc66f44",
|
|
66
|
+
"@dxos/util": "0.8.3-main.ef1bc66f44",
|
|
67
|
+
"@dxos/plugin-space": "0.8.3-main.ef1bc66f44",
|
|
68
|
+
"@dxos/types": "0.8.3-main.ef1bc66f44"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
72
|
+
"@types/react": "~19.2.7",
|
|
73
|
+
"@types/react-dom": "~19.2.3",
|
|
74
|
+
"react": "~19.2.3",
|
|
75
|
+
"react-dom": "~19.2.3",
|
|
76
|
+
"vite": "7.1.9",
|
|
77
|
+
"@dxos/plugin-inbox": "0.8.3-main.ef1bc66f44",
|
|
78
|
+
"@dxos/plugin-attention": "0.8.3-main.ef1bc66f44",
|
|
79
|
+
"@dxos/plugin-preview": "0.8.3-main.ef1bc66f44",
|
|
80
|
+
"@dxos/plugin-testing": "0.8.3-main.ef1bc66f44",
|
|
81
|
+
"@dxos/random": "0.8.3-main.ef1bc66f44",
|
|
82
|
+
"@dxos/react-ui": "0.8.3-main.ef1bc66f44",
|
|
83
|
+
"@dxos/plugin-theme": "0.8.3-main.ef1bc66f44",
|
|
84
|
+
"@dxos/ui-theme": "0.8.3-main.ef1bc66f44",
|
|
85
|
+
"@dxos/storybook-utils": "0.8.3-main.ef1bc66f44"
|
|
86
|
+
},
|
|
87
|
+
"peerDependencies": {
|
|
88
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
89
|
+
"effect": "3.19.16",
|
|
90
|
+
"react": "~19.2.3",
|
|
91
|
+
"react-dom": "~19.2.3",
|
|
92
|
+
"@dxos/react-ui": "0.8.3-main.ef1bc66f44",
|
|
93
|
+
"@dxos/ui-theme": "0.8.3-main.ef1bc66f44"
|
|
94
|
+
},
|
|
95
|
+
"publishConfig": {
|
|
96
|
+
"access": "public"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability, Plugin } from '@dxos/app-framework';
|
|
8
|
+
import { AppPlugin } from '@dxos/app-toolkit';
|
|
9
|
+
import { Operation } from '@dxos/operation';
|
|
10
|
+
import { SpaceCapabilities, SpaceEvents } from '@dxos/plugin-space';
|
|
11
|
+
import { type CreateObject } from '@dxos/plugin-space/types';
|
|
12
|
+
import { Pipeline } from '@dxos/types';
|
|
13
|
+
|
|
14
|
+
import { AppGraphBuilder, OperationResolver, ReactSurface } from './capabilities';
|
|
15
|
+
import { meta } from './meta';
|
|
16
|
+
import { translations } from './translations';
|
|
17
|
+
import { PipelineOperation } from './types';
|
|
18
|
+
|
|
19
|
+
export const PipelinePlugin = Plugin.define(meta).pipe(
|
|
20
|
+
AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),
|
|
21
|
+
AppPlugin.addMetadataModule({
|
|
22
|
+
metadata: {
|
|
23
|
+
id: Pipeline.Pipeline.typename,
|
|
24
|
+
metadata: {
|
|
25
|
+
icon: 'ph--path--regular',
|
|
26
|
+
iconHue: 'purple',
|
|
27
|
+
createObject: ((props) => Effect.sync(() => Pipeline.make(props))) satisfies CreateObject,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
AppPlugin.addOperationResolverModule({ activate: OperationResolver }),
|
|
32
|
+
AppPlugin.addSchemaModule({ schema: [Pipeline.Pipeline] }),
|
|
33
|
+
AppPlugin.addSurfaceModule({ activate: ReactSurface }),
|
|
34
|
+
AppPlugin.addTranslationsModule({ translations }),
|
|
35
|
+
Plugin.addModule({
|
|
36
|
+
id: 'on-space-created',
|
|
37
|
+
activatesOn: SpaceEvents.SpaceCreated,
|
|
38
|
+
activate: () =>
|
|
39
|
+
Effect.succeed(
|
|
40
|
+
Capability.contributes(SpaceCapabilities.OnCreateSpace, (params) =>
|
|
41
|
+
Operation.invoke(PipelineOperation.OnCreateSpace, params),
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
}),
|
|
45
|
+
Plugin.make,
|
|
46
|
+
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
9
|
+
import { ATTENDABLE_PATH_SEPARATOR, PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
|
|
10
|
+
import { GraphBuilder } from '@dxos/plugin-graph';
|
|
11
|
+
import { Pipeline } from '@dxos/types';
|
|
12
|
+
|
|
13
|
+
import { meta } from '../../meta';
|
|
14
|
+
|
|
15
|
+
export default Capability.makeModule(
|
|
16
|
+
Effect.fnUntraced(function* () {
|
|
17
|
+
const extensions = yield* Effect.all([
|
|
18
|
+
GraphBuilder.createTypeExtension({
|
|
19
|
+
id: `${meta.id}/triggers`,
|
|
20
|
+
type: Pipeline.Pipeline,
|
|
21
|
+
connector: (pipeline) => {
|
|
22
|
+
const nodeId = pipeline.id;
|
|
23
|
+
return Effect.succeed([
|
|
24
|
+
{
|
|
25
|
+
id: [nodeId, 'invocations'].join(ATTENDABLE_PATH_SEPARATOR),
|
|
26
|
+
type: PLANK_COMPANION_TYPE,
|
|
27
|
+
data: 'invocations',
|
|
28
|
+
properties: {
|
|
29
|
+
label: ['pipeline invocations label', { ns: meta.id }],
|
|
30
|
+
icon: 'ph--clock-countdown--regular',
|
|
31
|
+
disposition: 'hidden',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: [nodeId, 'automation'].join(ATTENDABLE_PATH_SEPARATOR),
|
|
36
|
+
type: PLANK_COMPANION_TYPE,
|
|
37
|
+
data: 'automation',
|
|
38
|
+
properties: {
|
|
39
|
+
label: ['pipeline automation label', { ns: meta.id }],
|
|
40
|
+
icon: 'ph--lightning--regular',
|
|
41
|
+
disposition: 'hidden',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import { Obj, Ref, Type } from '@dxos/echo';
|
|
9
|
+
import { OperationResolver } from '@dxos/operation';
|
|
10
|
+
import { Collection } from '@dxos/schema';
|
|
11
|
+
import { Pipeline } from '@dxos/types';
|
|
12
|
+
|
|
13
|
+
import { PipelineOperation } from '../../types';
|
|
14
|
+
|
|
15
|
+
export default Capability.makeModule(() =>
|
|
16
|
+
Effect.succeed(
|
|
17
|
+
Capability.contributes(Capabilities.OperationResolver, [
|
|
18
|
+
OperationResolver.make({
|
|
19
|
+
operation: PipelineOperation.OnCreateSpace,
|
|
20
|
+
handler: Effect.fnUntraced(function* ({ rootCollection }) {
|
|
21
|
+
const collection = Collection.makeManaged({ key: Type.getTypename(Pipeline.Pipeline) });
|
|
22
|
+
Obj.change(rootCollection, (c) => {
|
|
23
|
+
c.objects.push(Ref.make(collection));
|
|
24
|
+
});
|
|
25
|
+
}),
|
|
26
|
+
}),
|
|
27
|
+
]),
|
|
28
|
+
),
|
|
29
|
+
);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
9
|
+
import { Surface } from '@dxos/app-framework/ui';
|
|
10
|
+
import { InvocationTraceContainer } from '@dxos/devtools';
|
|
11
|
+
import { Obj } from '@dxos/echo';
|
|
12
|
+
import { Layout } from '@dxos/react-ui';
|
|
13
|
+
import { Pipeline } from '@dxos/types';
|
|
14
|
+
|
|
15
|
+
import { PipelineContainer, PipelineObjectSettings } from '../../components';
|
|
16
|
+
import { meta } from '../../meta';
|
|
17
|
+
|
|
18
|
+
export default Capability.makeModule(() =>
|
|
19
|
+
Effect.succeed(
|
|
20
|
+
Capability.contributes(Capabilities.ReactSurface, [
|
|
21
|
+
Surface.create({
|
|
22
|
+
id: meta.id,
|
|
23
|
+
role: 'article',
|
|
24
|
+
filter: (data): data is { subject: Pipeline.Pipeline } => Obj.instanceOf(Pipeline.Pipeline, data.subject),
|
|
25
|
+
component: ({ data, role }) => <PipelineContainer role={role} subject={data.subject} />,
|
|
26
|
+
}),
|
|
27
|
+
Surface.create({
|
|
28
|
+
id: `${meta.id}/companion/invocations`,
|
|
29
|
+
role: 'article',
|
|
30
|
+
filter: (data): data is { companionTo: Pipeline.Pipeline } =>
|
|
31
|
+
Obj.instanceOf(Pipeline.Pipeline, data.companionTo) && data.subject === 'invocations',
|
|
32
|
+
component: ({ data, role }) => {
|
|
33
|
+
const db = Obj.getDatabase(data.companionTo);
|
|
34
|
+
// TODO(wittjosiah): Filter the invocations to those relevant to the project.
|
|
35
|
+
return (
|
|
36
|
+
<Layout.Main role={role}>
|
|
37
|
+
<InvocationTraceContainer db={db} detailAxis='block' />
|
|
38
|
+
</Layout.Main>
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
Surface.create({
|
|
43
|
+
id: `${meta.id}/object-settings`,
|
|
44
|
+
role: 'object-settings',
|
|
45
|
+
filter: (data): data is { subject: Pipeline.Pipeline } => Obj.instanceOf(Pipeline.Pipeline, data.subject),
|
|
46
|
+
component: ({ data }) => <PipelineObjectSettings pipeline={data.subject} />,
|
|
47
|
+
}),
|
|
48
|
+
]),
|
|
49
|
+
),
|
|
50
|
+
);
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useComposedRefs } from '@radix-ui/react-compose-refs';
|
|
6
|
+
import type * as Schema from 'effect/Schema';
|
|
7
|
+
import React, { forwardRef, useMemo, useRef, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import { Obj, Query, Type } from '@dxos/echo';
|
|
10
|
+
import { getQueryTarget, resolveSchemaWithRegistry } from '@dxos/plugin-space';
|
|
11
|
+
import { Filter, getSpace, isSpace, useObject, useQuery } from '@dxos/react-client/echo';
|
|
12
|
+
import { ScrollArea, useAsyncEffect, useTranslation } from '@dxos/react-ui';
|
|
13
|
+
import { Card, Focus, Mosaic, type MosaicTileProps } from '@dxos/react-ui-mosaic';
|
|
14
|
+
import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
|
|
15
|
+
import { type Pipeline } from '@dxos/types';
|
|
16
|
+
|
|
17
|
+
import { meta } from '../meta';
|
|
18
|
+
|
|
19
|
+
import { type ItemProps, usePipeline } from './PipelineComponent';
|
|
20
|
+
|
|
21
|
+
//
|
|
22
|
+
// PipelineColumn
|
|
23
|
+
//
|
|
24
|
+
|
|
25
|
+
const PIPELINE_COLUMN_NAME = 'PipelineColumn';
|
|
26
|
+
|
|
27
|
+
export type PipelineColumnProps = {
|
|
28
|
+
column: Pipeline.Column;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// TODO(thure): Duplicates a lot of the same boilerplate as Kanban columns; is there an opportunity to DRY these out?
|
|
32
|
+
// TODO(wittjosiah): Support column DnD reordering.
|
|
33
|
+
// TODO(wittjosiah): Support item DnD reordering (ordering needs to be stored on the view presentation collection).
|
|
34
|
+
export const PipelineColumn = ({ column }: PipelineColumnProps) => {
|
|
35
|
+
const { t } = useTranslation(meta.id);
|
|
36
|
+
// Subscribe to the view target for reactivity.
|
|
37
|
+
const [viewSnapshot] = useObject(column.view);
|
|
38
|
+
const view = column.view.target;
|
|
39
|
+
const space = getSpace(view);
|
|
40
|
+
const { Item } = usePipeline(PIPELINE_COLUMN_NAME);
|
|
41
|
+
const [schema, setSchema] = useState<Schema.Schema.AnyNoContext>();
|
|
42
|
+
const query = useMemo(() => {
|
|
43
|
+
if (!view) {
|
|
44
|
+
return Query.select(Filter.nothing());
|
|
45
|
+
} else {
|
|
46
|
+
// NOTE: Snapshot is required to prevent signal read in prohibited scope.
|
|
47
|
+
// TODO(wittjosiah): Without stringify, filter.filters remains a proxied array.
|
|
48
|
+
return Query.fromAst(JSON.parse(JSON.stringify(viewSnapshot?.query.ast)));
|
|
49
|
+
}
|
|
50
|
+
}, [JSON.stringify(viewSnapshot?.query.ast)]);
|
|
51
|
+
|
|
52
|
+
useAsyncEffect(async () => {
|
|
53
|
+
if (!query || !space) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const schema = await resolveSchemaWithRegistry(space.db.schemaRegistry, query.ast);
|
|
58
|
+
setSchema(() => schema);
|
|
59
|
+
}, [space, query]);
|
|
60
|
+
|
|
61
|
+
const queryTarget = getQueryTarget(query.ast, space);
|
|
62
|
+
const items = useQuery(queryTarget, query);
|
|
63
|
+
const sortedItems = useMemo(() => {
|
|
64
|
+
// TODO(burdon): Hack to reverse queue.
|
|
65
|
+
return isSpace(queryTarget) ? items : [...items.reverse()];
|
|
66
|
+
}, [queryTarget, items]);
|
|
67
|
+
const projectionModel = useMemo(() => {
|
|
68
|
+
if (!schema || !view) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// For mutable schemas (EchoSchema), use the live jsonSchema reference for reactivity.
|
|
73
|
+
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema : Type.toJsonSchema(schema);
|
|
74
|
+
const change = createEchoChangeCallback(view, Type.isMutable(schema) ? schema : undefined);
|
|
75
|
+
return new ProjectionModel({ view, baseSchema: jsonSchema, change });
|
|
76
|
+
}, [schema, view]);
|
|
77
|
+
|
|
78
|
+
const Tile = useMemo(() => {
|
|
79
|
+
return forwardRef<HTMLDivElement, Pick<MosaicTileProps<Obj.Unknown>, 'classNames' | 'location' | 'data' | 'debug'>>(
|
|
80
|
+
(props, ref) => <ItemTile {...props} ref={ref} itemProps={{ item: props.data, projectionModel }} />,
|
|
81
|
+
);
|
|
82
|
+
}, [Item, projectionModel]);
|
|
83
|
+
|
|
84
|
+
if (!view) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Focus.Group asChild>
|
|
90
|
+
<div className='grid bs-full card-default-width overflow-hidden grid-rows-[min-content_1fr] density-fine border border-separator rounded-md bg-deckSurface'>
|
|
91
|
+
<Card.Toolbar>
|
|
92
|
+
<Card.DragHandle />
|
|
93
|
+
<Card.Title>{column.name ?? t('untitled view title')}</Card.Title>
|
|
94
|
+
<Card.Menu />
|
|
95
|
+
</Card.Toolbar>
|
|
96
|
+
<Mosaic.Container asChild orientation='vertical' withFocus>
|
|
97
|
+
<ScrollArea.Root orientation='vertical'>
|
|
98
|
+
<ScrollArea.Viewport classNames='p-2'>
|
|
99
|
+
<Mosaic.Stack orientation='vertical' items={sortedItems} getId={(item) => item.id} Tile={Tile} />
|
|
100
|
+
</ScrollArea.Viewport>
|
|
101
|
+
</ScrollArea.Root>
|
|
102
|
+
</Mosaic.Container>
|
|
103
|
+
</div>
|
|
104
|
+
</Focus.Group>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
PipelineColumn.displayName = PIPELINE_COLUMN_NAME;
|
|
109
|
+
|
|
110
|
+
//
|
|
111
|
+
// ItemTile
|
|
112
|
+
//
|
|
113
|
+
|
|
114
|
+
const ITEM_TILE_NAME = 'ItemTile';
|
|
115
|
+
|
|
116
|
+
type ItemTileProps = Pick<MosaicTileProps<Obj.Unknown>, 'classNames' | 'location' | 'data' | 'debug'> & {
|
|
117
|
+
itemProps: ItemProps;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const ItemTile = forwardRef<HTMLDivElement, ItemTileProps>(
|
|
121
|
+
({ classNames, data, location, debug, itemProps }, forwardedRef) => {
|
|
122
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
123
|
+
const composedRef = useComposedRefs<HTMLDivElement>(rootRef, forwardedRef);
|
|
124
|
+
const { Item } = usePipeline(ITEM_TILE_NAME);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<Mosaic.Tile asChild id={data.id} data={data} location={location} debug={debug}>
|
|
128
|
+
<Focus.Group asChild>
|
|
129
|
+
<Card.Root classNames={classNames} ref={composedRef}>
|
|
130
|
+
<Card.Toolbar>
|
|
131
|
+
<Card.DragHandle />
|
|
132
|
+
<Card.Title>{Obj.getLabel(data)}</Card.Title>
|
|
133
|
+
<Card.Menu />
|
|
134
|
+
</Card.Toolbar>
|
|
135
|
+
<Card.Content>
|
|
136
|
+
<Item {...itemProps} />
|
|
137
|
+
</Card.Content>
|
|
138
|
+
</Card.Root>
|
|
139
|
+
</Focus.Group>
|
|
140
|
+
</Mosaic.Tile>
|
|
141
|
+
);
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
ItemTile.displayName = ITEM_TILE_NAME;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useCallback, useEffect } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Filter, Ref } from '@dxos/client/echo';
|
|
9
|
+
import { Obj, Query, Type } from '@dxos/echo';
|
|
10
|
+
import { faker } from '@dxos/random';
|
|
11
|
+
import { useQuery } from '@dxos/react-client/echo';
|
|
12
|
+
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
13
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
14
|
+
import { Form, omitId } from '@dxos/react-ui-form';
|
|
15
|
+
import { withMosaic } from '@dxos/react-ui-mosaic/testing';
|
|
16
|
+
import { Collection, View } from '@dxos/schema';
|
|
17
|
+
import { createObjectFactory } from '@dxos/schema/testing';
|
|
18
|
+
import { Person, Pipeline } from '@dxos/types';
|
|
19
|
+
|
|
20
|
+
import { translations } from '../translations';
|
|
21
|
+
|
|
22
|
+
import { type ItemProps, PipelineComponent as PipelineComponent } from './PipelineComponent';
|
|
23
|
+
|
|
24
|
+
const StorybookProjectItem = ({ item, projectionModel }: ItemProps) => {
|
|
25
|
+
if (Obj.instanceOf(Person.Person, item)) {
|
|
26
|
+
const contact = item as Obj.Obj<Person.Person>;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Form.Root schema={omitId(Person.Person)} projection={projectionModel} values={contact} autoSave>
|
|
30
|
+
<Form.Viewport>
|
|
31
|
+
<Form.Content>
|
|
32
|
+
<Form.FieldSet />
|
|
33
|
+
</Form.Content>
|
|
34
|
+
</Form.Viewport>
|
|
35
|
+
</Form.Root>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return <span>{item.id}</span>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const DefaultStory = () => {
|
|
43
|
+
const { space } = useClientStory();
|
|
44
|
+
const pipelines = useQuery(space?.db, Filter.type(Pipeline.Pipeline));
|
|
45
|
+
const pipeline = pipelines[0];
|
|
46
|
+
|
|
47
|
+
const handleAddColumn = useCallback(() => {
|
|
48
|
+
if (!space || !pipeline) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create a new view for contacts similar to the initialization.
|
|
53
|
+
const view = View.make({
|
|
54
|
+
query: Query.select(Filter.type(Person.Person)),
|
|
55
|
+
jsonSchema: Type.toJsonSchema(Person.Person),
|
|
56
|
+
fields: ['fullName'],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
Obj.change(pipeline, (p) => {
|
|
60
|
+
p.columns.push({
|
|
61
|
+
name: 'New Contacts',
|
|
62
|
+
view: Ref.make(view) as (typeof p.columns)[number]['view'],
|
|
63
|
+
order: [],
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return Ref.make(view);
|
|
68
|
+
}, [space, pipeline]);
|
|
69
|
+
|
|
70
|
+
if (!pipeline) {
|
|
71
|
+
return <p>Loading…</p>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<PipelineComponent.Root Item={StorybookProjectItem} onAddColumn={handleAddColumn}>
|
|
76
|
+
<PipelineComponent.Content pipeline={pipeline} />
|
|
77
|
+
</PipelineComponent.Root>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const MutationsStory = () => {
|
|
82
|
+
const { space } = useClientStory();
|
|
83
|
+
const pipelines = useQuery(space?.db, Filter.type(Pipeline.Pipeline));
|
|
84
|
+
const contacts = useQuery(space?.db, Filter.type(Person.Person));
|
|
85
|
+
const pipeline = pipelines[0];
|
|
86
|
+
|
|
87
|
+
const handleAddColumn = useCallback(() => {
|
|
88
|
+
if (!space || !pipeline) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Create a new view for contacts similar to the initialization.
|
|
93
|
+
const view = View.make({
|
|
94
|
+
query: Query.select(Filter.type(Person.Person)),
|
|
95
|
+
jsonSchema: Type.toJsonSchema(Person.Person),
|
|
96
|
+
fields: ['fullName'],
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
Obj.change(pipeline, (p) => {
|
|
100
|
+
p.columns.push({
|
|
101
|
+
name: 'New Contacts',
|
|
102
|
+
view: Ref.make(view) as (typeof p.columns)[number]['view'],
|
|
103
|
+
order: [],
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return view;
|
|
108
|
+
}, [space, pipeline]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!space || !pipeline) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const interval = setInterval(() => {
|
|
116
|
+
const factory = createObjectFactory(space.db, faker as any);
|
|
117
|
+
const p = Math.random();
|
|
118
|
+
|
|
119
|
+
if (p < 0.4) {
|
|
120
|
+
// Append to the name
|
|
121
|
+
const contactToAdjust = faker.helpers.arrayElement(contacts);
|
|
122
|
+
Obj.change(contactToAdjust, (c) => {
|
|
123
|
+
c.fullName = (c.fullName ?? '') + ' X';
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
} else if (p < 0.7 && contacts.length > 1) {
|
|
127
|
+
// Remove a contact (30% chance, but only if we have more than 1)
|
|
128
|
+
const contactToRemove = faker.helpers.arrayElement(contacts);
|
|
129
|
+
space.db.remove(contactToRemove);
|
|
130
|
+
} else {
|
|
131
|
+
// Add a new contact (30% chance)
|
|
132
|
+
void factory([{ type: Person.Person, count: 1 }]);
|
|
133
|
+
}
|
|
134
|
+
}, 3_000);
|
|
135
|
+
|
|
136
|
+
return () => clearInterval(interval);
|
|
137
|
+
}, [space, pipeline, contacts]);
|
|
138
|
+
|
|
139
|
+
if (!pipeline) {
|
|
140
|
+
return <p>Loading…</p>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<PipelineComponent.Root Item={StorybookProjectItem} onAddColumn={handleAddColumn}>
|
|
145
|
+
<PipelineComponent.Content pipeline={pipeline} />
|
|
146
|
+
</PipelineComponent.Root>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const meta = {
|
|
151
|
+
title: 'plugins/plugin-pipeline/PipelineComponent',
|
|
152
|
+
decorators: [
|
|
153
|
+
withTheme(),
|
|
154
|
+
withLayout({ layout: 'column' }),
|
|
155
|
+
withMosaic(),
|
|
156
|
+
withClientProvider({
|
|
157
|
+
types: [Pipeline.Pipeline, View.View, Collection.Collection, Person.Person],
|
|
158
|
+
createIdentity: true,
|
|
159
|
+
createSpace: true,
|
|
160
|
+
onCreateSpace: async ({ space }) => {
|
|
161
|
+
// Create a view for contacts.
|
|
162
|
+
const view = View.make({
|
|
163
|
+
name: 'Contacts',
|
|
164
|
+
query: Query.select(Filter.type(Person.Person)),
|
|
165
|
+
jsonSchema: Type.toJsonSchema(Person.Person),
|
|
166
|
+
fields: ['fullName'],
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Create a pipeline with columns.
|
|
170
|
+
space.db.add(
|
|
171
|
+
Pipeline.make({
|
|
172
|
+
columns: [
|
|
173
|
+
{
|
|
174
|
+
name: 'Contacts',
|
|
175
|
+
view: Ref.make(view),
|
|
176
|
+
order: [],
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Generate random contacts.
|
|
183
|
+
const factory = createObjectFactory(space.db, faker as any);
|
|
184
|
+
await factory([{ type: Person.Person, count: 12 }]);
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
],
|
|
188
|
+
parameters: {
|
|
189
|
+
layout: 'fullscreen',
|
|
190
|
+
translations,
|
|
191
|
+
},
|
|
192
|
+
} satisfies Meta<typeof PipelineComponent>;
|
|
193
|
+
|
|
194
|
+
export default meta;
|
|
195
|
+
|
|
196
|
+
type Story = StoryObj<typeof meta>;
|
|
197
|
+
|
|
198
|
+
export const Default: Story = {
|
|
199
|
+
render: DefaultStory,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export const Mutations: Story = {
|
|
203
|
+
render: MutationsStory,
|
|
204
|
+
};
|