@dxos/plugin-masonry 0.8.4-main.ae835ea → 0.8.4-main.bcb3aa67d6

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 (72) hide show
  1. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  2. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +77 -67
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +66 -0
  7. package/dist/lib/browser/types/index.mjs.map +7 -0
  8. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  9. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +77 -67
  11. package/dist/lib/node-esm/index.mjs.map +4 -4
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/types/index.mjs +67 -0
  14. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  15. package/dist/types/src/MasonryPlugin.d.ts +2 -1
  16. package/dist/types/src/MasonryPlugin.d.ts.map +1 -1
  17. package/dist/types/src/capabilities/index.d.ts +2 -2
  18. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  19. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  20. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  21. package/dist/types/src/components/index.d.ts +1 -0
  22. package/dist/types/src/components/index.d.ts.map +1 -0
  23. package/dist/types/src/containers/MasonryContainer/MasonryContainer.d.ts +12 -0
  24. package/dist/types/src/containers/MasonryContainer/MasonryContainer.d.ts.map +1 -0
  25. package/dist/types/src/containers/MasonryContainer/MasonryContainer.stories.d.ts.map +1 -0
  26. package/dist/types/src/containers/MasonryContainer/index.d.ts +3 -0
  27. package/dist/types/src/containers/MasonryContainer/index.d.ts.map +1 -0
  28. package/dist/types/src/containers/index.d.ts +3 -0
  29. package/dist/types/src/containers/index.d.ts.map +1 -0
  30. package/dist/types/src/meta.d.ts +2 -2
  31. package/dist/types/src/meta.d.ts.map +1 -1
  32. package/dist/types/src/translations.d.ts +22 -11
  33. package/dist/types/src/translations.d.ts.map +1 -1
  34. package/dist/types/src/types/Masonry.d.ts +17 -22
  35. package/dist/types/src/types/Masonry.d.ts.map +1 -1
  36. package/dist/types/src/types/MasonryAction.d.ts +0 -17
  37. package/dist/types/src/types/MasonryAction.d.ts.map +1 -1
  38. package/dist/types/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +61 -33
  40. package/src/MasonryPlugin.tsx +43 -54
  41. package/src/capabilities/index.ts +2 -3
  42. package/src/capabilities/react-surface.tsx +23 -16
  43. package/src/components/index.ts +3 -0
  44. package/src/containers/MasonryContainer/MasonryContainer.stories.tsx +78 -0
  45. package/src/containers/MasonryContainer/MasonryContainer.tsx +124 -0
  46. package/src/containers/MasonryContainer/index.ts +7 -0
  47. package/src/containers/index.ts +7 -0
  48. package/src/meta.ts +3 -3
  49. package/src/translations.ts +14 -9
  50. package/src/types/Masonry.ts +26 -28
  51. package/src/types/MasonryAction.ts +8 -17
  52. package/dist/lib/browser/chunk-RVOTJON5.mjs +0 -92
  53. package/dist/lib/browser/chunk-RVOTJON5.mjs.map +0 -7
  54. package/dist/lib/browser/intent-resolver-WM3U563P.mjs +0 -31
  55. package/dist/lib/browser/intent-resolver-WM3U563P.mjs.map +0 -7
  56. package/dist/lib/browser/react-surface-Z7W24EZP.mjs +0 -112
  57. package/dist/lib/browser/react-surface-Z7W24EZP.mjs.map +0 -7
  58. package/dist/lib/node-esm/chunk-KTVJZX5B.mjs +0 -93
  59. package/dist/lib/node-esm/chunk-KTVJZX5B.mjs.map +0 -7
  60. package/dist/lib/node-esm/intent-resolver-YKIFA4JI.mjs +0 -32
  61. package/dist/lib/node-esm/intent-resolver-YKIFA4JI.mjs.map +0 -7
  62. package/dist/lib/node-esm/react-surface-BSDKYPZH.mjs +0 -113
  63. package/dist/lib/node-esm/react-surface-BSDKYPZH.mjs.map +0 -7
  64. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  65. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  66. package/dist/types/src/components/MasonryContainer.d.ts +0 -7
  67. package/dist/types/src/components/MasonryContainer.d.ts.map +0 -1
  68. package/dist/types/src/components/MasonryContainer.stories.d.ts.map +0 -1
  69. package/src/capabilities/intent-resolver.ts +0 -25
  70. package/src/components/MasonryContainer.stories.tsx +0 -83
  71. package/src/components/MasonryContainer.tsx +0 -69
  72. /package/dist/types/src/{components → containers/MasonryContainer}/MasonryContainer.stories.d.ts +0 -0
