@dxos/plugin-preview 0.8.4-main.2e9d522 → 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.
Files changed (101) hide show
  1. package/dist/lib/browser/chunk-CEWM5LJG.mjs +17 -0
  2. package/dist/lib/browser/chunk-CEWM5LJG.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +14 -10
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/{preview-popover-2UCEZUQW.mjs → preview-popover-RKHENTFP.mjs} +9 -13
  7. package/dist/lib/browser/preview-popover-RKHENTFP.mjs.map +7 -0
  8. package/dist/lib/browser/react-surface-ZAOGRCMT.mjs +527 -0
  9. package/dist/lib/browser/react-surface-ZAOGRCMT.mjs.map +7 -0
  10. package/dist/lib/node-esm/chunk-Q5MUNELK.mjs +19 -0
  11. package/dist/lib/node-esm/chunk-Q5MUNELK.mjs.map +7 -0
  12. package/dist/lib/node-esm/index.mjs +14 -10
  13. package/dist/lib/node-esm/index.mjs.map +3 -3
  14. package/dist/lib/node-esm/meta.json +1 -1
  15. package/dist/lib/node-esm/{preview-popover-OSHWR2YE.mjs → preview-popover-TZKX67EY.mjs} +9 -13
  16. package/dist/lib/node-esm/preview-popover-TZKX67EY.mjs.map +7 -0
  17. package/dist/lib/node-esm/react-surface-X76BTDM5.mjs +528 -0
  18. package/dist/lib/node-esm/react-surface-X76BTDM5.mjs.map +7 -0
  19. package/dist/types/src/PreviewPlugin.d.ts +1 -1
  20. package/dist/types/src/PreviewPlugin.d.ts.map +1 -1
  21. package/dist/types/src/capabilities/index.d.ts +2 -2
  22. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  23. package/dist/types/src/capabilities/preview-popover.d.ts +1 -1
  24. package/dist/types/src/capabilities/preview-popover.d.ts.map +1 -1
  25. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  26. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  27. package/dist/types/src/cards/OrganizationCard.d.ts +5 -0
  28. package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -0
  29. package/dist/types/src/cards/PersonCard.d.ts +5 -0
  30. package/dist/types/src/cards/PersonCard.d.ts.map +1 -0
  31. package/dist/types/src/cards/ProjectCard.d.ts +5 -0
  32. package/dist/types/src/cards/ProjectCard.d.ts.map +1 -0
  33. package/dist/types/src/cards/TaskCard.d.ts +5 -0
  34. package/dist/types/src/cards/TaskCard.d.ts.map +1 -0
  35. package/dist/types/src/cards/index.d.ts +5 -0
  36. package/dist/types/src/cards/index.d.ts.map +1 -0
  37. package/dist/types/src/components/CardComponents.d.ts +16 -0
  38. package/dist/types/src/components/CardComponents.d.ts.map +1 -0
  39. package/dist/types/src/components/CardSubjectMenu.d.ts +8 -0
  40. package/dist/types/src/components/CardSubjectMenu.d.ts.map +1 -0
  41. package/dist/types/src/components/index.d.ts +2 -3
  42. package/dist/types/src/components/index.d.ts.map +1 -1
  43. package/dist/types/src/meta.d.ts +0 -1
  44. package/dist/types/src/meta.d.ts.map +1 -1
  45. package/dist/types/src/stories/Card.stories.d.ts +29 -0
  46. package/dist/types/src/stories/Card.stories.d.ts.map +1 -0
  47. package/dist/types/src/stories/testing.d.ts +18 -0
  48. package/dist/types/src/stories/testing.d.ts.map +1 -0
  49. package/dist/types/src/translations.d.ts +6 -0
  50. package/dist/types/src/translations.d.ts.map +1 -1
  51. package/dist/types/src/types.d.ts +6 -2
  52. package/dist/types/src/types.d.ts.map +1 -1
  53. package/dist/types/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +34 -29
  55. package/src/PreviewPlugin.tsx +23 -24
  56. package/src/capabilities/preview-popover.ts +9 -14
  57. package/src/capabilities/react-surface.tsx +81 -93
  58. package/src/cards/OrganizationCard.tsx +29 -0
  59. package/src/cards/PersonCard.tsx +63 -0
  60. package/src/cards/ProjectCard.tsx +24 -0
  61. package/src/cards/TaskCard.tsx +46 -0
  62. package/src/cards/index.ts +8 -0
  63. package/src/components/CardComponents.tsx +67 -0
  64. package/src/components/CardSubjectMenu.tsx +103 -0
  65. package/src/components/index.ts +2 -3
  66. package/src/meta.ts +6 -3
  67. package/src/stories/Card.stories.tsx +62 -0
  68. package/src/stories/testing.tsx +114 -0
  69. package/src/translations.ts +6 -0
  70. package/src/types.ts +7 -3
  71. package/dist/lib/browser/chunk-Z73ENKLE.mjs +0 -14
  72. package/dist/lib/browser/chunk-Z73ENKLE.mjs.map +0 -7
  73. package/dist/lib/browser/preview-popover-2UCEZUQW.mjs.map +0 -7
  74. package/dist/lib/browser/react-surface-CZMUEWPV.mjs +0 -303
  75. package/dist/lib/browser/react-surface-CZMUEWPV.mjs.map +0 -7
  76. package/dist/lib/node-esm/chunk-2ORSL4GJ.mjs +0 -16
  77. package/dist/lib/node-esm/chunk-2ORSL4GJ.mjs.map +0 -7
  78. package/dist/lib/node-esm/preview-popover-OSHWR2YE.mjs.map +0 -7
  79. package/dist/lib/node-esm/react-surface-IPZBISWS.mjs +0 -304
  80. package/dist/lib/node-esm/react-surface-IPZBISWS.mjs.map +0 -7
  81. package/dist/types/src/components/ContactCard.d.ts +0 -7
  82. package/dist/types/src/components/ContactCard.d.ts.map +0 -1
  83. package/dist/types/src/components/OrganizationCard.d.ts +0 -5
  84. package/dist/types/src/components/OrganizationCard.d.ts.map +0 -1
  85. package/dist/types/src/components/ProjectCard.d.ts +0 -5
  86. package/dist/types/src/components/ProjectCard.d.ts.map +0 -1
  87. package/dist/types/src/testing/ExtrinsicPreviews.stories.d.ts +0 -35
  88. package/dist/types/src/testing/ExtrinsicPreviews.stories.d.ts.map +0 -1
  89. package/dist/types/src/testing/IntrinsicPreviews.stories.d.ts +0 -35
  90. package/dist/types/src/testing/IntrinsicPreviews.stories.d.ts.map +0 -1
  91. package/dist/types/src/testing/PopoverPreviews.stories.d.ts +0 -35
  92. package/dist/types/src/testing/PopoverPreviews.stories.d.ts.map +0 -1
  93. package/dist/types/src/testing/util.d.ts +0 -80
  94. package/dist/types/src/testing/util.d.ts.map +0 -1
  95. package/src/components/ContactCard.tsx +0 -70
  96. package/src/components/OrganizationCard.tsx +0 -41
  97. package/src/components/ProjectCard.tsx +0 -20
  98. package/src/testing/ExtrinsicPreviews.stories.tsx +0 -55
  99. package/src/testing/IntrinsicPreviews.stories.tsx +0 -55
  100. package/src/testing/PopoverPreviews.stories.tsx +0 -55
  101. package/src/testing/util.tsx +0 -79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-preview",
