@dxos/plugin-preview 0.8.4-main.2e9d522 → 0.8.4-main.3c1ae3b

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 (104) 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-4QZ2WDE6.mjs} +9 -13
  7. package/dist/lib/browser/preview-popover-4QZ2WDE6.mjs.map +7 -0
  8. package/dist/lib/browser/react-surface-4AKVMPIJ.mjs +513 -0
  9. package/dist/lib/browser/react-surface-4AKVMPIJ.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-FJ2ARIGS.mjs} +9 -13
  16. package/dist/lib/node-esm/preview-popover-FJ2ARIGS.mjs.map +7 -0
  17. package/dist/lib/node-esm/react-surface-ZJ4C32OR.mjs +514 -0
  18. package/dist/lib/node-esm/react-surface-ZJ4C32OR.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/FormCard.d.ts +8 -0
  28. package/dist/types/src/cards/FormCard.d.ts.map +1 -0
  29. package/dist/types/src/cards/OrganizationCard.d.ts +5 -0
  30. package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -0
  31. package/dist/types/src/cards/PersonCard.d.ts +5 -0
  32. package/dist/types/src/cards/PersonCard.d.ts.map +1 -0
  33. package/dist/types/src/cards/ProjectCard.d.ts +5 -0
  34. package/dist/types/src/cards/ProjectCard.d.ts.map +1 -0
  35. package/dist/types/src/cards/TaskCard.d.ts +5 -0
  36. package/dist/types/src/cards/TaskCard.d.ts.map +1 -0
  37. package/dist/types/src/cards/index.d.ts +6 -0
  38. package/dist/types/src/cards/index.d.ts.map +1 -0
  39. package/dist/types/src/components/CardComponents.d.ts +16 -0
  40. package/dist/types/src/components/CardComponents.d.ts.map +1 -0
  41. package/dist/types/src/components/CardSubjectMenu.d.ts +8 -0
  42. package/dist/types/src/components/CardSubjectMenu.d.ts.map +1 -0
  43. package/dist/types/src/components/index.d.ts +2 -3
  44. package/dist/types/src/components/index.d.ts.map +1 -1
  45. package/dist/types/src/meta.d.ts +0 -1
  46. package/dist/types/src/meta.d.ts.map +1 -1
  47. package/dist/types/src/stories/Card.stories.d.ts +29 -0
  48. package/dist/types/src/stories/Card.stories.d.ts.map +1 -0
  49. package/dist/types/src/stories/testing.d.ts +18 -0
  50. package/dist/types/src/stories/testing.d.ts.map +1 -0
  51. package/dist/types/src/translations.d.ts +6 -0
  52. package/dist/types/src/translations.d.ts.map +1 -1
  53. package/dist/types/src/types.d.ts +9 -3
  54. package/dist/types/src/types.d.ts.map +1 -1
  55. package/dist/types/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +34 -29
  57. package/src/PreviewPlugin.tsx +23 -24
  58. package/src/capabilities/preview-popover.ts +9 -14
  59. package/src/capabilities/react-surface.tsx +82 -123
  60. package/src/cards/FormCard.tsx +57 -0
  61. package/src/cards/OrganizationCard.tsx +34 -0
  62. package/src/cards/PersonCard.tsx +63 -0
  63. package/src/cards/ProjectCard.tsx +24 -0
  64. package/src/cards/TaskCard.tsx +46 -0
  65. package/src/cards/index.ts +9 -0
  66. package/src/components/CardComponents.tsx +67 -0
  67. package/src/components/CardSubjectMenu.tsx +103 -0
  68. package/src/components/index.ts +2 -3
  69. package/src/meta.ts +6 -3
  70. package/src/stories/Card.stories.tsx +62 -0
  71. package/src/stories/testing.tsx +120 -0
  72. package/src/translations.ts +6 -0
  73. package/src/types.ts +19 -5
  74. package/dist/lib/browser/chunk-Z73ENKLE.mjs +0 -14
  75. package/dist/lib/browser/chunk-Z73ENKLE.mjs.map +0 -7
  76. package/dist/lib/browser/preview-popover-2UCEZUQW.mjs.map +0 -7
  77. package/dist/lib/browser/react-surface-CZMUEWPV.mjs +0 -303
  78. package/dist/lib/browser/react-surface-CZMUEWPV.mjs.map +0 -7
  79. package/dist/lib/node-esm/chunk-2ORSL4GJ.mjs +0 -16
  80. package/dist/lib/node-esm/chunk-2ORSL4GJ.mjs.map +0 -7
  81. package/dist/lib/node-esm/preview-popover-OSHWR2YE.mjs.map +0 -7
  82. package/dist/lib/node-esm/react-surface-IPZBISWS.mjs +0 -304
  83. package/dist/lib/node-esm/react-surface-IPZBISWS.mjs.map +0 -7
  84. package/dist/types/src/components/ContactCard.d.ts +0 -7
  85. package/dist/types/src/components/ContactCard.d.ts.map +0 -1
  86. package/dist/types/src/components/OrganizationCard.d.ts +0 -5
  87. package/dist/types/src/components/OrganizationCard.d.ts.map +0 -1
  88. package/dist/types/src/components/ProjectCard.d.ts +0 -5
  89. package/dist/types/src/components/ProjectCard.d.ts.map +0 -1
  90. package/dist/types/src/testing/ExtrinsicPreviews.stories.d.ts +0 -35
  91. package/dist/types/src/testing/ExtrinsicPreviews.stories.d.ts.map +0 -1
  92. package/dist/types/src/testing/IntrinsicPreviews.stories.d.ts +0 -35
  93. package/dist/types/src/testing/IntrinsicPreviews.stories.d.ts.map +0 -1
  94. package/dist/types/src/testing/PopoverPreviews.stories.d.ts +0 -35
  95. package/dist/types/src/testing/PopoverPreviews.stories.d.ts.map +0 -1
  96. package/dist/types/src/testing/util.d.ts +0 -80
  97. package/dist/types/src/testing/util.d.ts.map +0 -1
  98. package/src/components/ContactCard.tsx +0 -70
  99. package/src/components/OrganizationCard.tsx +0 -41
  100. package/src/components/ProjectCard.tsx +0 -20
  101. package/src/testing/ExtrinsicPreviews.stories.tsx +0 -55
  102. package/src/testing/IntrinsicPreviews.stories.tsx +0 -55
  103. package/src/testing/PopoverPreviews.stories.tsx +0 -55
  104. 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.3c1ae3b",
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-graph": "0.8.4-main.3c1ae3b",
30
+ "@dxos/app-framework": "0.8.4-main.3c1ae3b",
31
+ "@dxos/async": "0.8.4-main.3c1ae3b",
32
+ "@dxos/lit-ui": "0.8.4-main.3c1ae3b",
33
+ "@dxos/echo": "0.8.4-main.3c1ae3b",
34
+ "@dxos/client": "0.8.4-main.3c1ae3b",
35
+ "@dxos/keys": "0.8.4-main.3c1ae3b",
36
+ "@dxos/log": "0.8.4-main.3c1ae3b",
37
+ "@dxos/plugin-attention": "0.8.4-main.3c1ae3b",
38
+ "@dxos/plugin-client": "0.8.4-main.3c1ae3b",
39
+ "@dxos/plugin-space": "0.8.4-main.3c1ae3b",
40
+ "@dxos/react-ui": "0.8.4-main.3c1ae3b",
41
+ "@dxos/react-ui-editor": "0.8.4-main.3c1ae3b",
42
+ "@dxos/react-client": "0.8.4-main.3c1ae3b",
43
+ "@dxos/plugin-deck": "0.8.4-main.3c1ae3b",
44
+ "@dxos/react-ui-menu": "0.8.4-main.3c1ae3b",
45
+ "@dxos/react-ui-stack": "0.8.4-main.3c1ae3b",
46
+ "@dxos/react-ui-form": "0.8.4-main.3c1ae3b",
47
+ "@dxos/react-ui-table": "0.8.4-main.3c1ae3b",
48
+ "@dxos/react-ui-theme": "0.8.4-main.3c1ae3b",
49
+ "@dxos/schema": "0.8.4-main.3c1ae3b",
50
+ "@dxos/types": "0.8.4-main.3c1ae3b",
51
+ "@dxos/util": "0.8.4-main.3c1ae3b"
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.3c1ae3b",
59
+ "@dxos/storybook-utils": "0.8.4-main.3c1ae3b"
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.makeRef(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 handleAnchorActivate = 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
+ handleAnchorActivate,
67
62
  customEventOptions,
68
63
  );