package/package.json CHANGED
@@ -1,63 +1,91 @@
1
1
  {
2
2
  "name": "@dxos/plugin-masonry",
3
- "version": "0.8.4-main.ae835ea",
3
+ "version": "0.8.4-main.bcb3aa67d6",
4
4
  "description": "Masonry DXOS Surface plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
13
  "sideEffects": true,
10
14
  "type": "module",
15
+ "imports": {
16
+ "#capabilities": "./src/capabilities/index.ts",
17
+ "#components": "./src/components/index.ts",
18
+ "#containers": "./src/containers/index.ts",
19
+ "#meta": "./src/meta.ts",
20
+ "#types": "./src/types/index.ts"
21
+ },
11
22
  "exports": {
12
23
  ".": {
13
24
  "source": "./src/index.ts",
14
25
  "types": "./dist/types/src/index.d.ts",
15
26
  "browser": "./dist/lib/browser/index.mjs",
16
27
  "node": "./dist/lib/node-esm/index.mjs"
28
+ },
29
+ "./types": {
30
+ "source": "./src/types/index.ts",
31
+ "types": "./dist/types/src/types/index.d.ts",
32
+ "browser": "./dist/lib/browser/types/index.mjs",
33
+ "node": "./dist/lib/node-esm/types/index.mjs"
17
34
  }
18
35
  },
19
36
  "types": "dist/types/src/index.d.ts",
37
+ "typesVersions": {
38
+ "*": {
39
+ "types": [
40
+ "dist/types/src/types/index.d.ts"
41
+ ]
42
+ }
43
+ },
20
44
  "files": [
21
45
  "dist",
22
46
  "src"
23
47
  ],
24
48
  "dependencies": {
25
- "@preact-signals/safe-react": "^0.9.0",
26
- "@preact/signals-core": "^1.12.1",
27
- "@preact/signals-react": "^3.3.0",
28
- "@dxos/echo": "0.8.4-main.ae835ea",
29
- "@dxos/plugin-client": "0.8.4-main.ae835ea",
30
- "@dxos/app-framework": "0.8.4-main.ae835ea",
31
- "@dxos/effect": "0.8.4-main.ae835ea",
32
- "@dxos/plugin-search": "0.8.4-main.ae835ea",
33
- "@dxos/react-ui": "0.8.4-main.ae835ea",
34
- "@dxos/plugin-space": "0.8.4-main.ae835ea",
35
- "@dxos/react-ui-form": "0.8.4-main.ae835ea",
36
- "@dxos/react-ui-stack": "0.8.4-main.ae835ea",
37
- "@dxos/schema": "0.8.4-main.ae835ea",
38
- "@dxos/react-ui-masonry": "0.8.4-main.ae835ea",
39
- "@dxos/util": "0.8.4-main.ae835ea",
40
- "@dxos/react-client": "0.8.4-main.ae835ea"
49
+ "@dxos/app-toolkit": "0.8.4-main.bcb3aa67d6",
50
+ "@dxos/echo": "0.8.4-main.bcb3aa67d6",
51
+ "@dxos/effect": "0.8.4-main.bcb3aa67d6",
52
+ "@dxos/app-framework": "0.8.4-main.bcb3aa67d6",
53
+ "@dxos/operation": "0.8.4-main.bcb3aa67d6",
54
+ "@dxos/plugin-client": "0.8.4-main.bcb3aa67d6",
55
+ "@dxos/plugin-search": "0.8.4-main.bcb3aa67d6",
56
+ "@dxos/react-client": "0.8.4-main.bcb3aa67d6",
57
+ "@dxos/plugin-space": "0.8.4-main.bcb3aa67d6",
58
+ "@dxos/react-ui": "0.8.4-main.bcb3aa67d6",
59
+ "@dxos/react-ui-form": "0.8.4-main.bcb3aa67d6",
60
+ "@dxos/react-ui-masonry": "0.8.4-main.bcb3aa67d6",
61
+ "@dxos/react-ui-search": "0.8.4-main.bcb3aa67d6",
62
+ "@dxos/react-ui-mosaic": "0.8.4-main.bcb3aa67d6",
63
+ "@dxos/react-ui-stack": "0.8.4-main.bcb3aa67d6",
64
+ "@dxos/schema": "0.8.4-main.bcb3aa67d6",
65
+ "@dxos/types": "0.8.4-main.bcb3aa67d6",
66
+ "@dxos/react-ui-menu": "0.8.4-main.bcb3aa67d6",
67
+ "@dxos/util": "0.8.4-main.bcb3aa67d6"
41
68
  },
42
69
  "devDependencies": {
43
- "@types/react": "~19.2.2",
44
- "@types/react-dom": "~19.2.2",
45
- "effect": "3.18.3",
46
- "react": "~19.2.0",
47
- "react-dom": "~19.2.0",
48
- "vite": "7.1.9",
49
- "@dxos/plugin-preview": "0.8.4-main.ae835ea",
50
- "@dxos/random": "0.8.4-main.ae835ea",
51
- "@dxos/plugin-theme": "0.8.4-main.ae835ea",
52
- "@dxos/storybook-utils": "0.8.4-main.ae835ea",
53
- "@dxos/react-ui-theme": "0.8.4-main.ae835ea"
70
+ "@types/react": "~19.2.7",
71
+ "@types/react-dom": "~19.2.3",
72
+ "effect": "3.20.0",
73
+ "react": "~19.2.3",
74
+ "react-dom": "~19.2.3",
75
+ "vite": "^7.1.11",
76
+ "@dxos/plugin-preview": "0.8.4-main.bcb3aa67d6",
77
+ "@dxos/plugin-testing": "0.8.4-main.bcb3aa67d6",
78
+ "@dxos/plugin-theme": "0.8.4-main.bcb3aa67d6",
79
+ "@dxos/storybook-utils": "0.8.4-main.bcb3aa67d6",
80
+ "@dxos/ui-theme": "0.8.4-main.bcb3aa67d6",
81
+ "@dxos/random": "0.8.4-main.bcb3aa67d6"
54
82
  },
55
83
  "peerDependencies": {
56
- "effect": "^3.13.3",
57
- "react": "^19.0.0",
58
- "react-dom": "^19.0.0",
59
- "@dxos/react-ui": "0.8.4-main.ae835ea",
60
- "@dxos/react-ui-theme": "0.8.4-main.ae835ea"
84
+ "effect": "3.20.0",
85
+ "react": "~19.2.3",
86
+ "react-dom": "~19.2.3",
87
+ "@dxos/react-ui": "0.8.4-main.bcb3aa67d6",
88
+ "@dxos/ui-theme": "0.8.4-main.bcb3aa67d6"
61
89
  },
62
90
  "publishConfig": {
63
91
  "access": "public"
@@ -2,61 +2,50 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, Events, contributes, createIntent, defineModule, definePlugin } from '@dxos/app-framework';
6
- import { ClientEvents } from '@dxos/plugin-client';
7
- import { SpaceCapabilities } from '@dxos/plugin-space';
8
- import { defineObjectForm } from '@dxos/plugin-space/types';
5
+ import * as Effect from 'effect/Effect';
9
6
 
10
- import { IntentResolver, ReactSurface } from './capabilities';
11
- import { meta } from './meta';
7
+ import * as Option from 'effect/Option';
8
+
9
+ import { Plugin } from '@dxos/app-framework';
10
+ import { AppPlugin } from '@dxos/app-toolkit';
11
+ import { Annotation, Type } from '@dxos/echo';
12
+ import { Operation } from '@dxos/operation';
13
+ import { type CreateObject } from '@dxos/plugin-space/types';
14
+ import { SpaceOperation } from '@dxos/plugin-space/operations';
15
+ import { ViewModel } from '@dxos/schema';
16
+
17
+ import { meta } from '#meta';
12
18
  import { translations } from './translations';
13
- import { Masonry, MasonryAction } from './types';
19
+ import { Masonry, MasonryAction } from '#types';
14
20
 
15
- export const MasonryPlugin = definePlugin(meta, () => [
16
- defineModule({
17
- id: `${meta.id}/module/translations`,
18
- activatesOn: Events.SetupTranslations,
19
- activate: () => contributes(Capabilities.Translations, translations),
20
- }),
21
- defineModule({
22
- id: `${meta.id}/module/react-surface`,
23
- activatesOn: Events.SetupReactSurface,
24
- activate: ReactSurface,
25
- }),
26
- defineModule({
27
- id: `${meta.id}/module/metadata`,
28
- activatesOn: Events.SetupMetadata,
29
- activate: () =>
30
- contributes(Capabilities.Metadata, {
31
- id: Masonry.Masonry.typename,
32
- metadata: {
33
- icon: 'ph--wall--regular',
34
- iconHue: 'green',
35
- },
36
- }),
37
- }),
38
- defineModule({
39
- id: `${meta.id}/module/object-form`,
40
- activatesOn: ClientEvents.SetupSchema,
41
- activate: () =>
42
- contributes(
43
- SpaceCapabilities.ObjectForm,
44
- defineObjectForm({
45
- objectSchema: Masonry.Masonry,
46
- formSchema: MasonryAction.MasonryProps,
47
- hidden: true,
48
- getIntent: (props, options) => createIntent(MasonryAction.CreateMasonry, { ...props, space: options.space }),
49
- }),
50
- ),
51
- }),
52
- defineModule({
53
- id: `${meta.id}/module/react-surface`,
54
- activatesOn: Events.SetupReactSurface,
55
- activate: ReactSurface,
56
- }),
57
- defineModule({
58
- id: `${meta.id}/module/intent-resolver`,
59
- activatesOn: Events.SetupIntentResolver,
60
- activate: IntentResolver,
21
+ import { ReactSurface } from '#capabilities';
22
+
23
+ export const MasonryPlugin = Plugin.define(meta).pipe(
24
+ AppPlugin.addMetadataModule({
25
+ metadata: {
26
+ id: Type.getTypename(Masonry.Masonry),
27
+ metadata: {
28
+ icon: Annotation.IconAnnotation.get(Masonry.Masonry).pipe(Option.getOrThrow).icon,
29
+ iconHue: Annotation.IconAnnotation.get(Masonry.Masonry).pipe(Option.getOrThrow).hue ?? 'white',
30
+ inputSchema: MasonryAction.MasonryProps,
31
+ createObject: ((props, options) =>
32
+ Effect.gen(function* () {
33
+ const object = yield* Effect.promise(async () => {
34
+ const { view } = await ViewModel.makeFromDatabase({ db: options.db, typename: props.typename });
35
+ return Masonry.make({ name: props.name, view });
36
+ });
37
+ return yield* Operation.invoke(SpaceOperation.AddObject, {
38
+ object,
39
+ target: options.target,
40
+ hidden: true,
41
+ targetNodeId: options.targetNodeId,
42
+ });
43
+ })) satisfies CreateObject,
44
+ },
45
+ },
61
46
  }),
62
- ]);
47
+ AppPlugin.addSchemaModule({ schema: [Masonry.Masonry] }),
48
+ AppPlugin.addSurfaceModule({ activate: ReactSurface }),
49
+ AppPlugin.addTranslationsModule({ translations }),
50
+ Plugin.make,
51
+ );
@@ -2,7 +2,6 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { lazy } from '@dxos/app-framework';
5
+ import { Capability } from '@dxos/app-framework';
6
6
 
7
- export const ReactSurface = lazy(() => import('./react-surface'));
8
- export const IntentResolver = lazy(() => import('./intent-resolver'));
7
+ export const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));
@@ -2,24 +2,31 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import * as Effect from 'effect/Effect';
5
6
  import React from 'react';
6
7
 
7
- import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
8
+ import { Capabilities, Capability } from '@dxos/app-framework';
9
+ import { Surface } from '@dxos/app-framework/ui';
8
10
  import { Obj } from '@dxos/echo';
9
- import { DataType } from '@dxos/schema';
11
+ import { View } from '@dxos/echo';
10
12
 
11
- import { MasonryContainer } from '../components/MasonryContainer';
12
- import { meta } from '../meta';
13
- import { Masonry } from '../types';
13
+ import { MasonryContainer } from '#containers';
14
+ import { meta } from '#meta';
15
+ import { Masonry } from '#types';
14
16
 
15
- export default () =>
16
- contributes(Capabilities.ReactSurface, [
17
- createSurface({
18
- id: meta.id,
19
- role: ['article', 'section'],
20
- filter: (data): data is { subject: DataType.View } =>
21
- Obj.instanceOf(DataType.View, data.subject) &&
22
- Obj.instanceOf(Masonry.Masonry, data.subject.presentation?.target),
23
- component: ({ data, role }) => <MasonryContainer view={data.subject} role={role} />,
24
- }),
25
- ]);
17
+ export default Capability.makeModule(() =>
18
+ Effect.succeed(
19
+ Capability.contributes(Capabilities.ReactSurface, [
20
+ Surface.create({
21
+ id: meta.id,
22
+ role: ['article', 'section'],
23
+ filter: (data): data is { subject: Masonry.Masonry | View.View } =>
24
+ Obj.instanceOf(Masonry.Masonry, data.subject) || Obj.instanceOf(View.View, data.subject),
25
+ component: ({ data, role }) => {
26
+ const view = Obj.instanceOf(View.View, data.subject) ? data.subject : data.subject.view;
27
+ return <MasonryContainer view={view} role={role} />;
28
+ },
29
+ }),
30
+ ]),
31
+ ),
32
+ );
@@ -0,0 +1,3 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
@@ -0,0 +1,78 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
7
+ import React from 'react';
8
+
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { View } from '@dxos/echo';
11
+ import { ClientPlugin } from '@dxos/plugin-client';
12
+ import { initializeIdentity } from '@dxos/plugin-client/testing';
13
+ import { PreviewPlugin } from '@dxos/plugin-preview';
14
+ import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
15
+ import { faker } from '@dxos/random';
16
+ import { Filter, useObject, useQuery, useSpaces } from '@dxos/react-client/echo';
17
+ import { ViewModel } from '@dxos/schema';
18
+ import { createObjectFactory } from '@dxos/schema/testing';
19
+ import { Organization } from '@dxos/types';
20
+
21
+ import { Masonry } from '#types';
22
+
23
+ import { MasonryContainer } from './MasonryContainer';
24
+
25
+ faker.seed(0);
26
+
27
+ const StorybookMasonry = () => {
28
+ const spaces = useSpaces();
29
+ const space = spaces[spaces.length - 1];
30
+ const masonries = useQuery(space?.db, Filter.type(Masonry.Masonry));
31
+ const [masonry] = useObject(masonries.at(0));
32
+
33
+ return masonry ? <MasonryContainer view={masonry.view} role='story' /> : null;
34
+ };
35
+
36
+ const meta = {
37
+ title: 'plugins/plugin-masonry/containers/Masonry',
38
+ component: StorybookMasonry,
39
+ render: () => <StorybookMasonry />,
40
+ decorators: [
41
+ withPluginManager({
42
+ plugins: [
43
+ ...corePlugins(),
44
+ StorybookPlugin({}),
45
+ ClientPlugin({
46
+ types: [Organization.Organization, View.View, Masonry.Masonry],
47
+ onClientInitialized: ({ client }) =>
48
+ Effect.gen(function* () {
49
+ yield* initializeIdentity(client);
50
+ const space = yield* Effect.promise(() => client.spaces.create());
51
+ yield* Effect.promise(() => space.waitUntilReady());
52
+ const { view } = yield* Effect.promise(() =>
53
+ ViewModel.makeFromDatabase({
54
+ db: space.db,
55
+ typename: Organization.Organization.typename,
56
+ }),
57
+ );
58
+ const masonry = Masonry.make({ view });
59
+ space.db.add(masonry);
60
+ const factory = createObjectFactory(space.db, faker as any);
61
+ yield* Effect.promise(() => factory([{ type: Organization.Organization, count: 64 }]));
62
+ }),
63
+ }),
64
+
65
+ PreviewPlugin(),
66
+ ],
67
+ }),
68
+ ],
69
+ parameters: {
70
+ layout: 'fullscreen',
71
+ },
72
+ } satisfies Meta;
73
+
74
+ export default meta;
75
+
76
+ type Story = StoryObj<typeof meta>;
77
+
78
+ export const Default: Story = {};
@@ -0,0 +1,124 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Function from 'effect/Function';
6
+ import * as Option from 'effect/Option';
7
+ import type * as Schema from 'effect/Schema';
8
+ import React, { useEffect, useMemo, useState } from 'react';
9
+
10
+ import { Surface, useCapabilities } from '@dxos/app-framework/ui';
11
+ import { AppCapabilities } from '@dxos/app-toolkit';
12
+ import { useObjectMenuItems } from '@dxos/app-toolkit/ui';
13
+ import { Annotation, Filter, Obj, Query, type Ref, Type } from '@dxos/echo';
14
+ import { type View } from '@dxos/echo';
15
+ import { useObject, useQuery } from '@dxos/react-client/echo';
16
+ import { Card, Panel, Toolbar } from '@dxos/react-ui';
17
+ import { Masonry as MasonryComponent } from '@dxos/react-ui-masonry';
18
+ import { Menu } from '@dxos/react-ui-menu';
19
+ import { SearchList, useSearchListResults } from '@dxos/react-ui-search';
20
+ import { getTagFromQuery, getTypenameFromQuery } from '@dxos/schema';
21
+ import { isNonNullable } from '@dxos/util';
22
+
23
+ export type MasonryContainerProps = {
24
+ view: View.View;
25
+ role?: string;
26
+ };
27
+
28
+ export const MasonryContainer = ({
29
+ view: viewOrRef,
30
+ role: _role,
31
+ }: {
32
+ view: View.View | Ref.Ref<View.View>;
33
+ role?: string;
34
+ }) => {
35
+ const [view] = useObject(viewOrRef);
36
+ const schemas = useCapabilities(AppCapabilities.Schema);
37
+ const db = view && Obj.getDatabase(view);
38
+ const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
39
+ const tag = view?.query ? getTagFromQuery(view.query.ast) : undefined;
40
+
41
+ const [cardSchema, setCardSchema] = useState<Schema.Schema.AnyNoContext>();
42
+
43
+ useEffect(() => {
44
+ const staticSchema = schemas.flat().find((schema) => Type.getTypename(schema) === typename);
45
+ if (staticSchema) {
46
+ setCardSchema(() => staticSchema);
47
+ }
48
+ if (!staticSchema && typename && db) {
49
+ const query = db.schemaRegistry.query({ typename });
50
+ const unsubscribe = query.subscribe(
51
+ () => {
52
+ const [schema] = query.results;
53
+ if (schema) {
54
+ setCardSchema(schema);
55
+ }
56
+ },
57
+ { fire: true },
58
+ );
59
+ return unsubscribe;
60
+ }
61
+ }, [schemas, typename, db]);
62
+
63
+ const query = useMemo(() => {
64
+ const baseFilter = cardSchema ? Filter.type(cardSchema) : Filter.nothing();
65
+ return tag ? Query.select(baseFilter).select(Filter.tag(tag)) : Query.select(baseFilter);
66
+ }, [cardSchema, tag]);
67
+ const objects = useQuery(db, query);
68
+
69
+ const sortedObjects = useMemo(
70
+ () =>
71
+ objects.filter(isNonNullable).toSorted((a, b) => (Obj.getLabel(a) ?? '').localeCompare(Obj.getLabel(b) ?? '')),
72
+ [objects],
73
+ );
74
+
75
+ const { results, handleSearch } = useSearchListResults({
76
+ items: sortedObjects,
77
+ extract: (obj) => Obj.getLabel(obj) ?? '',
78
+ });
79
+
80
+ return (
81
+ <MasonryComponent.Root Tile={Item}>
82
+ <SearchList.Root onSearch={handleSearch}>
83
+ <Panel.Root>
84
+ <Panel.Toolbar asChild>
85
+ <Toolbar.Root>
86
+ <SearchList.Input placeholder='Search...' />
87
+ </Toolbar.Root>
88
+ </Panel.Toolbar>
89
+ <Panel.Content>
90
+ <MasonryComponent.Content items={results} getId={(data: any) => data?.id} />
91
+ </Panel.Content>
92
+ </Panel.Root>
93
+ </SearchList.Root>
94
+ </MasonryComponent.Root>
95
+ );
96
+ };
97
+
98
+ const Item = ({ data }: { data: any }) => {
99
+ const objectMenuItems = useObjectMenuItems(data);
100
+ const icon = Function.pipe(
101
+ Obj.getSchema(data),
102
+ Option.fromNullable,
103
+ Option.flatMap(Annotation.IconAnnotation.get),
104
+ Option.map(({ icon }) => icon),
105
+ Option.getOrElse(() => 'ph--placeholder--regular'),
106
+ );
107
+
108
+ return (
109
+ <Menu.Root>
110
+ <Card.Root>
111
+ <Card.Toolbar>
112
+ <Card.Icon icon={icon} />
113
+ <Card.Title>{Obj.getLabel(data)}</Card.Title>
114
+ {/* TODO(wittjosiah): Reconcile with Card.Menu. */}
115
+ <Menu.Trigger asChild disabled={!objectMenuItems?.length}>
116
+ <Toolbar.IconButton iconOnly variant='ghost' icon='ph--dots-three-vertical--regular' label='Actions' />
117
+ </Menu.Trigger>
118
+ <Menu.Content items={objectMenuItems} />
119
+ </Card.Toolbar>
120
+ <Surface.Surface role='card--content' limit={1} data={{ subject: data }} />
121
+ </Card.Root>
122
+ </Menu.Root>
123
+ );
124
+ };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { MasonryContainer } from './MasonryContainer';
6
+
7
+ export default MasonryContainer;
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type ComponentType, lazy } from 'react';
6
+
7
+ export const MasonryContainer: ComponentType<any> = lazy(() => import('./MasonryContainer'));
package/src/meta.ts CHANGED
@@ -2,11 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type PluginMeta } from '@dxos/app-framework';
5
+ import { type Plugin } from '@dxos/app-framework';
6
6
  import { trim } from '@dxos/util';