3
- "version": "0.8.4-main.2e9d522",
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",
@@ -10,6 +10,7 @@
10
10
  "type": "module",
11
11
  "exports": {
12
12
  ".": {
13
+ "source": "./src/index.ts",
13
14
  "types": "./dist/types/src/index.d.ts",
14
15
  "browser": "./dist/lib/browser/index.mjs"
15
16
  }
@@ -23,40 +24,44 @@
23
24
  "src"
24
25
  ],
25
26
  "dependencies": {
27
+ "@effect-atom/atom-react": "^0.3.4",
26
28
  "@preact-signals/safe-react": "^0.9.0",
27
- "@dxos/app-framework": "0.8.4-main.2e9d522",
28
- "@dxos/async": "0.8.4-main.2e9d522",
29
- "@dxos/client": "0.8.4-main.2e9d522",
30
- "@dxos/echo": "0.8.4-main.2e9d522",
31
- "@dxos/echo-schema": "0.8.4-main.2e9d522",
32
- "@dxos/keys": "0.8.4-main.2e9d522",
33
- "@dxos/log": "0.8.4-main.2e9d522",
34
- "@dxos/lit-ui": "0.8.4-main.2e9d522",
35
- "@dxos/plugin-attention": "0.8.4-main.2e9d522",
36
- "@dxos/plugin-client": "0.8.4-main.2e9d522",
37
- "@dxos/plugin-deck": "0.8.4-main.2e9d522",
38
- "@dxos/react-client": "0.8.4-main.2e9d522",
39
- "@dxos/react-ui": "0.8.4-main.2e9d522",
40
- "@dxos/react-ui-form": "0.8.4-main.2e9d522",
41
- "@dxos/react-ui-stack": "0.8.4-main.2e9d522",
42
- "@dxos/react-ui-editor": "0.8.4-main.2e9d522",
43
- "@dxos/react-ui-table": "0.8.4-main.2e9d522",
44
- "@dxos/react-ui-theme": "0.8.4-main.2e9d522",
45
- "@dxos/schema": "0.8.4-main.2e9d522",
46
- "@dxos/util": "0.8.4-main.2e9d522"
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"
47
52
  },
