@dxos/plugin-preview 0.8.4-main.406dc2a → 0.8.4-main.548089c
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/browser/chunk-CEWM5LJG.mjs +17 -0
- package/dist/lib/browser/chunk-CEWM5LJG.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -5
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-X56QWACK.mjs → react-surface-ZAOGRCMT.mjs} +238 -159
- package/dist/lib/browser/react-surface-ZAOGRCMT.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-L3IZVX56.mjs → chunk-Q5MUNELK.mjs} +6 -1
- package/dist/lib/node-esm/chunk-Q5MUNELK.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +5 -5
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-4P7UCMK7.mjs → react-surface-X76BTDM5.mjs} +238 -159
- package/dist/lib/node-esm/react-surface-X76BTDM5.mjs.map +7 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/cards/OrganizationCard.d.ts +5 -0
- package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -0
- package/dist/types/src/cards/PersonCard.d.ts +5 -0
- package/dist/types/src/cards/PersonCard.d.ts.map +1 -0
- package/dist/types/src/cards/ProjectCard.d.ts +5 -0
- package/dist/types/src/cards/ProjectCard.d.ts.map +1 -0
- package/dist/types/src/{components → cards}/TaskCard.d.ts +2 -2
- package/dist/types/src/cards/TaskCard.d.ts.map +1 -0
- package/dist/types/src/cards/index.d.ts +5 -0
- package/dist/types/src/cards/index.d.ts.map +1 -0
- package/dist/types/src/components/CardComponents.d.ts +16 -0
- package/dist/types/src/components/CardComponents.d.ts.map +1 -0
- package/dist/types/src/components/CardSubjectMenu.d.ts +3 -7
- package/dist/types/src/components/CardSubjectMenu.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/stories/Card.stories.d.ts +0 -1
- package/dist/types/src/stories/Card.stories.d.ts.map +1 -1
- package/dist/types/src/stories/testing.d.ts +2 -1
- package/dist/types/src/stories/testing.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -3
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -26
- package/src/PreviewPlugin.tsx +2 -2
- package/src/capabilities/react-surface.tsx +44 -27
- package/src/cards/OrganizationCard.tsx +29 -0
- package/src/cards/PersonCard.tsx +63 -0
- package/src/cards/ProjectCard.tsx +24 -0
- package/src/{components → cards}/TaskCard.tsx +6 -6
- package/src/cards/index.ts +8 -0
- package/src/components/CardComponents.tsx +67 -0
- package/src/components/CardSubjectMenu.tsx +61 -40
- package/src/components/index.ts +2 -3
- package/src/meta.ts +5 -0
- package/src/stories/Card.stories.tsx +5 -2
- package/src/stories/testing.tsx +19 -19
- package/src/types.ts +5 -4
- package/dist/lib/browser/chunk-XKCIQK3C.mjs +0 -12
- package/dist/lib/browser/chunk-XKCIQK3C.mjs.map +0 -7
- package/dist/lib/browser/react-surface-X56QWACK.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-L3IZVX56.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-4P7UCMK7.mjs.map +0 -7
- package/dist/types/src/components/ContactCard.d.ts +0 -7
- package/dist/types/src/components/ContactCard.d.ts.map +0 -1
- package/dist/types/src/components/OrganizationCard.d.ts +0 -5
- package/dist/types/src/components/OrganizationCard.d.ts.map +0 -1
- package/dist/types/src/components/ProjectCard.d.ts +0 -5
- package/dist/types/src/components/ProjectCard.d.ts.map +0 -1
- package/dist/types/src/components/TaskCard.d.ts.map +0 -1
- package/src/components/ContactCard.tsx +0 -78
- package/src/components/OrganizationCard.tsx +0 -44
- package/src/components/ProjectCard.tsx +0 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-preview",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.548089c",
|
|
4
4
|
"description": "Preview plugin",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -24,38 +24,39 @@
|
|
|
24
24
|
"src"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@effect-
|
|
27
|
+
"@effect-atom/atom-react": "^0.3.4",
|
|
28
28
|
"@preact-signals/safe-react": "^0.9.0",
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/plugin-
|
|
39
|
-
"@dxos/plugin-
|
|
40
|
-
"@dxos/plugin-space": "0.8.4-main.
|
|
41
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
42
|
-
"@dxos/react-
|
|
43
|
-
"@dxos/react-
|
|
44
|
-
"@dxos/react-ui-form": "0.8.4-main.
|
|
45
|
-
"@dxos/react-ui-menu": "0.8.4-main.
|
|
46
|
-
"@dxos/react-ui-
|
|
47
|
-
"@dxos/react-ui-
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
29
|
+
"@dxos/app-framework": "0.8.4-main.548089c",
|
|
30
|
+
"@dxos/async": "0.8.4-main.548089c",
|
|
31
|
+
"@dxos/echo": "0.8.4-main.548089c",
|
|
32
|
+
"@dxos/app-graph": "0.8.4-main.548089c",
|
|
33
|
+
"@dxos/keys": "0.8.4-main.548089c",
|
|
34
|
+
"@dxos/lit-ui": "0.8.4-main.548089c",
|
|
35
|
+
"@dxos/client": "0.8.4-main.548089c",
|
|
36
|
+
"@dxos/log": "0.8.4-main.548089c",
|
|
37
|
+
"@dxos/plugin-attention": "0.8.4-main.548089c",
|
|
38
|
+
"@dxos/plugin-deck": "0.8.4-main.548089c",
|
|
39
|
+
"@dxos/plugin-client": "0.8.4-main.548089c",
|
|
40
|
+
"@dxos/plugin-space": "0.8.4-main.548089c",
|
|
41
|
+
"@dxos/react-ui-editor": "0.8.4-main.548089c",
|
|
42
|
+
"@dxos/react-ui": "0.8.4-main.548089c",
|
|
43
|
+
"@dxos/react-client": "0.8.4-main.548089c",
|
|
44
|
+
"@dxos/react-ui-form": "0.8.4-main.548089c",
|
|
45
|
+
"@dxos/react-ui-menu": "0.8.4-main.548089c",
|
|
46
|
+
"@dxos/react-ui-stack": "0.8.4-main.548089c",
|
|
47
|
+
"@dxos/react-ui-table": "0.8.4-main.548089c",
|
|
48
|
+
"@dxos/react-ui-theme": "0.8.4-main.548089c",
|
|
49
|
+
"@dxos/schema": "0.8.4-main.548089c",
|
|
50
|
+
"@dxos/types": "0.8.4-main.548089c",
|
|
51
|
+
"@dxos/util": "0.8.4-main.548089c"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"effect": "3.18.3",
|
|
54
55
|
"react": "~19.2.0",
|
|
55
56
|
"react-dom": "~19.2.0",
|
|
56
57
|
"vite": "7.1.9",
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
58
|
+
"@dxos/random": "0.8.4-main.548089c",
|
|
59
|
+
"@dxos/storybook-utils": "0.8.4-main.548089c"
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
61
62
|
"effect": "^3.13.3",
|
package/src/PreviewPlugin.tsx
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { Capabilities, Events, contributes, defineModule, definePlugin } from '@dxos/app-framework';
|
|
6
6
|
import { ClientCapabilities, ClientEvents } from '@dxos/plugin-client';
|
|
7
|
-
import {
|
|
7
|
+
import { Organization, Person } from '@dxos/types';
|
|
8
8
|
|
|
9
9
|
import { PreviewPopover, ReactSurface } from './capabilities';
|
|
10
10
|
import { meta } from './meta';
|
|
@@ -19,7 +19,7 @@ export const PreviewPlugin = definePlugin(meta, () => [
|
|
|
19
19
|
defineModule({
|
|
20
20
|
id: `${meta.id}/module/schema`,
|
|
21
21
|
activatesOn: ClientEvents.SetupSchema,
|
|
22
|
-
activate: () => [contributes(ClientCapabilities.Schema, [
|
|
22
|
+
activate: () => [contributes(ClientCapabilities.Schema, [Person.Person, Organization.Organization])],
|
|
23
23
|
}),
|
|
24
24
|
defineModule({
|
|
25
25
|
id: `${meta.id}/module/preview-popover`,
|
|
@@ -4,16 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Surface,
|
|
11
|
-
contributes,
|
|
12
|
-
createIntent,
|
|
13
|
-
createSurface,
|
|
14
|
-
useIntentDispatcher,
|
|
15
|
-
} from '@dxos/app-framework';
|
|
16
|
-
import { fullyQualifiedId, getSchema, getSpace } from '@dxos/client/echo';
|
|
7
|
+
import { Capabilities, LayoutAction, contributes, createIntent, createSurface } from '@dxos/app-framework';
|
|
8
|
+
import { Surface, useIntentDispatcher } from '@dxos/app-framework/react';
|
|
9
|
+
import { getSchema, getSpace } from '@dxos/client/echo';
|
|
17
10
|
import { Obj } from '@dxos/echo';
|
|
18
11
|
import { type JsonPath, setValue } from '@dxos/echo/internal';
|
|
19
12
|
import { useActiveSpace } from '@dxos/plugin-space';
|
|
@@ -21,31 +14,34 @@ import { useTranslation } from '@dxos/react-ui';
|
|
|
21
14
|
import { Form } from '@dxos/react-ui-form';
|
|
22
15
|
import { Card } from '@dxos/react-ui-stack';
|
|
23
16
|
import { descriptionMessage, mx } from '@dxos/react-ui-theme';
|
|
24
|
-
import {
|
|
17
|
+
import { type ProjectionModel } from '@dxos/schema';
|
|
18
|
+
import { Organization, Person, Project, Task } from '@dxos/types';
|
|
25
19
|
|
|
26
|
-
import {
|
|
27
|
-
import { TaskCard } from '../components/TaskCard';
|
|
20
|
+
import { OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
|
|
28
21
|
import { meta } from '../meta';
|
|
22
|
+
import { type PreviewProps } from '../types';
|
|
29
23
|
|
|
30
24
|
export default () =>
|
|
31
25
|
contributes(Capabilities.ReactSurface, [
|
|
32
26
|
//
|
|
33
27
|
// Specific schema types.
|
|
28
|
+
// TODO(burdon): Create helpers and factor out.
|
|
34
29
|
//
|
|
30
|
+
|
|
35
31
|
createSurface({
|
|
36
32
|
id: `${meta.id}/schema-popover--contact`,
|
|
37
33
|
role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
|
|
38
|
-
filter: (data): data is { subject:
|
|
34
|
+
filter: (data): data is { subject: Person.Person } => Obj.instanceOf(Person.Person, data.subject),
|
|
39
35
|
component: ({ data, role }) => {
|
|
40
36
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
41
37
|
const space = getSpace(data.subject);
|
|
42
|
-
const activeSpace = useActiveSpace();
|
|
43
|
-
const
|
|
44
|
-
(
|
|
38
|
+
const activeSpace = useActiveSpace(); // TODO(burdon): Disambiguate with space?
|
|
39
|
+
const handleSelect = useCallback<NonNullable<PreviewProps['onSelect']>>(
|
|
40
|
+
(object) =>
|
|
45
41
|
dispatch(
|
|
46
42
|
createIntent(LayoutAction.Open, {
|
|
47
43
|
part: 'main',
|
|
48
|
-
subject: [
|
|
44
|
+
subject: [Obj.getDXN(object).toString()],
|
|
49
45
|
options: {
|
|
50
46
|
workspace: space?.id,
|
|
51
47
|
},
|
|
@@ -55,19 +51,19 @@ export default () =>
|
|
|
55
51
|
);
|
|
56
52
|
|
|
57
53
|
return (
|
|
58
|
-
<
|
|
54
|
+
<PersonCard role={role} subject={data.subject} activeSpace={activeSpace} onSelect={handleSelect}>
|
|
59
55
|
{role === 'card--popover' && <Surface role='related' data={data} />}
|
|
60
|
-
</
|
|
56
|
+
</PersonCard>
|
|
61
57
|
);
|
|
62
58
|
},
|
|
63
59
|
}),
|
|
64
60
|
createSurface({
|
|
65
61
|
id: `${meta.id}/schema-popover--organization`,
|
|
66
62
|
role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
|
|
67
|
-
filter: (data): data is { subject:
|
|
63
|
+
filter: (data): data is { subject: Organization.Organization } =>
|
|
64
|
+
Obj.instanceOf(Organization.Organization, data.subject),
|
|
68
65
|
component: ({ data, role }) => {
|
|
69
66
|
const activeSpace = useActiveSpace();
|
|
70
|
-
|
|
71
67
|
return (
|
|
72
68
|
<OrganizationCard role={role} subject={data.subject} activeSpace={activeSpace}>
|
|
73
69
|
{role === 'card--popover' && <Surface role='related' data={data} />}
|
|
@@ -78,23 +74,25 @@ export default () =>
|
|
|
78
74
|
createSurface({
|
|
79
75
|
id: `${meta.id}/schema-popover--project`,
|
|
80
76
|
role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
|
|
81
|
-
filter: (data): data is { subject:
|
|
77
|
+
filter: (data): data is { subject: Project.Project } => Obj.instanceOf(Project.Project, data.subject),
|
|
82
78
|
component: ({ data, role }) => {
|
|
83
79
|
const activeSpace = useActiveSpace();
|
|
84
|
-
|
|
85
80
|
return <ProjectCard subject={data.subject} role={role} activeSpace={activeSpace} />;
|
|
86
81
|
},
|
|
87
82
|
}),
|
|
88
83
|
createSurface({
|
|
89
84
|
id: `${meta.id}/schema-popover--task`,
|
|
90
85
|
role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
|
|
91
|
-
filter: (data): data is { subject:
|
|
92
|
-
component: ({ data, role }) =>
|
|
86
|
+
filter: (data): data is { subject: Task.Task } => Obj.instanceOf(Task.Task, data.subject),
|
|
87
|
+
component: ({ data, role }) => {
|
|
88
|
+
return <TaskCard subject={data.subject} role={role} />;
|
|
89
|
+
},
|
|
93
90
|
}),
|
|
94
91
|
|
|
95
92
|
//
|
|
96
93
|
// Fallback for any object.
|
|
97
94
|
//
|
|
95
|
+
|
|
98
96
|
createSurface({
|
|
99
97
|
id: `${meta.id}/fallback-popover`,
|
|
100
98
|
role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
|
|
@@ -119,16 +117,35 @@ export default () =>
|
|
|
119
117
|
return (
|
|
120
118
|
<Card.SurfaceRoot role={role}>
|
|
121
119
|
<Form
|
|
120
|
+
id={data.subject.id}
|
|
122
121
|
schema={schema}
|
|
123
122
|
projection={data.projection}
|
|
124
123
|
values={data.subject}
|
|
125
124
|
readonly={role === 'card--popover' ? 'static' : false}
|
|
126
125
|
onSave={handleSave}
|
|
127
126
|
autoSave
|
|
128
|
-
{...(role === 'card--intrinsic' && {
|
|
127
|
+
{...(role === 'card--intrinsic' && {
|
|
128
|
+
outerSpacing: 'blockStart-0',
|
|
129
|
+
})}
|
|
129
130
|
/>
|
|
130
131
|
</Card.SurfaceRoot>
|
|
131
132
|
);
|
|
132
133
|
},
|
|
133
134
|
}),
|
|
135
|
+
|
|
136
|
+
createSurface({
|
|
137
|
+
id: `${meta.id}/section`,
|
|
138
|
+
role: ['section'],
|
|
139
|
+
position: 'fallback',
|
|
140
|
+
filter: (data): data is { subject: Obj.Any } => Obj.isObject(data.subject),
|
|
141
|
+
component: ({ data }) => {
|
|
142
|
+
return (
|
|
143
|
+
<div role='none' className='flex is-full justify-center'>
|
|
144
|
+
<div role='none' className='pbs-2 pbe-2 card-min-width card-max-width'>
|
|
145
|
+
<Surface role='card' data={data} limit={1} />
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
134
151
|
]);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Card } from '@dxos/react-ui-stack';
|
|
8
|
+
import { type Organization } from '@dxos/types';
|
|
9
|
+
|
|
10
|
+
import { CardHeader, CardLink } from '../components';
|
|
11
|
+
import { type PreviewProps } from '../types';
|
|
12
|
+
|
|
13
|
+
export const OrganizationCard = ({ children, subject, role, activeSpace }: PreviewProps<Organization.Organization>) => {
|
|
14
|
+
const { name, image, description, website } = subject;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Card.SurfaceRoot id={subject.id} role={role}>
|
|
18
|
+
<Card.Poster
|
|
19
|
+
alt={name ?? ''}
|
|
20
|
+
{...(image ? { image } : { icon: 'ph--building-office--regular' })}
|
|
21
|
+
classNames={!image && 'opacity-50'}
|
|
22
|
+
/>
|
|
23
|
+
<CardHeader label={name} subject={subject} activeSpace={activeSpace} />
|
|
24
|
+
{description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
|
|
25
|
+
{website && <CardLink label={website} href={website} />}
|
|
26
|
+
{children}
|
|
27
|
+
</Card.SurfaceRoot>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { Fragment } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Avatar, Icon } from '@dxos/react-ui';
|
|
8
|
+
import { Card, cardHeading, cardText } from '@dxos/react-ui-stack';
|
|
9
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
10
|
+
import { type Person } from '@dxos/types';
|
|
11
|
+
|
|
12
|
+
import { CardRow, CardSubjectMenu, gridRow } from '../components';
|
|
13
|
+
import { type PreviewProps } from '../types';
|
|
14
|
+
|
|
15
|
+
export const PersonCard = ({ children, role, subject, activeSpace, onSelect }: PreviewProps<Person.Person>) => {
|
|
16
|
+
const { fullName, image, organization: { target: organization } = {}, emails = [] } = subject;
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Card.SurfaceRoot id={subject.id} role={role}>
|
|
20
|
+
<Avatar.Root>
|
|
21
|
+
<Card.Text role='group' classNames={mx('grid gap-2 grid-cols-[1fr_min-content]')}>
|
|
22
|
+
<div role='none' className={mx(gridRow, 'grid-rows-2')}>
|
|
23
|
+
{activeSpace ? <CardSubjectMenu subject={subject} activeSpace={activeSpace} /> : <div />}
|
|
24
|
+
<Avatar.Label asChild>
|
|
25
|
+
<h2 className={mx(cardHeading, 'grow truncate')}>{fullName}</h2>
|
|
26
|
+
</Avatar.Label>
|
|
27
|
+
</div>
|
|
28
|
+
<Avatar.Content
|
|
29
|
+
imgSrc={image}
|
|
30
|
+
icon='ph--user--regular'
|
|
31
|
+
size={16}
|
|
32
|
+
classNames={['text-subdued', !image && 'opacity-50']}
|
|
33
|
+
hue='neutral'
|
|
34
|
+
variant='square'
|
|
35
|
+
/>
|
|
36
|
+
</Card.Text>
|
|
37
|
+
</Avatar.Root>
|
|
38
|
+
{organization?.name && (
|
|
39
|
+
<CardRow
|
|
40
|
+
icon='ph--buildings--regular'
|
|
41
|
+
label={organization.name}
|
|
42
|
+
onClick={onSelect ? () => onSelect(organization) : undefined}
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
{emails.length > 0 && (
|
|
46
|
+
<dl className={mx(cardText, gridRow, '[&_dt]:text-subdued [&_dt]:pbs-0.5 [&_dd]:min-is-0')}>
|
|
47
|
+
{emails.map(({ label, value }) => (
|
|
48
|
+
<Fragment key={value}>
|
|
49
|
+
<dt>
|
|
50
|
+
<span className='sr-only'>{label}</span>
|
|
51
|
+
<Icon icon='ph--at--regular' size={5} />
|
|
52
|
+
</dt>
|
|
53
|
+
<dd key={value} className='col-span-2 break-words'>
|
|
54
|
+
{value}
|
|
55
|
+
</dd>
|
|
56
|
+
</Fragment>
|
|
57
|
+
))}
|
|
58
|
+
</dl>
|
|
59
|
+
)}
|
|
60
|
+
{children}
|
|
61
|
+
</Card.SurfaceRoot>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Obj } from '@dxos/echo';
|
|
8
|
+
import { Card } from '@dxos/react-ui-stack';
|
|
9
|
+
import { type Project } from '@dxos/types';
|
|
10
|
+
|
|
11
|
+
import { CardHeader } from '../components';
|
|
12
|
+
import { type PreviewProps } from '../types';
|
|
13
|
+
|
|
14
|
+
export const ProjectCard = ({ subject, role, activeSpace }: PreviewProps<Project.Project>) => {
|
|
15
|
+
const { name, image, description } = subject;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Card.SurfaceRoot id={subject.id} role={role}>
|
|
19
|
+
{image && <Card.Poster image={image} alt={Obj.getLabel(subject) ?? ''} aspect='auto' />}
|
|
20
|
+
<CardHeader label={name} subject={subject} activeSpace={activeSpace} />
|
|
21
|
+
{description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
|
|
22
|
+
</Card.SurfaceRoot>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -8,15 +8,14 @@ import React from 'react';
|
|
|
8
8
|
import { type PropertyMetaAnnotation, PropertyMetaAnnotationId } from '@dxos/echo/internal';
|
|
9
9
|
import { Card, cardNoSpacing, cardSpacing } from '@dxos/react-ui-stack';
|
|
10
10
|
import { mx } from '@dxos/react-ui-theme';
|
|
11
|
-
import {
|
|
11
|
+
import { Task } from '@dxos/types';
|
|
12
12
|
|
|
13
|
+
import { CardSubjectMenu } from '../components';
|
|
13
14
|
import { type PreviewProps } from '../types';
|
|
14
15
|
|
|
15
|
-
import { CardSubjectMenu } from './CardSubjectMenu';
|
|
16
|
-
|
|
17
16
|
// TODO(thure): Should this move upstream as a helper? Is there an easier way to get options?
|
|
18
17
|
const getActiveStatusOption = (status?: string) => {
|
|
19
|
-
const properties = SchemaAST.getPropertySignatures(
|
|
18
|
+
const properties = SchemaAST.getPropertySignatures(Task.Task.ast);
|
|
20
19
|
const statusProperty = properties.find((p) => p.name === 'status');
|
|
21
20
|
const statusMeta = SchemaAST.getAnnotation<PropertyMetaAnnotation>(PropertyMetaAnnotationId)(
|
|
22
21
|
// TODO(thure): Typescript asserts `.type` doesn’t have `.types`, but in runtime it does.
|
|
@@ -27,11 +26,12 @@ const getActiveStatusOption = (status?: string) => {
|
|
|
27
26
|
return options.find(({ id }) => id === status);
|
|
28
27
|
};
|
|
29
28
|
|
|
30
|
-
export const TaskCard = ({ subject, role, activeSpace }: PreviewProps<
|
|
29
|
+
export const TaskCard = ({ subject, role, activeSpace }: PreviewProps<Task.Task>) => {
|
|
31
30
|
const { title, status } = subject;
|
|
32
31
|
const statusOption = getActiveStatusOption(status);
|
|
32
|
+
|
|
33
33
|
return (
|
|
34
|
-
<Card.SurfaceRoot role={role}>
|
|
34
|
+
<Card.SurfaceRoot id={subject.id} role={role}>
|
|
35
35
|
<div role='none' className={mx('flex items-center gap-2', cardSpacing)}>
|
|
36
36
|
<Card.Heading classNames={[cardNoSpacing, 'min-is-0 flex-1 truncate']}>{title}</Card.Heading>
|
|
37
37
|
{statusOption && (
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Button, Icon } from '@dxos/react-ui';
|
|
8
|
+
import { Card, cardNoSpacing, cardSpacing } from '@dxos/react-ui-stack';
|
|
9
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
10
|
+
|
|
11
|
+
import { type PreviewProps } from '../types';
|
|
12
|
+
|
|
13
|
+
import { CardSubjectMenu } from './CardSubjectMenu';
|
|
14
|
+
|
|
15
|
+
export const gridRow = 'is-full grid grid-cols-[1.5rem_1fr_min-content] gap-2 items-center';
|
|
16
|
+
|
|
17
|
+
// TODO(burdon): Standardize card grid for common rows.
|
|
18
|
+
|
|
19
|
+
export const CardHeader = ({ label, subject, activeSpace }: { label?: string } & PreviewProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<div role='none' className={mx('flex items-center gap-2', cardSpacing)}>
|
|
22
|
+
<Card.Heading classNames={cardNoSpacing}>{label}</Card.Heading>
|
|
23
|
+
<CardSubjectMenu subject={subject} activeSpace={activeSpace} />
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const CardRow = ({ icon, label, onClick }: { icon: string; label: string; onClick?: () => void }) => {
|
|
29
|
+
if (!onClick) {
|
|
30
|
+
return (
|
|
31
|
+
<Card.Chrome>
|
|
32
|
+
<p data-variant='ghost' className={mx(gridRow, 'dx-button')}>
|
|
33
|
+
<Icon icon={icon} size={5} classNames='text-subdued' />
|
|
34
|
+
<span className='min-is-0 flex-1 truncate col-span-2'>{label}</span>
|
|
35
|
+
</p>
|
|
36
|
+
</Card.Chrome>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Card.Chrome>
|
|
42
|
+
<Button variant='ghost' classNames={mx(gridRow, 'text-start')} onClick={onClick}>
|
|
43
|
+
<Icon icon={icon} size={5} classNames='text-subdued' />
|
|
44
|
+
<span className='min-is-0 flex-1 truncate'>{label}</span>
|
|
45
|
+
<Icon icon='ph--arrow-right--regular' />
|
|
46
|
+
</Button>
|
|
47
|
+
</Card.Chrome>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const CardLink = ({ label, href }: { label: string; href: string }) => {
|
|
52
|
+
return (
|
|
53
|
+
<Card.Chrome>
|
|
54
|
+
<a
|
|
55
|
+
className={mx(gridRow, 'dx-button dx-focus-ring')}
|
|
56
|
+
data-variant='ghost'
|
|
57
|
+
href={href}
|
|
58
|
+
target='_blank'
|
|
59
|
+
rel='noreferrer'
|
|
60
|
+
>
|
|
61
|
+
<Icon icon='ph--link--regular' size={5} classNames='text-subdued' />
|
|
62
|
+
<span className='min-is-0 flex-1 truncate'>{label}</span>
|
|
63
|
+
<Icon icon='ph--arrow-square-out--regular' />
|
|
64
|
+
</a>
|
|
65
|
+
</Card.Chrome>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -2,27 +2,73 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
-
import { LayoutAction, createIntent
|
|
8
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
9
|
+
import { useIntentDispatcher } from '@dxos/app-framework/react';
|
|
9
10
|
import { ACTION_TYPE } from '@dxos/app-graph';
|
|
10
11
|
import { Obj } from '@dxos/echo';
|
|
11
12
|
import { SpaceAction } from '@dxos/plugin-space/types';
|
|
12
|
-
import { type Space, fullyQualifiedId } from '@dxos/react-client/echo';
|
|
13
13
|
import { IconButton, type IconButtonProps, useTranslation } from '@dxos/react-ui';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type ActionGraphProps,
|
|
16
|
+
DropdownMenu,
|
|
17
|
+
type MenuActions,
|
|
18
|
+
MenuProvider,
|
|
19
|
+
useMenuActions,
|
|
20
|
+
} from '@dxos/react-ui-menu';
|
|
15
21
|
|
|
16
22
|
import { meta } from '../meta';
|
|
23
|
+
import { type PreviewProps } from '../types';
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Generic menu for objects; builds menu with common actions.
|
|
27
|
+
*/
|
|
28
|
+
// TODO(burdon): Reconcile title and menu with main Card header.
|
|
29
|
+
export const CardSubjectMenu = ({
|
|
30
|
+
subject,
|
|
31
|
+
activeSpace,
|
|
32
|
+
...props
|
|
33
|
+
}: PreviewProps & Omit<IconButtonProps, 'icon' | 'label'>) => {
|
|
34
|
+
const { t } = useTranslation(meta.id);
|
|
35
|
+
const menuProps = useSubjectMenuGroupItems({ subject, activeSpace });
|
|
36
|
+
|
|
37
|
+
if (!activeSpace) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<MenuProvider {...menuProps}>
|
|
43
|
+
<DropdownMenu.Root>
|
|
44
|
+
<DropdownMenu.Trigger asChild>
|
|
45
|
+
<IconButton
|
|
46
|
+
variant='ghost'
|
|
47
|
+
icon='ph--dots-three-vertical--bold'
|
|
48
|
+
iconOnly
|
|
49
|
+
label={t('more options label')}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
</DropdownMenu.Trigger>
|
|
53
|
+
</DropdownMenu.Root>
|
|
54
|
+
</MenuProvider>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const useSubjectMenuGroupItems = ({ subject, activeSpace }: PreviewProps): MenuActions => {
|
|
19
59
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
20
60
|
const result: ActionGraphProps = { edges: [], nodes: [] };
|
|
21
61
|
|
|
22
62
|
result.nodes.push({
|
|
23
63
|
type: ACTION_TYPE,
|
|
24
64
|
id: `${subject.id}/open`,
|
|
25
|
-
data: () =>
|
|
65
|
+
data: () =>
|
|
66
|
+
dispatch(
|
|
67
|
+
createIntent(LayoutAction.Open, {
|
|
68
|
+
part: 'main',
|
|
69
|
+
subject: [Obj.getDXN(subject).toString()],
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
26
72
|
properties: {
|
|
27
73
|
label: ['open object label', { ns: meta.id }],
|
|
28
74
|
icon: 'ph--arrow-right--regular',
|
|
@@ -34,7 +80,14 @@ const useSubjectMenuGroupItems = (subject: Obj.Any, activeSpace?: Space) => {
|
|
|
34
80
|
type: ACTION_TYPE,
|
|
35
81
|
id: `${subject.id}/add-to-space`,
|
|
36
82
|
// TODO(wittjosiah): Update reference to point to db object when adding?
|
|
37
|
-
data: () =>
|
|
83
|
+
data: () =>
|
|
84
|
+
dispatch(
|
|
85
|
+
createIntent(SpaceAction.AddObject, {
|
|
86
|
+
object: subject,
|
|
87
|
+
target: activeSpace,
|
|
88
|
+
hidden: true,
|
|
89
|
+
}),
|
|
90
|
+
),
|
|
38
91
|
properties: {
|
|
39
92
|
label: ['add object to space label', { ns: meta.id }],
|
|
40
93
|
icon: 'ph--file-plus--regular',
|
|
@@ -46,37 +99,5 @@ const useSubjectMenuGroupItems = (subject: Obj.Any, activeSpace?: Space) => {
|
|
|
46
99
|
result.edges.push({ source: 'root', target });
|
|
47
100
|
});
|
|
48
101
|
|
|
49
|
-
return useMenuActions(
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* This is a generic menu for objects that tries to infer common actions.
|
|
54
|
-
*/
|
|
55
|
-
export const CardSubjectMenu = ({
|
|
56
|
-
subject,
|
|
57
|
-
activeSpace,
|
|
58
|
-
...props
|
|
59
|
-
}: Omit<IconButtonProps, 'icon' | 'label'> & { subject: Obj.Any; activeSpace?: Space }) => {
|
|
60
|
-
const { t } = useTranslation(meta.id);
|
|
61
|
-
const menuProps = useSubjectMenuGroupItems(subject, activeSpace);
|
|
62
|
-
|
|
63
|
-
if (!activeSpace) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<MenuProvider {...menuProps}>
|
|
69
|
-
<DropdownMenu.Root>
|
|
70
|
-
<DropdownMenu.Trigger asChild>
|
|
71
|
-
<IconButton
|
|
72
|
-
iconOnly
|
|
73
|
-
variant='ghost'
|
|
74
|
-
icon='ph--dots-three-vertical--bold'
|
|
75
|
-
label={t('more options label')}
|
|
76
|
-
{...props}
|
|
77
|
-
/>
|
|
78
|
-
</DropdownMenu.Trigger>
|
|
79
|
-
</DropdownMenu.Root>
|
|
80
|
-
</MenuProvider>
|
|
81
|
-
);
|
|
102
|
+
return useMenuActions(Atom.make(result));
|
|
82
103
|
};
|
package/src/components/index.ts
CHANGED
package/src/meta.ts
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type PluginMeta } from '@dxos/app-framework';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
6
7
|
|
|
7
8
|
export const meta: PluginMeta = {
|
|
8
9
|
id: 'dxos.org/plugin/preview',
|
|
9
10
|
name: 'Preview',
|
|
11
|
+
description: trim`
|
|
12
|
+
Rich preview panel for viewing object content without opening full editors.
|
|
13
|
+
Quick peek at documents, images, and data with inline rendering.
|
|
14
|
+
`,
|
|
10
15
|
icon: 'ph--eye--regular',
|
|
11
16
|
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-preview',
|
|
12
17
|
};
|
|
@@ -6,21 +6,24 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
|
|
7
7
|
import { IntentPlugin } from '@dxos/app-framework';
|
|
8
8
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
9
|
+
import { faker } from '@dxos/random';
|
|
9
10
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
11
|
|
|
11
12
|
import { translations } from '../translations';
|
|
12
13
|
|
|
13
14
|
import { Defaultstory, createCards } from './testing';
|
|
14
15
|
|
|
16
|
+
faker.seed(999);
|
|
17
|
+
|
|
15
18
|
const meta = {
|
|
16
19
|
title: 'plugins/plugin-preview/Card',
|
|
17
20
|
render: Defaultstory,
|
|
18
21
|
decorators: [
|
|
19
|
-
withTheme,
|
|
22
|
+
withTheme,
|
|
23
|
+
// TODO(wittjosiah): Try to write story which does not depend on plugin manager.
|
|
20
24
|
withPluginManager({ plugins: [IntentPlugin()] }),
|
|
21
25
|
],
|
|
22
26
|
parameters: {
|
|
23
|
-
layout: 'column',
|
|
24
27
|
translations,
|
|
25
28
|
},
|
|
26
29
|
tags: ['cards'],
|