7
7
 
8
- export const meta: PluginMeta = {
9
- id: 'dxos.org/plugin/masonry',
8
+ export const meta: Plugin.Meta = {
9
+ id: 'org.dxos.plugin.masonry',
10
10
  name: 'Masonry',
11
11
  description: trim`
12
12
  Responsive grid layout that displays query results in an adaptive masonry pattern.
@@ -2,23 +2,28 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Type } from '@dxos/echo';
5
6
  import { type Resource } from '@dxos/react-ui';
6
7
 
7
- import { meta } from './meta';
8
- import { Masonry } from './types';
8
+ import { meta } from '#meta';
9
+ import { Masonry } from '#types';
9
10
 
10
11
  export const translations = [
11
12
  {
12
13
  'en-US': {
13
- [Masonry.Masonry.typename]: {
14
- 'typename label': 'Masonry',
15
- 'typename label_zero': 'Masonries',
16
- 'typename label_one': 'Masonry',
17
- 'typename label_other': 'Masonries',
18
- 'object name placeholder': 'New masonry',
14
+ [Type.getTypename(Masonry.Masonry)]: {
15
+ 'typename.label': 'Masonry',
16
+ 'typename.label_zero': 'Masonries',
17
+ 'typename.label_one': 'Masonry',
18
+ 'typename.label_other': 'Masonries',
19
+ 'object-name.placeholder': 'New masonry',
20
+ 'add-object.label': 'Add masonry',
21
+ 'rename-object.label': 'Rename masonry',
22
+ 'delete-object.label': 'Delete masonry',
23
+ 'object-deleted.label': 'Masonry deleted',
19
24
  },
20
25
  [meta.id]: {
21
- 'plugin name': 'Masonry',
26
+ 'plugin.name': 'Masonry',
22
27
  },
23
28
  },
24
29
  },
@@ -4,47 +4,45 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { Obj, Type } from '@dxos/echo';
8
- import { type JsonSchemaType, ViewAnnotation, toEffectSchema } from '@dxos/echo/internal';
9
- import { type CreateViewFromSpaceProps, type DataType, createViewFromSpace } from '@dxos/schema';
7
+ import { Annotation, Obj, Ref, Type } from '@dxos/echo';
8
+ import { View } from '@dxos/echo';
9
+ import { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/internal';
10
+ import { ViewAnnotation } from '@dxos/schema';
10
11
 
11
12
  export const Masonry = Schema.Struct({
13
+ name: Schema.String.pipe(Schema.optional),
14
+
15
+ view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false)),
16
+
12
17
  arrangement: Schema.Array(
13
18
  Schema.Struct({
14
- ids: Schema.Array(Type.ObjectId),
19
+ ids: Schema.Array(Obj.ID),
15
20
  hidden: Schema.optional(Schema.Boolean),
16
- }).pipe(Schema.mutable),
17
- ).pipe(Schema.mutable, Schema.optional),
21
+ }),
22
+ ).pipe(FormInputAnnotation.set(false), Schema.optional),
18
23
  // TODO(wittjosiah): Consider Masonry supporting not being just a view but referencing arbitrary data directly.
19
24
  }).pipe(
20
- Type.Obj({
21
- typename: 'dxos.org/type/Masonry',
25
+ Type.object({
26
+ typename: 'org.dxos.type.masonry',
22
27
  version: '0.1.0',
23
28
  }),
29
+ LabelAnnotation.set(['name']),
24
30
  ViewAnnotation.set(true),
31
+ Annotation.IconAnnotation.set({
32
+ icon: 'ph--wall--regular',
33
+ hue: 'green',
34
+ }),
25
35
  );
26
36
 
27
- export type Masonry = Schema.Schema.Type<typeof Masonry>;
37
+ export interface Masonry extends Schema.Schema.Type<typeof Masonry> {}
38
+
39
+ type MakeProps = Omit<Partial<Obj.MakeProps<typeof Masonry>>, 'view'> & {
40
+ view: View.View;
41
+ };
28
42
 
29
43
  /**
30
- * Make a masonry object.
44
+ * Make a masonry as a view of a data set.
31
45
  */
32
- export const make = (props: Obj.MakeProps<typeof Masonry> = {}) => Obj.make(Masonry, props);
33
-
34
- export type MakeViewProps = Omit<CreateViewFromSpaceProps, 'presentation'>;
35
-
36
- export const makeView = async ({
37
- ...props
38
- }: MakeViewProps): Promise<{
39
- jsonSchema: JsonSchemaType;
40
- view: DataType.View;
41
- schema: ReturnType<typeof toEffectSchema>;
42
- }> => {
43
- const masonry = Obj.make(Masonry, {});
44
- const { jsonSchema, view } = await createViewFromSpace({ ...props, presentation: masonry });
45
-
46
- // Preset sizes.
47
- const schema = toEffectSchema(jsonSchema);
48
-
49
- return { jsonSchema, schema, view };
46
+ export const make = ({ name, arrangement = [], view }: MakeProps): Masonry => {
47
+ return Obj.make(Masonry, { name, view: Ref.make(view), arrangement });
50
48
  };