69
64
  } else {
@@ -2,161 +2,120 @@
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';
23
- import { useTranslation } from '@dxos/react-ui';
24
- import { Form } from '@dxos/react-ui-form';
25
- import { Card } from '@dxos/react-ui-stack';
26
- import { TableView } from '@dxos/react-ui-table/types';
27
- import { descriptionMessage, mx } from '@dxos/react-ui-theme';
28
- import { DataType } from '@dxos/schema';
7
+ import { Capabilities, LayoutAction, contributes, createIntent, createSurface } from '@dxos/app-framework';
8
+ import { Surface, SurfaceCardRole, useIntentDispatcher } from '@dxos/app-framework/react';
9
+ import { getSpace } from '@dxos/client/echo';
10
+ import { Obj } from '@dxos/echo';
11
+ import { useActiveSpace } from '@dxos/plugin-space';
12
+ import { type ProjectionModel } from '@dxos/schema';
13
+ import { Organization, Person, Project, Task } from '@dxos/types';
29
14
 
30
- import { ContactCard, OrganizationCard, ProjectCard } from '../components';
31
- import { PREVIEW_PLUGIN } from '../meta';
15
+ import { FormCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
16
+ import { meta } from '../meta';
17
+ import { type CardPreviewProps } from '../types';
32
18
 
33
19
  export default () =>
34
20
  contributes(Capabilities.ReactSurface, [
35
21
  //
36
22
  // Specific schema types.
23
+ // TODO(burdon): Create helpers and factor out.
37
24
  //
38
- createSurface({
39
- id: `${PREVIEW_PLUGIN}/schema-popover--contact`,
40
- role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
41
- filter: (data): data is { subject: DataType.Person } => Obj.instanceOf(DataType.Person, data.subject),
25
+
26
+ // TODO(burdon): Infer Role type.
27
+ createSurface<{ subject: Person.Person }>({
28
+ id: `${meta.id}/schema-popover--contact`,
29
+ role: SurfaceCardRole.literals as any,
30
+ filter: (data): data is { subject: Person.Person } => Obj.instanceOf(Person.Person, data.subject),
42
31
  component: ({ data, role }) => {
43
- const { dispatch } = useIntentDispatcher();
32
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
44
33
  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),
34
+ const activeSpace = useActiveSpace(); // TODO(burdon): Disambiguate with space?
35
+ const handleSelect = useCallback<NonNullable<CardPreviewProps['onSelect']>>(
36
+ (object) =>
37
+ dispatch(
38
+ createIntent(LayoutAction.Open, {
39
+ part: 'main',
40
+ subject: [Obj.getDXN(object).toString()],
41
+ options: {
42
+ workspace: space?.id,
43
+ },
44
+ }),
45
+ ),
46
+ [dispatch],
59
47
  );
60
48
 
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],
49
+ return (
50
+ <PersonCard
51
+ role={role as SurfaceCardRole}
52
+ subject={data.subject}
53
+ activeSpace={activeSpace}
54
+ onSelect={handleSelect}
55
+ >
56
+ {role === 'card--popover' && <Surface role='related' data={data} />}
57
+ </PersonCard>
99
58
  );
100
-
59
+ },
60
+ }),
61
+ createSurface({
62
+ id: `${meta.id}/schema-popover--organization`,
63
+ role: SurfaceCardRole.literals as any,
64
+ filter: (data): data is { subject: Organization.Organization } =>
65
+ Obj.instanceOf(Organization.Organization, data.subject),
66
+ component: ({ data, role }) => {
67
+ const activeSpace = useActiveSpace();
101
68
  return (
102
- <ContactCard role={role} subject={data.subject} onOrgClick={handleOrgClick}>
69
+ <OrganizationCard role={role as SurfaceCardRole} subject={data.subject} activeSpace={activeSpace}>
103
70
  {role === 'card--popover' && <Surface role='related' data={data} />}
104
- </ContactCard>
71
+ </OrganizationCard>
105
72
  );
106
73
  },
107
74
  }),
108
75
  createSurface({
109
- id: `${PREVIEW_PLUGIN}/schema-popover--organization`,
110
- 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
- ),
76
+ id: `${meta.id}/schema-popover--project`,
77
+ role: SurfaceCardRole.literals as any,
78
+ filter: (data): data is { subject: Project.Project } => Obj.instanceOf(Project.Project, data.subject),
79
+ component: ({ data, role }) => {
80
+ const activeSpace = useActiveSpace();
81
+ return <ProjectCard subject={data.subject} role={role as SurfaceCardRole} activeSpace={activeSpace} />;
82
+ },
117
83
  }),
118
84
  createSurface({
119
- id: `${PREVIEW_PLUGIN}/schema-popover--project`,
120
- 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} />,
85
+ id: `${meta.id}/schema-popover--task`,
86
+ role: SurfaceCardRole.literals as any,
87
+ filter: (data): data is { subject: Task.Task } => Obj.instanceOf(Task.Task, data.subject),
88
+ component: ({ data, role }) => {
89
+ return <TaskCard subject={data.subject} role={role as SurfaceCardRole} />;
90
+ },
123
91
  }),
124
92
 
125
93
  //
126
94
  // Fallback for any object.
127
95
  //
96
+
128
97
  createSurface({
129
- id: `${PREVIEW_PLUGIN}/fallback-popover`,
130
- role: ['card--popover', 'card--intrinsic', 'card--transclusion', 'card--extrinsic', 'card'],
98
+ id: `${meta.id}/fallback-popover`,
99
+ role: SurfaceCardRole.literals as any,
131
100
  position: 'fallback',
132
- filter: (data): data is { subject: Obj.Any } => Obj.isObject(data.subject),
101
+ filter: (data): data is { subject: Obj.Any; projection?: ProjectionModel } => Obj.isObject(data.subject),
133
102
  component: ({ data, role }) => {
134
- const schema = getSchema(data.subject);
135
- const { t } = useTranslation(PREVIEW_PLUGIN);
136
- if (!schema) {
137
- // TODO(burdon): Use Alert.
138
- return <p className={mx(descriptionMessage)}>{t('unable to create preview message')}</p>;
139
- }
140
-
141
- const handleSave = useCallback((values: any, { changed }: { changed: Record<string, boolean> }) => {
142
- const changedPaths = Object.keys(changed).filter((path) => changed[path]);
143
- for (const path of changedPaths) {
144
- const value = values[path];
145
- setValue(data.subject, path as JsonPath, value);
146
- }
147
- }, []);
103
+ return <FormCard subject={data.subject} projection={data.projection} role={role as SurfaceCardRole} />;
104
+ },
105
+ }),
148
106
 
107
+ createSurface({
108
+ id: `${meta.id}/section`,
109
+ role: ['section'],
110
+ position: 'fallback',
111
+ filter: (data): data is { subject: Obj.Any } => Obj.isObject(data.subject),
112
+ component: ({ data }) => {
149
113
  return (
150
- <Card.SurfaceRoot role={role}>
151
- <Form
152
- schema={schema}
153
- values={data.subject}
154
- readonly={role === 'card--popover'}
155
- onSave={handleSave}
156
- autoSave
157
- {...(role === 'card--intrinsic' && { outerSpacing: 'blockStart-0' })}
158
- />
159
- </Card.SurfaceRoot>
114
+ <div role='none' className='flex is-full justify-center'>
115
+ <div role='none' className='pbs-2 pbe-2 card-min-width card-max-width'>
116
+ <Surface role='card' data={data} limit={1} />
117
+ </div>
118
+ </div>
160
119
  );
161
120
  },
162
121
  }),
@@ -0,0 +1,57 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+ import { useCallback } from 'react';
7
+
8
+ import { Obj } from '@dxos/echo';
9
+ import { type JsonPath, setValue } from '@dxos/echo/internal';
10
+ import { useTranslation } from '@dxos/react-ui';
11
+ import { Form, omitId } from '@dxos/react-ui-form';
12
+ import { Card } from '@dxos/react-ui-stack';
13
+ import { descriptionMessage, mx } from '@dxos/react-ui-theme';
14
+ import { type ProjectionModel } from '@dxos/schema';
15
+
16
+ import { meta } from '../meta';
17
+ import { type CardPreviewProps } from '../types';
18
+
19
+ export const FormCard = ({
20
+ subject,
21
+ projection,
22
+ role,
23
+ }: CardPreviewProps<Obj.Any> & { projection?: ProjectionModel }) => {
24
+ const schema = Obj.getSchema(subject);
25
+ const { t } = useTranslation(meta.id);
26
+ if (!schema) {
27
+ // TODO(burdon): Use Alert.
28
+ return <p className={mx(descriptionMessage)}>{t('unable to create preview message')}</p>;
29
+ }
30
+
31
+ const handleSave = useCallback((values: any, { changed }: { changed: Record<string, boolean> }) => {
32
+ const paths = Object.keys(changed).filter((path) => changed[path]);
33
+ for (const path of paths) {
34
+ const value = values[path];
35
+ setValue(subject, path as JsonPath, value);
36
+ }
37
+ }, []);
38
+
39
+ return (
40
+ <Card.SurfaceRoot id={subject.id} role={role}>
41
+ <Form.Root
42
+ schema={omitId(schema)}
43
+ projection={projection}
44
+ values={subject}
45
+ layout={role === 'card--popover' ? 'static' : undefined}
46
+ autoSave
47
+ onSave={handleSave}
48
+ >
49
+ <Form.Viewport>
50
+ <Form.Content>
51
+ <Form.FieldSet />
52
+ </Form.Content>
53
+ </Form.Viewport>
54
+ </Form.Root>
55
+ </Card.SurfaceRoot>
56
+ );
57
+ };
@@ -0,0 +1,34 @@
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 CardPreviewProps } from '../types';
12
+
13
+ export const OrganizationCard = ({
14
+ children,
15
+ subject,
16
+ role,
17
+ activeSpace,
18
+ }: CardPreviewProps<Organization.Organization>) => {
19
+ const { name, image, description, website } = subject;
20
+
21
+ return (
22
+ <Card.SurfaceRoot id={subject.id} role={role}>
23
+ <Card.Poster
24
+ alt={name ?? ''}
25
+ {...(image ? { image } : { icon: 'ph--building-office--regular' })}
26
+ classNames={!image && 'opacity-50'}
27
+ />
28
+ <CardHeader label={name} subject={subject} activeSpace={activeSpace} />
29
+ {description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
30
+ {website && <CardLink label={website} href={website} />}
31
+ {children}
32
+ </Card.SurfaceRoot>
33
+ );
34
+ };
@@ -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 CardPreviewProps } from '../types';
14
+
15
+ export const PersonCard = ({ children, role, subject, activeSpace, onSelect }: CardPreviewProps<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 CardPreviewProps } from '../types';
13
+
14
+ export const ProjectCard = ({ subject, role, activeSpace }: CardPreviewProps<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
+ };