@dxos/plugin-preview 0.9.0 → 0.9.1-staging.ee54ba693a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/neutral/capabilities/index.mjs +2 -2
- package/dist/lib/neutral/chunk-SXDHWUBO.mjs +32 -0
- package/dist/lib/neutral/chunk-SXDHWUBO.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +1 -1
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/lib/neutral/meta.mjs +1 -1
- package/dist/lib/neutral/plugin.mjs +1 -1
- package/dist/lib/neutral/{preview-popover-FLMYMFCH.mjs → preview-popover-EEQQJAH5.mjs} +16 -9
- package/dist/lib/neutral/preview-popover-EEQQJAH5.mjs.map +7 -0
- package/dist/lib/neutral/{react-surface-FE7E2JP7.mjs → react-surface-2CN3DRU6.mjs} +79 -65
- package/dist/lib/neutral/react-surface-2CN3DRU6.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +1 -1
- package/dist/lib/neutral/translations.mjs.map +3 -3
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +2 -2
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/preview-popover.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +2 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/cards/FormCard.d.ts +2 -2
- package/dist/types/src/cards/FormCard.d.ts.map +1 -1
- package/dist/types/src/cards/JsonCard.d.ts.map +1 -1
- package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -1
- package/dist/types/src/cards/PersonCard.d.ts.map +1 -1
- package/dist/types/src/cards/ProjectCard.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +32 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/stories/Card.stories.d.ts +4 -6
- package/dist/types/src/stories/Card.stories.d.ts.map +1 -1
- package/dist/types/src/stories/testing.d.ts +5 -5
- package/dist/types/src/stories/testing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dx.config.ts +21 -0
- package/package.json +32 -30
- package/src/PreviewPlugin.test.ts +1 -1
- package/src/capabilities/preview-popover.ts +15 -13
- package/src/capabilities/react-surface.tsx +23 -25
- package/src/cards/FormCard.tsx +18 -15
- package/src/cards/JsonCard.tsx +23 -8
- package/src/cards/OrganizationCard.tsx +1 -5
- package/src/cards/PersonCard.tsx +26 -21
- package/src/cards/ProjectCard.tsx +5 -1
- package/src/meta.ts +3 -14
- package/src/stories/Card.stories.tsx +16 -35
- package/src/stories/testing.tsx +7 -2
- package/src/translations.ts +1 -1
- package/dist/lib/neutral/chunk-QOM5APM7.mjs +0 -23
- package/dist/lib/neutral/chunk-QOM5APM7.mjs.map +0 -7
- package/dist/lib/neutral/preview-popover-FLMYMFCH.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-FE7E2JP7.mjs.map +0 -7
package/dx.config.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config2 } from '@dxos/app-framework/config';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
export default Config2.make({
|
|
9
|
+
plugin: {
|
|
10
|
+
key: 'org.dxos.plugin.preview',
|
|
11
|
+
name: 'Preview',
|
|
12
|
+
author: 'DXOS',
|
|
13
|
+
description: trim`
|
|
14
|
+
Rich preview panel for viewing object content without opening full editors.
|
|
15
|
+
Quick peek at documents, images, and data with inline rendering.
|
|
16
|
+
`,
|
|
17
|
+
icon: { key: 'ph--eye--regular' },
|
|
18
|
+
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-preview',
|
|
19
|
+
tags: ['system'],
|
|
20
|
+
},
|
|
21
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-preview",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1-staging.ee54ba693a",
|
|
4
4
|
"description": "Preview plugin",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -65,43 +65,45 @@
|
|
|
65
65
|
"types": "dist/types/src/index.d.ts",
|
|
66
66
|
"files": [
|
|
67
67
|
"dist",
|
|
68
|
+
"dx.config.ts",
|
|
68
69
|
"src"
|
|
69
70
|
],
|
|
70
71
|
"dependencies": {
|
|
71
|
-
"@dxos/app-
|
|
72
|
-
"@dxos/app-
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/echo": "0.9.
|
|
75
|
-
"@dxos/echo-react": "0.9.
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/plugin-client": "0.9.
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/react-ui
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/react-ui-
|
|
84
|
-
"@dxos/
|
|
85
|
-
"@dxos/
|
|
86
|
-
"@dxos/
|
|
87
|
-
"@dxos/
|
|
88
|
-
"@dxos/
|
|
72
|
+
"@dxos/app-toolkit": "0.9.1-staging.ee54ba693a",
|
|
73
|
+
"@dxos/app-framework": "0.9.1-staging.ee54ba693a",
|
|
74
|
+
"@dxos/async": "0.9.1-staging.ee54ba693a",
|
|
75
|
+
"@dxos/echo": "0.9.1-staging.ee54ba693a",
|
|
76
|
+
"@dxos/echo-react": "0.9.1-staging.ee54ba693a",
|
|
77
|
+
"@dxos/effect": "0.9.1-staging.ee54ba693a",
|
|
78
|
+
"@dxos/keys": "0.9.1-staging.ee54ba693a",
|
|
79
|
+
"@dxos/plugin-client": "0.9.1-staging.ee54ba693a",
|
|
80
|
+
"@dxos/log": "0.9.1-staging.ee54ba693a",
|
|
81
|
+
"@dxos/react-ui": "0.9.1-staging.ee54ba693a",
|
|
82
|
+
"@dxos/react-ui-form": "0.9.1-staging.ee54ba693a",
|
|
83
|
+
"@dxos/client": "0.9.1-staging.ee54ba693a",
|
|
84
|
+
"@dxos/react-ui-mosaic": "0.9.1-staging.ee54ba693a",
|
|
85
|
+
"@dxos/react-ui-syntax-highlighter": "0.9.1-staging.ee54ba693a",
|
|
86
|
+
"@dxos/schema": "0.9.1-staging.ee54ba693a",
|
|
87
|
+
"@dxos/types": "0.9.1-staging.ee54ba693a",
|
|
88
|
+
"@dxos/ui-types": "0.9.1-staging.ee54ba693a",
|
|
89
|
+
"@dxos/ui-editor": "0.9.1-staging.ee54ba693a",
|
|
90
|
+
"@dxos/util": "0.9.1-staging.ee54ba693a"
|
|
89
91
|
},
|
|
90
92
|
"devDependencies": {
|
|
91
|
-
"effect": "3.21.
|
|
92
|
-
"react": "~19.2.
|
|
93
|
-
"react-dom": "~19.2.
|
|
93
|
+
"effect": "3.21.4",
|
|
94
|
+
"react": "~19.2.7",
|
|
95
|
+
"react-dom": "~19.2.7",
|
|
94
96
|
"vite": "^8.0.16",
|
|
95
|
-
"@dxos/
|
|
96
|
-
"@dxos/
|
|
97
|
-
"@dxos/random": "0.9.
|
|
98
|
-
"@dxos/
|
|
99
|
-
"@dxos/
|
|
97
|
+
"@dxos/plugin-testing": "0.9.1-staging.ee54ba693a",
|
|
98
|
+
"@dxos/echo-react": "0.9.1-staging.ee54ba693a",
|
|
99
|
+
"@dxos/random": "0.9.1-staging.ee54ba693a",
|
|
100
|
+
"@dxos/react-ui-components": "0.9.1-staging.ee54ba693a",
|
|
101
|
+
"@dxos/storybook-utils": "0.9.1-staging.ee54ba693a"
|
|
100
102
|
},
|
|
101
103
|
"peerDependencies": {
|
|
102
|
-
"effect": "3.21.
|
|
103
|
-
"react": "~19.2.
|
|
104
|
-
"react-dom": "~19.2.
|
|
104
|
+
"effect": "3.21.4",
|
|
105
|
+
"react": "~19.2.7",
|
|
106
|
+
"react-dom": "~19.2.7"
|
|
105
107
|
},
|
|
106
108
|
"publishConfig": {
|
|
107
109
|
"access": "public"
|
|
@@ -11,7 +11,7 @@ import { PreviewPlugin } from '#plugin';
|
|
|
11
11
|
|
|
12
12
|
import { meta } from './meta';
|
|
13
13
|
|
|
14
|
-
const moduleId = (name: string) => `${meta.
|
|
14
|
+
const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
|
|
15
15
|
|
|
16
16
|
describe('PreviewPlugin', () => {
|
|
17
17
|
test('modules activate on the expected events', { timeout: 10_000 }, async ({ expect }) => {
|
|
@@ -5,27 +5,29 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
7
|
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
-
import { AppCapabilities,
|
|
8
|
+
import { AppCapabilities, AppSpace, LayoutOperation, Paths } from '@dxos/app-toolkit';
|
|
9
9
|
import { addEventListener } from '@dxos/async';
|
|
10
|
-
import { type Client } from '@dxos/client';
|
|
11
10
|
import { type Space } from '@dxos/client/echo';
|
|
12
11
|
import { Obj } from '@dxos/echo';
|
|
13
12
|
import { EID } from '@dxos/keys';
|
|
14
13
|
import { log } from '@dxos/log';
|
|
15
14
|
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
16
15
|
import { DX_ANCHOR_ACTIVATE, type DxAnchorActivate } from '@dxos/react-ui';
|
|
17
|
-
import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-
|
|
16
|
+
import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-types';
|
|
18
17
|
|
|
19
18
|
const customEventOptions = { capture: true, passive: false };
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
// TODO(burdon): Factor out?
|
|
21
|
+
const handlePreviewLookup = async (space: Space, { dxn, label }: PreviewLinkRef): Promise<PreviewLinkTarget | null> => {
|
|
22
|
+
const eid = EID.tryParse(dxn);
|
|
23
|
+
if (!eid) {
|
|
24
|
+
// dxn: type URIs and other non-EID refs cannot be resolved to an object.
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
26
27
|
try {
|
|
27
|
-
const object = await
|
|
28
|
-
|
|
28
|
+
const object = await space.db.makeRef(eid).load();
|
|
29
|
+
const resolvedLabel = Obj.getLabel(object as any, { fallback: 'typename' });
|
|
30
|
+
return { label: resolvedLabel ?? label, object };
|
|
29
31
|
} catch {
|
|
30
32
|
return null;
|
|
31
33
|
}
|
|
@@ -74,12 +76,12 @@ export default Capability.makeModule(
|
|
|
74
76
|
// would crash inside Atom's identity check (`'~atom/Serializable' in undefined`). When layout
|
|
75
77
|
// isn't available, fall through to the personal-space default.
|
|
76
78
|
const [layoutAtom] = capabilities.getAll(AppCapabilities.Layout);
|
|
77
|
-
const spaceId = layoutAtom && getSpaceIdFromPath(registry.get(layoutAtom).workspace);
|
|
78
|
-
const space = (spaceId && client.spaces.get(spaceId)) ?? getPersonalSpace(client);
|
|
79
|
+
const spaceId = layoutAtom && Paths.getSpaceIdFromPath(registry.get(layoutAtom).workspace);
|
|
80
|
+
const space = (spaceId && client.spaces.get(spaceId)) ?? AppSpace.getPersonalSpace(client);
|
|
79
81
|
if (!space) {
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
82
|
-
const result = await handlePreviewLookup(
|
|
84
|
+
const result = await handlePreviewLookup(space, { dxn, label });
|
|
83
85
|
if (!result) {
|
|
84
86
|
return;
|
|
85
87
|
}
|
|
@@ -10,8 +10,9 @@ import { Surface } from '@dxos/app-framework/ui';
|
|
|
10
10
|
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
11
11
|
import { Obj, Type } from '@dxos/echo';
|
|
12
12
|
import { Card } from '@dxos/react-ui';
|
|
13
|
-
import { Expando
|
|
13
|
+
import { Expando } from '@dxos/schema';
|
|
14
14
|
import { Organization, Person, Pipeline, Task } from '@dxos/types';
|
|
15
|
+
import { Position } from '@dxos/util';
|
|
15
16
|
|
|
16
17
|
import { ExpandoCard, FormCard, JsonCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
|
|
17
18
|
|
|
@@ -25,8 +26,8 @@ export default Capability.makeModule(() =>
|
|
|
25
26
|
|
|
26
27
|
Surface.create<{ subject: Person.Person }>({
|
|
27
28
|
id: 'schemaPopoverContact',
|
|
28
|
-
position:
|
|
29
|
-
filter: AppSurface.object(AppSurface.
|
|
29
|
+
position: Position.first,
|
|
30
|
+
filter: AppSurface.object(AppSurface.CardContent, Person.Person),
|
|
30
31
|
component: ({ data, role }) => {
|
|
31
32
|
return (
|
|
32
33
|
<>
|
|
@@ -38,8 +39,8 @@ export default Capability.makeModule(() =>
|
|
|
38
39
|
}),
|
|
39
40
|
Surface.create({
|
|
40
41
|
id: 'schemaPopoverOrganization',
|
|
41
|
-
position:
|
|
42
|
-
filter: AppSurface.object(AppSurface.
|
|
42
|
+
position: Position.first,
|
|
43
|
+
filter: AppSurface.object(AppSurface.CardContent, Organization.Organization),
|
|
43
44
|
component: ({ data, role }) => {
|
|
44
45
|
return (
|
|
45
46
|
<>
|
|
@@ -51,23 +52,23 @@ export default Capability.makeModule(() =>
|
|
|
51
52
|
}),
|
|
52
53
|
Surface.create({
|
|
53
54
|
id: 'schemaPopoverProject',
|
|
54
|
-
position:
|
|
55
|
-
filter: AppSurface.object(AppSurface.
|
|
55
|
+
position: Position.first,
|
|
56
|
+
filter: AppSurface.object(AppSurface.CardContent, Pipeline.Pipeline),
|
|
56
57
|
component: ({ data, role }) => {
|
|
57
58
|
return <ProjectCard role={role} subject={data.subject} />;
|
|
58
59
|
},
|
|
59
60
|
}),
|
|
60
61
|
Surface.create({
|
|
61
62
|
id: 'schemaPopoverTask',
|
|
62
|
-
position:
|
|
63
|
-
filter: AppSurface.object(AppSurface.
|
|
63
|
+
position: Position.first,
|
|
64
|
+
filter: AppSurface.object(AppSurface.CardContent, Task.Task),
|
|
64
65
|
component: ({ data, role }) => {
|
|
65
66
|
return <TaskCard role={role} subject={data.subject} />;
|
|
66
67
|
},
|
|
67
68
|
}),
|
|
68
69
|
Surface.create<AppSurface.ObjectCardData<Expando.Expando>>({
|
|
69
70
|
id: 'schemaPopoverExpando',
|
|
70
|
-
filter: AppSurface.object(AppSurface.
|
|
71
|
+
filter: AppSurface.object(AppSurface.CardContent, Expando.Expando),
|
|
71
72
|
component: ({ data, role }) => {
|
|
72
73
|
return <ExpandoCard role={role} subject={data.subject} ignorePaths={data.ignorePaths} />;
|
|
73
74
|
},
|
|
@@ -75,19 +76,18 @@ export default Capability.makeModule(() =>
|
|
|
75
76
|
|
|
76
77
|
Surface.create({
|
|
77
78
|
id: 'schemaPopoverDynamicType',
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!Obj.isObject(data.subject)) {
|
|
79
|
+
filter: AppSurface.subject(AppSurface.CardContent, (subject): subject is Obj.Unknown => {
|
|
80
|
+
if (!Obj.isObject(subject)) {
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
83
|
-
const type = Obj.getType(
|
|
83
|
+
const type = Obj.getType(subject);
|
|
84
84
|
if (type) {
|
|
85
85
|
return Type.getDatabase(type) != null;
|
|
86
86
|
}
|
|
87
87
|
// Obj.getType fails for database-registered schemas (DXN mismatch); fall back to typename query.
|
|
88
88
|
try {
|
|
89
|
-
const db = Obj.getDatabase(
|
|
90
|
-
const typename = Obj.getTypename(
|
|
89
|
+
const db = Obj.getDatabase(subject);
|
|
90
|
+
const typename = Obj.getTypename(subject);
|
|
91
91
|
return (
|
|
92
92
|
!!db &&
|
|
93
93
|
!!typename &&
|
|
@@ -99,7 +99,7 @@ export default Capability.makeModule(() =>
|
|
|
99
99
|
} catch {
|
|
100
100
|
return false;
|
|
101
101
|
}
|
|
102
|
-
},
|
|
102
|
+
}),
|
|
103
103
|
component: ({ data, role }) => {
|
|
104
104
|
// Dynamic/mutable schemas render an editable, full-layout form;
|
|
105
105
|
// FormCard handles both static and runtime schema resolution internally.
|
|
@@ -113,9 +113,8 @@ export default Capability.makeModule(() =>
|
|
|
113
113
|
|
|
114
114
|
Surface.create({
|
|
115
115
|
id: 'fallbackPopover',
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
filter: (data): data is { subject: Obj.Unknown; projection?: ProjectionModel } => Obj.isObject(data.subject),
|
|
116
|
+
position: Position.last,
|
|
117
|
+
filter: AppSurface.subject(AppSurface.CardContent, Obj.isObject),
|
|
119
118
|
component: ({ data, role }) => {
|
|
120
119
|
return <FormCard role={role} subject={data.subject} projection={data.projection} />;
|
|
121
120
|
},
|
|
@@ -123,9 +122,8 @@ export default Capability.makeModule(() =>
|
|
|
123
122
|
|
|
124
123
|
Surface.create({
|
|
125
124
|
id: 'fallbackJson',
|
|
126
|
-
|
|
127
|
-
position:
|
|
128
|
-
filter: (data): data is Record<string, unknown> => true,
|
|
125
|
+
filter: Surface.makeFilter(AppSurface.CardContent),
|
|
126
|
+
position: Position.last,
|
|
129
127
|
component: ({ data }) => {
|
|
130
128
|
return <JsonCard data={data} />;
|
|
131
129
|
},
|
|
@@ -133,14 +131,14 @@ export default Capability.makeModule(() =>
|
|
|
133
131
|
|
|
134
132
|
Surface.create({
|
|
135
133
|
id: 'section',
|
|
136
|
-
position:
|
|
134
|
+
position: Position.last,
|
|
137
135
|
filter: AppSurface.subject(AppSurface.Section, Obj.isObject),
|
|
138
136
|
component: ({ data }) => {
|
|
139
137
|
return (
|
|
140
138
|
<div className='flex w-full justify-center'>
|
|
141
139
|
<div className='py-2 dx-card-min-width dx-card-max-width'>
|
|
142
140
|
<Card.Root>
|
|
143
|
-
<Surface.Surface type={AppSurface.
|
|
141
|
+
<Surface.Surface type={AppSurface.CardContent} data={data} limit={1} />
|
|
144
142
|
</Card.Root>
|
|
145
143
|
</div>
|
|
146
144
|
</div>
|
package/src/cards/FormCard.tsx
CHANGED
|
@@ -10,8 +10,8 @@ import { Obj, Type } from '@dxos/echo';
|
|
|
10
10
|
import { useType } from '@dxos/echo-react';
|
|
11
11
|
import { type AnyProperties } from '@dxos/echo/internal';
|
|
12
12
|
import { SchemaEx } from '@dxos/effect';
|
|
13
|
-
import { Card,
|
|
14
|
-
import { Form, type
|
|
13
|
+
import { Card, useTranslation } from '@dxos/react-ui';
|
|
14
|
+
import { Form, type FormPresentation, type FormUpdateMeta, getFormProperties, omitId } from '@dxos/react-ui-form';
|
|
15
15
|
import { type ProjectionModel } from '@dxos/schema';
|
|
16
16
|
|
|
17
17
|
import { meta } from '#meta';
|
|
@@ -19,7 +19,7 @@ import { meta } from '#meta';
|
|
|
19
19
|
export type FormCardProps = AppSurface.ObjectCardProps & {
|
|
20
20
|
projection?: ProjectionModel;
|
|
21
21
|
readonly?: boolean;
|
|
22
|
-
layout?:
|
|
22
|
+
layout?: FormPresentation;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -29,18 +29,29 @@ export type FormCardProps = AppSurface.ObjectCardProps & {
|
|
|
29
29
|
* schema looked up via `useType`.
|
|
30
30
|
*/
|
|
31
31
|
export const FormCard = ({ subject, projection, readonly = true, layout }: FormCardProps) => {
|
|
32
|
-
const { t } = useTranslation(meta.
|
|
32
|
+
const { t } = useTranslation(meta.profile.key);
|
|
33
33
|
// Readonly cards default to the `static` presentation — plain DOM, undefined values
|
|
34
34
|
// omitted — which reads as a preview rather than a form. Editable cards keep the
|
|
35
35
|
// `compact` form layout. Callers can override either via the explicit `layout` prop.
|
|
36
|
-
const resolvedLayout:
|
|
36
|
+
const resolvedLayout: FormPresentation = layout ?? (readonly ? 'static' : 'compact');
|
|
37
37
|
|
|
38
38
|
// Try the static schema first; fall back to the runtime/database schema for
|
|
39
39
|
// dynamic types whose schema isn't reachable via `Obj.getSchema` (DXN mismatch).
|
|
40
40
|
const staticType = Obj.getType(subject);
|
|
41
41
|
const db = Obj.getDatabase(subject);
|
|
42
|
-
// Obj.getTypeURI throws
|
|
43
|
-
|
|
42
|
+
// `Obj.getTypeURI` throws on corrupted objects that are missing a type; swallow that
|
|
43
|
+
// and fall through to the "unable to create preview" path rather than crashing the card.
|
|
44
|
+
const fallbackTypeUri = useMemo(() => {
|
|
45
|
+
if (staticType) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return Obj.getTypeURI(subject);
|
|
50
|
+
} catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}, [staticType, subject]);
|
|
54
|
+
const runtimeType = useType(db, fallbackTypeUri);
|
|
44
55
|
const schema = useMemo((): Schema.Schema.AnyNoContext | undefined => {
|
|
45
56
|
const resolvedType = runtimeType ?? staticType;
|
|
46
57
|
return resolvedType ? omitId(Type.getSchema(resolvedType)) : undefined;
|
|
@@ -112,14 +123,6 @@ export const FormCard = ({ subject, projection, readonly = true, layout }: FormC
|
|
|
112
123
|
</Form.Content>
|
|
113
124
|
</Form.Viewport>
|
|
114
125
|
</Form.Root>
|
|
115
|
-
<Card.Row fullWidth>
|
|
116
|
-
<div className='pt-1'>
|
|
117
|
-
<div {...{ 'data-hue': hue }} className='inline-flex items-center gap-1 dx-tag'>
|
|
118
|
-
<Icon icon={icon} />
|
|
119
|
-
<span>{Obj.getTypename(subject)}</span>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</Card.Row>
|
|
123
126
|
</Card.Body>
|
|
124
127
|
);
|
|
125
128
|
};
|
package/src/cards/JsonCard.tsx
CHANGED
|
@@ -2,18 +2,33 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React from 'react';
|
|
5
|
+
import React, { useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Card } from '@dxos/react-ui';
|
|
7
|
+
import { Card, ToggleIconButton } from '@dxos/react-ui';
|
|
8
8
|
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
9
9
|
|
|
10
10
|
export const JsonCard = ({ data }: { data: unknown }) => {
|
|
11
|
+
const [open, setOpen] = useState(false);
|
|
12
|
+
// JSON.stringify throws on circular references / unsupported values (e.g. BigInt); keep the card stable.
|
|
13
|
+
let collapsedLength = 0;
|
|
14
|
+
try {
|
|
15
|
+
collapsedLength = JSON.stringify(data)?.length ?? 0;
|
|
16
|
+
} catch {}
|
|
11
17
|
return (
|
|
12
|
-
<Card.
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
<Card.Row>
|
|
19
|
+
<Card.Block>
|
|
20
|
+
<ToggleIconButton
|
|
21
|
+
variant='ghost'
|
|
22
|
+
icon='ph--caret-right--regular'
|
|
23
|
+
iconOnly
|
|
24
|
+
active={open}
|
|
25
|
+
onClick={() => setOpen(!open)}
|
|
26
|
+
label='Toggle JSON'
|
|
27
|
+
/>
|
|
28
|
+
</Card.Block>
|
|
29
|
+
{(open && <JsonHighlighter data={data} classNames='py-1.5 col-span-full text-xs overflow-auto' />) || (
|
|
30
|
+
<Card.Text variant='description'>{collapsedLength}</Card.Text>
|
|
31
|
+
)}
|
|
32
|
+
</Card.Row>
|
|
18
33
|
);
|
|
19
34
|
};
|
|
@@ -13,11 +13,7 @@ export const OrganizationCard = ({ subject }: AppSurface.ObjectCardProps<Organiz
|
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
15
|
<Card.Body>
|
|
16
|
-
<Card.Poster
|
|
17
|
-
alt={name ?? ''}
|
|
18
|
-
{...(image ? { image } : { icon: 'ph--building-office--regular' })}
|
|
19
|
-
classNames={!image && 'opacity-50'}
|
|
20
|
-
/>
|
|
16
|
+
{image && <Card.Poster alt={name ?? ''} image={image} />}
|
|
21
17
|
{description && (
|
|
22
18
|
<Card.Row>
|
|
23
19
|
<Card.Text variant='description'>{description}</Card.Text>
|
package/src/cards/PersonCard.tsx
CHANGED
|
@@ -6,12 +6,12 @@ import * as Effect from 'effect/Effect';
|
|
|
6
6
|
import React, { useCallback } from 'react';
|
|
7
7
|
|
|
8
8
|
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
9
|
-
import { LayoutOperation,
|
|
9
|
+
import { LayoutOperation, Paths } from '@dxos/app-toolkit';
|
|
10
10
|
import { type AppSurface } from '@dxos/app-toolkit/ui';
|
|
11
11
|
import { Obj } from '@dxos/echo';
|
|
12
12
|
import { EffectEx } from '@dxos/effect';
|
|
13
13
|
import { Avatar } from '@dxos/react-ui';
|
|
14
|
-
import { Card } from '@dxos/react-ui';
|
|
14
|
+
import { Card, Icon } from '@dxos/react-ui';
|
|
15
15
|
import { type Person } from '@dxos/types';
|
|
16
16
|
|
|
17
17
|
export const PersonCard = ({ subject }: AppSurface.ObjectCardProps<Person.Person>) => {
|
|
@@ -24,42 +24,47 @@ export const PersonCard = ({ subject }: AppSurface.ObjectCardProps<Person.Person
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
return Effect.gen(function* () {
|
|
27
|
-
const organizationPath = getObjectPathFromObject(organization);
|
|
27
|
+
const organizationPath = Paths.getObjectPathFromObject(organization);
|
|
28
28
|
const db = Obj.getDatabase(organization);
|
|
29
29
|
yield* invoke(LayoutOperation.UpdatePopover, { state: false, anchorId: '' });
|
|
30
30
|
yield* invoke(LayoutOperation.Open, {
|
|
31
31
|
subject: [organizationPath],
|
|
32
|
-
workspace: db ? getSpacePath(db.spaceId) : undefined,
|
|
32
|
+
workspace: db ? Paths.getSpacePath(db.spaceId) : undefined,
|
|
33
33
|
});
|
|
34
34
|
}).pipe(EffectEx.runAndForwardErrors);
|
|
35
35
|
}, [invoke, organization]);
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
38
|
<Card.Body>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
39
|
+
{image && (
|
|
40
|
+
<Card.Row>
|
|
41
|
+
<Avatar.Root>
|
|
42
42
|
<Avatar.Content
|
|
43
43
|
imgSrc={image}
|
|
44
44
|
icon='ph--user--regular'
|
|
45
45
|
size={20}
|
|
46
|
-
classNames={!image && 'opacity-50'}
|
|
46
|
+
classNames={[!image && 'opacity-50']}
|
|
47
47
|
hue='neutral'
|
|
48
48
|
variant='square'
|
|
49
49
|
/>
|
|
50
|
-
</
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</Card.
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
</Avatar.Root>
|
|
51
|
+
</Card.Row>
|
|
52
|
+
)}
|
|
53
|
+
{organization?.name && (
|
|
54
|
+
<Card.Action icon='ph--buildings--regular' label={organization.name} onClick={handleOrganizationClick} />
|
|
55
|
+
)}
|
|
56
|
+
{emails.length > 0 && (
|
|
57
|
+
<Card.Row>
|
|
58
|
+
<Card.Block>
|
|
59
|
+
<Icon icon='ph--at--regular' />
|
|
60
|
+
</Card.Block>
|
|
61
|
+
<Card.Text truncate className='text-sky-text text-sm'>
|
|
62
|
+
{emails.map(({ value }) => (
|
|
63
|
+
<div key={value}>{value}</div>
|
|
64
|
+
))}
|
|
65
|
+
</Card.Text>
|
|
66
|
+
</Card.Row>
|
|
67
|
+
)}
|
|
63
68
|
</Card.Body>
|
|
64
69
|
);
|
|
65
70
|
};
|
|
@@ -16,7 +16,11 @@ export const ProjectCard = ({ subject }: AppSurface.ObjectCardProps<Pipeline.Pip
|
|
|
16
16
|
<Card.Body>
|
|
17
17
|
{image && <Card.Poster image={image} alt={Obj.getLabel(subject) ?? ''} aspect='auto' />}
|
|
18
18
|
{/* <CardHeader label={name} subject={subject} db={db} /> */}
|
|
19
|
-
{description &&
|
|
19
|
+
{description && (
|
|
20
|
+
<Card.Row>
|
|
21
|
+
<Card.Text variant='description'>{description}</Card.Text>
|
|
22
|
+
</Card.Row>
|
|
23
|
+
)}
|
|
20
24
|
</Card.Body>
|
|
21
25
|
);
|
|
22
26
|
};
|
package/src/meta.ts
CHANGED
|
@@ -3,18 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Plugin } from '@dxos/app-framework';
|
|
6
|
-
import { DXN } from '@dxos/keys';
|
|
7
|
-
import { trim } from '@dxos/util';
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
author: 'DXOS',
|
|
13
|
-
description: trim`
|
|
14
|
-
Rich preview panel for viewing object content without opening full editors.
|
|
15
|
-
Quick peek at documents, images, and data with inline rendering.
|
|
16
|
-
`,
|
|
17
|
-
icon: 'ph--eye--regular',
|
|
18
|
-
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-preview',
|
|
19
|
-
tags: ['system'],
|
|
20
|
-
});
|
|
7
|
+
import config from '../dx.config';
|
|
8
|
+
|
|
9
|
+
export const meta = Plugin.getMetaFromConfig(config);
|
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import React from 'react';
|
|
7
6
|
|
|
8
7
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
9
8
|
import { corePlugins } from '@dxos/plugin-testing';
|
|
10
9
|
import { random } from '@dxos/random';
|
|
11
|
-
import { Card } from '@dxos/react-ui';
|
|
12
10
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
13
11
|
import { type Expando } from '@dxos/schema';
|
|
14
12
|
import { type Organization, type Person, type Pipeline, type Task } from '@dxos/types';
|
|
15
13
|
|
|
16
14
|
import { translations } from '#translations';
|
|
17
15
|
|
|
18
|
-
import { ExpandoCard, FormCard,
|
|
16
|
+
import { ExpandoCard, FormCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
|
|
19
17
|
import {
|
|
20
18
|
DefaultStory,
|
|
21
19
|
createExpando,
|
|
@@ -114,6 +112,13 @@ export const _Person: StoryObj<typeof DefaultStory<Person.Person>> = {
|
|
|
114
112
|
},
|
|
115
113
|
};
|
|
116
114
|
|
|
115
|
+
export const _PersonNoImage: StoryObj<typeof DefaultStory<Person.Person>> = {
|
|
116
|
+
args: {
|
|
117
|
+
Component: PersonCard,
|
|
118
|
+
createObject: createPerson,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
117
122
|
export const _Organization: StoryObj<typeof DefaultStory<Organization.Organization>> = {
|
|
118
123
|
args: {
|
|
119
124
|
Component: OrganizationCard,
|
|
@@ -122,7 +127,14 @@ export const _Organization: StoryObj<typeof DefaultStory<Organization.Organizati
|
|
|
122
127
|
},
|
|
123
128
|
};
|
|
124
129
|
|
|
125
|
-
export const
|
|
130
|
+
export const _OrganizationNoImage: StoryObj<typeof DefaultStory<Organization.Organization>> = {
|
|
131
|
+
args: {
|
|
132
|
+
Component: OrganizationCard,
|
|
133
|
+
createObject: createOrganization,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const _Pipeline: StoryObj<typeof DefaultStory<Pipeline.Pipeline>> = {
|
|
126
138
|
args: {
|
|
127
139
|
Component: ProjectCard,
|
|
128
140
|
createObject: createProject,
|
|
@@ -144,34 +156,3 @@ export const _Expando: StoryObj<typeof DefaultStory<Expando.Expando>> = {
|
|
|
144
156
|
createObject: createExpando,
|
|
145
157
|
},
|
|
146
158
|
};
|
|
147
|
-
|
|
148
|
-
export const _Json = {
|
|
149
|
-
render: () => {
|
|
150
|
-
const data = {
|
|
151
|
-
subject: {
|
|
152
|
-
id: random.string.uuid(),
|
|
153
|
-
name: random.person.fullName(),
|
|
154
|
-
email: random.internet.email(),
|
|
155
|
-
tags: [random.lorem.word(), random.lorem.word(), random.lorem.word()],
|
|
156
|
-
nested: {
|
|
157
|
-
count: random.number.int({ max: 100 }),
|
|
158
|
-
active: random.datatype.boolean(),
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<div className='flex justify-center p-16'>
|
|
165
|
-
<div className='dx-card-min-width dx-card-max-width'>
|
|
166
|
-
<Card.Root>
|
|
167
|
-
<Card.Header>
|
|
168
|
-
<Card.IconBlock />
|
|
169
|
-
<Card.Title>JSON</Card.Title>
|
|
170
|
-
</Card.Header>
|
|
171
|
-
<JsonCard data={data} />
|
|
172
|
-
</Card.Root>
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
);
|
|
176
|
-
},
|
|
177
|
-
};
|