48
53
  "devDependencies": {
49
- "effect": "3.17.0",
50
- "react": "~18.2.0",
51
- "react-dom": "~18.2.0",
52
- "vite": "5.4.7",
53
- "@dxos/random": "0.8.4-main.2e9d522",
54
- "@dxos/storybook-utils": "0.8.4-main.2e9d522"
54
+ "effect": "3.18.3",
55
+ "react": "~19.2.0",
56
+ "react-dom": "~19.2.0",
57
+ "vite": "7.1.9",
58
+ "@dxos/random": "0.8.4-main.548089c",
59
+ "@dxos/storybook-utils": "0.8.4-main.548089c"
55
60
  },
56
61
  "peerDependencies": {
57
62
  "effect": "^3.13.3",
58
- "react": "~18.2.0",
59
- "react-dom": "~18.2.0"
63
+ "react": "^19.0.0",
64
+ "react-dom": "^19.0.0"
60
65
  },
61
66
  "publishConfig": {
62
67
  "access": "public"
@@ -4,32 +4,31 @@
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 { DataType } from '@dxos/schema';
7
+ import { Organization, Person } from '@dxos/types';
8
8
 
9
9
  import { PreviewPopover, ReactSurface } from './capabilities';
10
10
  import { meta } from './meta';
11
11
  import { translations } from './translations';
12
12
 
13
- export const PreviewPlugin = () =>
14
- definePlugin(meta, [
15
- defineModule({
16
- id: `${meta.id}/module/translations`,
17
- activatesOn: Events.SetupTranslations,
18
- activate: () => contributes(Capabilities.Translations, translations),
19
- }),
20
- defineModule({
21
- id: `${meta.id}/module/schema`,
22
- activatesOn: ClientEvents.SetupSchema,
23
- activate: () => [contributes(ClientCapabilities.Schema, [DataType.Person, DataType.Organization])],
24
- }),
25
- defineModule({
26
- id: `${meta.id}/module/preview-popover`,
27
- activatesOn: Events.Startup,
28
- activate: PreviewPopover,
29
- }),
30
- defineModule({
31
- id: `${meta.id}/module/react-surface`,
32
- activatesOn: Events.SetupReactSurface,
33
- activate: ReactSurface,
34
- }),
35
- ]);
13
+ export const PreviewPlugin = definePlugin(meta, () => [
14
+ defineModule({
15
+ id: `${meta.id}/module/translations`,
16
+ activatesOn: Events.SetupTranslations,
17
+ activate: () => contributes(Capabilities.Translations, translations),
18
+ }),
19
+ defineModule({
20
+ id: `${meta.id}/module/schema`,
21
+ activatesOn: ClientEvents.SetupSchema,
22
+ activate: () => [contributes(ClientCapabilities.Schema, [Person.Person, Organization.Organization])],
23
+ }),
24
+ defineModule({
25
+ id: `${meta.id}/module/preview-popover`,
26
+ activatesOn: Events.Startup,
27
+ activate: PreviewPopover,
28
+ }),
29
+ defineModule({
30
+ id: `${meta.id}/module/react-surface`,
31
+ activatesOn: Events.SetupReactSurface,
32
+ activate: ReactSurface,
33
+ }),
34
+ ]);
@@ -2,14 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, LayoutAction, contributes, createIntent, type PluginContext } from '@dxos/app-framework';
5
+ import { Capabilities, LayoutAction, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
6
6
  import { addEventListener } from '@dxos/async';
7
- import { type Client, resolveRef } from '@dxos/client';
8
- import { parseId, type Space } from '@dxos/client/echo';
7
+ import { type Client } from '@dxos/client';
8
+ import { type Space, parseId } from '@dxos/client/echo';
9
9
  import { DXN } from '@dxos/keys';
10
- import { type DxRefTagActivate } from '@dxos/lit-ui';
11
10
  import { log } from '@dxos/log';
12
11
  import { ClientCapabilities } from '@dxos/plugin-client';
12
+ import { DX_ANCHOR_ACTIVATE, type DxAnchorActivate } from '@dxos/react-ui';
13
13
  import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/react-ui-editor';
14
14
 
15
15
  const customEventOptions = { capture: true, passive: false };
@@ -20,21 +20,16 @@ const handlePreviewLookup = async (
20
20
  { ref, label }: PreviewLinkRef,
21
21
  ): Promise<PreviewLinkTarget | null> => {
22
22
  try {
23
- const dxn = DXN.parse(ref);
24
- if (!dxn) {
25
- return null;
26
- }
27
-
28
- const object = await resolveRef(client, dxn, defaultSpace);
23
+ const object = await defaultSpace.db.ref(DXN.parse(ref)).load();
29
24
  return { label, object };
30
- } catch (err) {
25
+ } catch {
31
26
  return null;
32
27
  }
33
28
  };
34
29
 
35
30
  export default (context: PluginContext) => {
36
31
  // TODO(wittjosiah): Factor out lookup handlers to other plugins to make not ECHO-specific.
37
- const handleDxRefTagActivate = async ({ refId, label, trigger }: DxRefTagActivate) => {
32
+ const handleDxAnchorActivate = async ({ refId, label, trigger }: DxAnchorActivate) => {
38
33
  const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
39
34
  const client = context.getCapability(ClientCapabilities.Client);
40
35
  const [layout] = context.getCapabilities(Capabilities.Layout);
@@ -62,8 +57,8 @@ export default (context: PluginContext) => {
62
57
  if (document.defaultView) {
63
58
  cleanup = addEventListener(
64
59
  document.defaultView,
65
- 'dx-ref-tag-activate' as any,
66
- handleDxRefTagActivate,
60
+ DX_ANCHOR_ACTIVATE as any,
61
+ handleDxAnchorActivate,
67
62
  customEventOptions,
68
63
  );
69
64
  } else {
@@ -2,137 +2,105 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Effect } from 'effect';
6
5
  import React, { useCallback } from 'react';
7
6
 
8
- import {
9
- Capabilities,
10
- LayoutAction,
11
- Surface,
12
- contributes,
13
- createIntent,
14
- createSurface,
15
- useIntentDispatcher,
16
- } from '@dxos/app-framework';
17
- import { fullyQualifiedId, getSchema, getSpace } from '@dxos/client/echo';
18
- import { Filter, Obj } from '@dxos/echo';
19
- import { type JsonPath, setValue } from '@dxos/echo-schema';
20
- import { AttentionAction } from '@dxos/plugin-attention/types';
21
- import { ATTENDABLE_PATH_SEPARATOR, DeckAction } from '@dxos/plugin-deck/types';
22
- import { useQuery, useSpace } from '@dxos/react-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';
10
+ import { Obj } from '@dxos/echo';
11
+ import { type JsonPath, setValue } from '@dxos/echo/internal';
12
+ import { useActiveSpace } from '@dxos/plugin-space';
23
13
  import { useTranslation } from '@dxos/react-ui';
24
14
  import { Form } from '@dxos/react-ui-form';
25
15
  import { Card } from '@dxos/react-ui-stack';
26
- import { TableView } from '@dxos/react-ui-table/types';
27
16
  import { descriptionMessage, mx } from '@dxos/react-ui-theme';
28
- import { DataType } from '@dxos/schema';
17
+ import { type ProjectionModel } from '@dxos/schema';
18
+ import { Organization, Person, Project, Task } from '@dxos/types';
29
19
 
30
- import { ContactCard, OrganizationCard, ProjectCard } from '../components';
31
- import { PREVIEW_PLUGIN } from '../meta';
20
+ import { OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
21
+ import { meta } from '../meta';
22
+ import { type PreviewProps } from '../types';
32
23
 
33
24
  export default () =>
34
25
  contributes(Capabilities.ReactSurface, [
35
26
  //
36
27
  // Specific schema types.
28
+ // TODO(burdon): Create helpers and factor out.
37
29
  //
30
+
38
31
  createSurface({
39
- id: `${PREVIEW_PLUGIN}/schema-popover--contact`,
32
+ id: `${meta.id}/schema-popover--contact`,
40
33
  role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
41
- filter: (data): data is { subject: DataType.Person } => Obj.instanceOf(DataType.Person, data.subject),
34
+ filter: (data): data is { subject: Person.Person } => Obj.instanceOf(Person.Person, data.subject),
42
35
  component: ({ data, role }) => {
43
- const { dispatch } = useIntentDispatcher();
36
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
44
37
  const space = getSpace(data.subject);
45
- const defaultSpace = useSpace();
46
-
47
- const currentSpaceOrgs = useQuery(space, Filter.type(DataType.Organization));
48
- const currentSpaceViews = useQuery(space, Filter.type(DataType.View));
49
- const defaultSpaceViews = useQuery(defaultSpace, Filter.type(DataType.View));
50
- const currentSpaceOrgTable = currentSpaceViews.find(
51
- (view) =>
52
- view.query.typename === DataType.Organization.typename &&
53
- Obj.instanceOf(TableView, view.presentation.target),
54
- );
55
- const defaultSpaceOrgTable = defaultSpaceViews.find(
56
- (view) =>
57
- view.query.typename === DataType.Organization.typename &&
58
- Obj.instanceOf(TableView, view.presentation.target),
38
+ const activeSpace = useActiveSpace(); // TODO(burdon): Disambiguate with space?
39
+ const handleSelect = useCallback<NonNullable<PreviewProps['onSelect']>>(
40
+ (object) =>
41
+ dispatch(
42
+ createIntent(LayoutAction.Open, {
43
+ part: 'main',
44
+ subject: [Obj.getDXN(object).toString()],
45
+ options: {
46
+ workspace: space?.id,
47
+ },
48
+ }),
49
+ ),
50
+ [dispatch],
59
51
  );
60
52
 
61
- // TODO(wittjosiah): Generalized way of handling related objects navigation.
62
- const handleOrgClick = useCallback(
63
- (org: DataType.Organization) =>
64
- Effect.gen(function* () {
65
- const view = currentSpaceOrgs.includes(org) ? currentSpaceOrgTable : defaultSpaceOrgTable;
66
- yield* dispatch(
67
- createIntent(LayoutAction.UpdatePopover, {
68
- part: 'popover',
69
- options: {
70
- state: false,
71
- anchorId: '',
72
- },
73
- }),
74
- );
75
- if (view) {
76
- const id = fullyQualifiedId(view);
77
- yield* dispatch(
78
- createIntent(LayoutAction.Open, {
79
- part: 'main',
80
- subject: [id],
81
- options: { workspace: space?.id },
82
- }),
83
- );
84
- yield* dispatch(
85
- createIntent(DeckAction.ChangeCompanion, {
86
- primary: id,
87
- companion: [id, 'selected-objects'].join(ATTENDABLE_PATH_SEPARATOR),
88
- }),
89
- );
90
- yield* dispatch(
91
- createIntent(AttentionAction.Select, {
92
- contextId: id,
93
- selection: { mode: 'multi', ids: [org.id] },
94
- }),
95
- );
96
- }
97
- }).pipe(Effect.runPromise),
98
- [dispatch, currentSpaceOrgs, currentSpaceOrgTable, defaultSpaceOrgTable],
53
+ return (
54
+ <PersonCard role={role} subject={data.subject} activeSpace={activeSpace} onSelect={handleSelect}>
55
+ {role === 'card--popover' && <Surface role='related' data={data} />}
56
+ </PersonCard>
99
57
  );
100
-
58
+ },
59
+ }),
60
+ createSurface({
61
+ id: `${meta.id}/schema-popover--organization`,
62
+ role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
63
+ filter: (data): data is { subject: Organization.Organization } =>
64
+ Obj.instanceOf(Organization.Organization, data.subject),
65
+ component: ({ data, role }) => {
66
+ const activeSpace = useActiveSpace();
101
67
  return (
102
- <ContactCard role={role} subject={data.subject} onOrgClick={handleOrgClick}>
68
+ <OrganizationCard role={role} subject={data.subject} activeSpace={activeSpace}>
103
69
  {role === 'card--popover' && <Surface role='related' data={data} />}
104
- </ContactCard>
70
+ </OrganizationCard>
105
71
  );
106
72
  },
107
73
  }),
108
74
  createSurface({
109
- id: `${PREVIEW_PLUGIN}/schema-popover--organization`,
75
+ id: `${meta.id}/schema-popover--project`,
110
76
  role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
111
- filter: (data): data is { subject: DataType.Organization } => Obj.instanceOf(DataType.Organization, data.subject),
112
- component: ({ data, role }) => (
113
- <OrganizationCard role={role} subject={data.subject}>
114
- {role === 'card--popover' && <Surface role='related' data={data} />}
115
- </OrganizationCard>
116
- ),
77
+ filter: (data): data is { subject: Project.Project } => Obj.instanceOf(Project.Project, data.subject),
78
+ component: ({ data, role }) => {
79
+ const activeSpace = useActiveSpace();
80
+ return <ProjectCard subject={data.subject} role={role} activeSpace={activeSpace} />;
81
+ },
117
82
  }),
118
83
  createSurface({
119
- id: `${PREVIEW_PLUGIN}/schema-popover--project`,
84
+ id: `${meta.id}/schema-popover--task`,
120
85
  role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
121
- filter: (data): data is { subject: DataType.Project } => Obj.instanceOf(DataType.Project, data.subject),
122
- component: ({ data, role }) => <ProjectCard subject={data.subject} role={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
+ },
123
90
  }),
124
91
 
125
92
  //
126
93
  // Fallback for any object.
127
94
  //
95
+
128
96
  createSurface({
129
- id: `${PREVIEW_PLUGIN}/fallback-popover`,
97
+ id: `${meta.id}/fallback-popover`,
130
98
  role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
131
99
  position: 'fallback',
132
- filter: (data): data is { subject: Obj.Any } => Obj.isObject(data.subject),
100
+ filter: (data): data is { subject: Obj.Any; projection?: ProjectionModel } => Obj.isObject(data.subject),
133
101
  component: ({ data, role }) => {
134
102
  const schema = getSchema(data.subject);
135
- const { t } = useTranslation(PREVIEW_PLUGIN);
103
+ const { t } = useTranslation(meta.id);
136
104
  if (!schema) {
137
105
  // TODO(burdon): Use Alert.
138
106
  return <p className={mx(descriptionMessage)}>{t('unable to create preview message')}</p>;
@@ -149,15 +117,35 @@ export default () =>
149
117
  return (
150
118
  <Card.SurfaceRoot role={role}>
151
119
  <Form
120
+ id={data.subject.id}
152
121
  schema={schema}
122
+ projection={data.projection}
153
123
  values={data.subject}
154
- readonly={role === 'card--popover'}
124
+ readonly={role === 'card--popover' ? 'static' : false}
155
125
  onSave={handleSave}
156
126
  autoSave
157
- {...(role === 'card--intrinsic' && { outerSpacing: 'blockStart-0' })}
127
+ {...(role === 'card--intrinsic' && {
128
+ outerSpacing: 'blockStart-0',
129
+ })}
158
130
  />
159
131
  </Card.SurfaceRoot>
160
132
  );
161
133
  },
162
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
+ }),
163
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
+ };
@@ -0,0 +1,46 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as SchemaAST from 'effect/SchemaAST';
6
+ import React from 'react';
7
+
8
+ import { type PropertyMetaAnnotation, PropertyMetaAnnotationId } from '@dxos/echo/internal';
9
+ import { Card, cardNoSpacing, cardSpacing } from '@dxos/react-ui-stack';
10
+ import { mx } from '@dxos/react-ui-theme';
11
+ import { Task } from '@dxos/types';
12
+
13
+ import { CardSubjectMenu } from '../components';
14
+ import { type PreviewProps } from '../types';
15
+
16
+ // TODO(thure): Should this move upstream as a helper? Is there an easier way to get options?
17
+ const getActiveStatusOption = (status?: string) => {
18
+ const properties = SchemaAST.getPropertySignatures(Task.Task.ast);
19
+ const statusProperty = properties.find((p) => p.name === 'status');
20
+ const statusMeta = SchemaAST.getAnnotation<PropertyMetaAnnotation>(PropertyMetaAnnotationId)(
21
+ // TODO(thure): Typescript asserts `.type` doesn’t have `.types`, but in runtime it does.
22
+ (statusProperty!.type as any).types[0],
23
+ );
24
+ const options = // TODO(thure): Typescript asserts `statusMeta` doesn’t have `.value`, but in runtime it does.
25
+ (statusMeta as any).value.singleSelect.options as { id: string; title: string; color: string }[];
26
+ return options.find(({ id }) => id === status);
27
+ };
28
+
29
+ export const TaskCard = ({ subject, role, activeSpace }: PreviewProps<Task.Task>) => {
30
+ const { title, status } = subject;
31
+ const statusOption = getActiveStatusOption(status);
32
+
33
+ return (
34
+ <Card.SurfaceRoot id={subject.id} role={role}>
35
+ <div role='none' className={mx('flex items-center gap-2', cardSpacing)}>
36
+ <Card.Heading classNames={[cardNoSpacing, 'min-is-0 flex-1 truncate']}>{title}</Card.Heading>
37
+ {statusOption && (
38
+ <span className='dx-tag' data-hue={statusOption.color}>
39
+ {statusOption.title}
40
+ </span>
41
+ )}
42
+ <CardSubjectMenu subject={subject} activeSpace={activeSpace} />
43
+ </div>
44
+ </Card.SurfaceRoot>
45
+ );
46
+ };
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './OrganizationCard';
6
+ export * from './PersonCard';
7
+ export * from './ProjectCard';
8
+ export * from './TaskCard';