@dxos/plugin-masonry 0.8.4-main.c351d160a8 → 0.8.4-main.d9fc60f731

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 (102) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +328 -0
  3. package/README.md +1 -1
  4. package/dist/lib/neutral/MasonryContainer-XXJXCOOA.mjs +96 -0
  5. package/dist/lib/neutral/MasonryContainer-XXJXCOOA.mjs.map +7 -0
  6. package/dist/lib/neutral/MasonryPlugin.mjs +38 -0
  7. package/dist/lib/neutral/MasonryPlugin.mjs.map +7 -0
  8. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  9. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  10. package/dist/lib/neutral/chunk-IJ2FIXSI.mjs +32 -0
  11. package/dist/lib/neutral/chunk-IJ2FIXSI.mjs.map +7 -0
  12. package/dist/lib/{browser/chunk-ANLADCDB.mjs → neutral/chunk-SS54S63V.mjs} +10 -11
  13. package/dist/lib/neutral/chunk-SS54S63V.mjs.map +7 -0
  14. package/dist/lib/neutral/components/index.mjs +1 -0
  15. package/dist/lib/neutral/containers/index.mjs +9 -0
  16. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  17. package/dist/lib/neutral/create-object-WRCYV4HT.mjs +39 -0
  18. package/dist/lib/neutral/create-object-WRCYV4HT.mjs.map +7 -0
  19. package/dist/lib/neutral/index.mjs +14 -0
  20. package/dist/lib/neutral/meta.json +1 -0
  21. package/dist/lib/neutral/meta.mjs +8 -0
  22. package/dist/lib/neutral/plugin.mjs +12 -0
  23. package/dist/lib/neutral/plugin.mjs.map +7 -0
  24. package/dist/lib/{browser/react-surface-E3X4DF5N.mjs → neutral/react-surface-7CZHE3XA.mjs} +15 -22
  25. package/dist/lib/neutral/react-surface-7CZHE3XA.mjs.map +7 -0
  26. package/dist/lib/neutral/translations.mjs +30 -0
  27. package/dist/lib/neutral/translations.mjs.map +7 -0
  28. package/dist/lib/{browser → neutral}/types/index.mjs +1 -1
  29. package/dist/lib/neutral/types/index.mjs.map +7 -0
  30. package/dist/types/src/MasonryPlugin.d.ts +1 -0
  31. package/dist/types/src/MasonryPlugin.d.ts.map +1 -1
  32. package/dist/types/src/MasonryPlugin.test.d.ts +2 -0
  33. package/dist/types/src/MasonryPlugin.test.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  35. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/index.d.ts +8 -1
  37. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  39. package/dist/types/src/containers/MasonryContainer/MasonryContainer.d.ts +1 -2
  40. package/dist/types/src/containers/MasonryContainer/MasonryContainer.d.ts.map +1 -1
  41. package/dist/types/src/containers/MasonryContainer/MasonryContainer.stories.d.ts.map +1 -1
  42. package/dist/types/src/containers/MasonryContainer/index.d.ts +1 -2
  43. package/dist/types/src/containers/MasonryContainer/index.d.ts.map +1 -1
  44. package/dist/types/src/index.d.ts +1 -1
  45. package/dist/types/src/index.d.ts.map +1 -1
  46. package/dist/types/src/meta.d.ts +1 -1
  47. package/dist/types/src/meta.d.ts.map +1 -1
  48. package/dist/types/src/plugin.d.ts +3 -0
  49. package/dist/types/src/plugin.d.ts.map +1 -0
  50. package/dist/types/src/translations.d.ts +20 -20
  51. package/dist/types/src/translations.d.ts.map +1 -1
  52. package/dist/types/src/types/Masonry.d.ts +6 -6
  53. package/dist/types/src/types/Masonry.d.ts.map +1 -1
  54. package/dist/types/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +79 -46
  56. package/src/MasonryPlugin.test.ts +26 -0
  57. package/src/MasonryPlugin.tsx +13 -26
  58. package/src/capabilities/create-object.ts +36 -0
  59. package/src/capabilities/index.ts +4 -1
  60. package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +10 -9
  61. package/src/containers/MasonryContainer/MasonryContainer.stories.tsx +11 -13
  62. package/src/containers/MasonryContainer/MasonryContainer.tsx +63 -41
  63. package/src/containers/MasonryContainer/index.ts +1 -3
  64. package/src/index.ts +1 -2
  65. package/src/meta.ts +19 -7
  66. package/src/plugin.ts +9 -0
  67. package/src/translations.ts +12 -12
  68. package/src/types/Masonry.ts +12 -13
  69. package/src/types/MasonryAction.ts +1 -1
  70. package/src/vite-env.d.ts +10 -0
  71. package/dist/lib/browser/MasonryContainer-Q7VPG6YS.mjs +0 -84
  72. package/dist/lib/browser/MasonryContainer-Q7VPG6YS.mjs.map +0 -7
  73. package/dist/lib/browser/chunk-7W5GV4MG.mjs +0 -19
  74. package/dist/lib/browser/chunk-7W5GV4MG.mjs.map +0 -7
  75. package/dist/lib/browser/chunk-ANLADCDB.mjs.map +0 -7
  76. package/dist/lib/browser/index.mjs +0 -78
  77. package/dist/lib/browser/index.mjs.map +0 -7
  78. package/dist/lib/browser/meta.json +0 -1
  79. package/dist/lib/browser/react-surface-E3X4DF5N.mjs.map +0 -7
  80. package/dist/lib/node-esm/MasonryContainer-NQGW3OZD.mjs +0 -85
  81. package/dist/lib/node-esm/MasonryContainer-NQGW3OZD.mjs.map +0 -7
  82. package/dist/lib/node-esm/chunk-CCWUD5KF.mjs +0 -21
  83. package/dist/lib/node-esm/chunk-CCWUD5KF.mjs.map +0 -7
  84. package/dist/lib/node-esm/chunk-GHHWMU6E.mjs +0 -68
  85. package/dist/lib/node-esm/chunk-GHHWMU6E.mjs.map +0 -7
  86. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  87. package/dist/lib/node-esm/index.mjs +0 -79
  88. package/dist/lib/node-esm/index.mjs.map +0 -7
  89. package/dist/lib/node-esm/meta.json +0 -1
  90. package/dist/lib/node-esm/react-surface-4C6BZVEJ.mjs +0 -43
  91. package/dist/lib/node-esm/react-surface-4C6BZVEJ.mjs.map +0 -7
  92. package/dist/lib/node-esm/types/index.mjs +0 -11
  93. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  94. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  95. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  96. package/src/capabilities/react-surface/index.ts +0 -7
  97. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  98. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
  99. /package/dist/lib/{browser/types → neutral/components}/index.mjs.map +0 -0
  100. /package/dist/lib/{node-esm/types → neutral}/index.mjs.map +0 -0
  101. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
  102. /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
package/LICENSE CHANGED
@@ -1,8 +1,105 @@
1
- MIT License
2
- Copyright (c) 2022 DXOS
1
+ # Functional Source License, Version 1.1, ALv2 Future License
3
2
 
4
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
3
+ ## Abbreviation
5
4
 
6
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
5
+ FSL-1.1-Apache-2.0
7
6
 
8
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7
+ ## Notice
8
+
9
+ Copyright 2026 DXOS
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to
27
+ use, copy, modify, create derivative works, publicly perform, publicly display
28
+ and redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or
34
+ service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software
39
+ that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee
52
+ using the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to
59
+ the infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software,
68
+ you must include a copy of or a link to these Terms and Conditions and not
69
+ remove any copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
75
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
76
+
77
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
78
+ SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
79
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
80
+
81
+ ### Trademarks
82
+
83
+ Except for displaying the License Details and identifying us as the origin of
84
+ the Software, you have no right under these Terms and Conditions to use our
85
+ trademarks, trade names, service marks or product names.
86
+
87
+ ## Grant of Future License
88
+
89
+ We hereby irrevocably grant you an additional license to use the Software under
90
+ the Apache License, Version 2.0 that is effective on the second anniversary of
91
+ the date we make the Software available. On or after that date, you may use the
92
+ Software under the Apache License, Version 2.0, in which case the following
93
+ will apply:
94
+
95
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
96
+ this file except in compliance with the License.
97
+
98
+ You may obtain a copy of the License at
99
+
100
+ http://www.apache.org/licenses/LICENSE-2.0
101
+
102
+ Unless required by applicable law or agreed to in writing, software distributed
103
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
+ specific language governing permissions and limitations under the License.
package/PLUGIN.mdl ADDED
@@ -0,0 +1,328 @@
1
+ ---
2
+ id: org.dxos.plugin.masonry
3
+ name: MasonryPlugin
4
+ version: 0.1.0
5
+ ---
6
+
7
+ A responsive masonry grid plugin for `DXOS` Composer.
8
+
9
+ A `Masonry` is an ECHO object that wraps a `View` — a query-driven window onto
10
+ a typed collection — and renders the matching objects as a flowing, column-balanced
11
+ card grid.
12
+ Cards resize to fit their content; the grid reflows automatically as items are
13
+ added, removed, or the viewport changes.
14
+ A search bar at the top filters cards client-side without altering the underlying
15
+ query.
16
+
17
+ ## Extensions
18
+
19
+ The following extension dialects are used in this document.
20
+ Each extension is defined in the Appendix or resolved via its URI.
21
+
22
+ | Term | URI |
23
+ |-------------|--------------------------------|
24
+ | `type` | `org.dxos.mdl.type@1.0` |
25
+ | `feat` | `org.dxos.mdl.feat@1.0` |
26
+ | `test` | `org.dxos.mdl.test@1.0` |
27
+ | `component` | `org.dxos.mdl.component@1.0` |
28
+ | `op` | `org.dxos.mdl.op@1.0` |
29
+
30
+ ## Types
31
+
32
+ ```mdl
33
+ type ColumnSlot
34
+ fields:
35
+ ids: string[] # ordered object IDs assigned to this column
36
+ hidden?: boolean # column hidden from the viewport
37
+ ```
38
+
39
+ ```mdl
40
+ type Masonry
41
+ typename: org.dxos.type.masonry
42
+ fields:
43
+ name?: string
44
+ view: Ref<View> # ECHO View that drives the query
45
+ arrangement?: ColumnSlot[] # persisted column assignments (optional)
46
+ ```
47
+
48
+ ## Components
49
+
50
+ ```mdl
51
+ component MasonryContainer
52
+ desc: |
53
+ Main surface rendered for the Article and Section roles.
54
+ Resolves the View from the Masonry object, runs the ECHO query,
55
+ filters results with the search bar, and delegates each card
56
+ to a Surface slot so other plugins can supply card bodies.
57
+ props:
58
+ view: View | Ref<View>
59
+ role?: string
60
+ state:
61
+ cardSchema?: Schema # resolved Effect Schema for the queried typename
62
+ objects: EchoObject[] # live query results
63
+ results: EchoObject[] # search-filtered subset of objects
64
+ slots:
65
+ card?: ReactNode # injected via AppSurface.Card per object
66
+ actions:
67
+ handleSearch(query: string) # filters results client-side
68
+ layout: |
69
+ ┌─────────────────────────┐
70
+ │ [Search bar] │ ← SearchList.Input inside Toolbar
71
+ ├─────────────────────────┤
72
+ │ ┌──────┐ ┌──────┐ │
73
+ │ │ Card │ │ Card │ … │ ← MasonryComponent.Viewport (column-balanced)
74
+ │ │[body]│ │[body]│ │
75
+ │ └──────┘ └──────┘ │
76
+ └─────────────────────────┘
77
+ ```
78
+
79
+ ```mdl
80
+ component Item
81
+ desc: |
82
+ Individual tile rendered inside the masonry grid.
83
+ Displays the object label and type icon in a Card.Toolbar, exposes a
84
+ context menu for object-level actions, and delegates the card body to
85
+ a Surface slot so other plugins can inject rich content.
86
+ props:
87
+ data: EchoObject
88
+ state:
89
+ icon: string # resolved from schema IconAnnotation
90
+ objectMenuItems: MenuItem[] # contributed by other plugins
91
+ slots:
92
+ cardBody?: ReactNode # AppSurface.Card surface for the object
93
+ actions:
94
+ openMenu()
95
+ layout: |
96
+ ┌────────────────────────┐
97
+ │ [icon] Label [⋮ menu] │ ← Card.Toolbar
98
+ ├────────────────────────┤
99
+ │ [cardBody slot] │ ← Surface.Surface(AppSurface.Card)
100
+ └────────────────────────┘
101
+ ```
102
+
103
+ ## Operations
104
+
105
+ ```mdl
106
+ op createMasonry
107
+ desc: |
108
+ Creates a new Masonry ECHO object backed by a ViewModel built from the
109
+ selected typename, then adds it to the target space node.
110
+ input:
111
+ name?: string
112
+ typename?: string # ECHO typename of card objects to query
113
+ target: SpaceNode # space or folder to add the object into
114
+ targetNodeId?: string
115
+ output: Masonry
116
+ effects: [echo:write]
117
+ requires: [SpaceOperation.AddObject, ViewModel]
118
+ note: |
119
+ ViewModel.makeFromDatabase is called first to create the View;
120
+ the resulting View reference is embedded in the Masonry before persisting.
121
+ ```
122
+
123
+ ## Features
124
+
125
+ ```mdl
126
+ feat F-1: Create Masonry
127
+
128
+ req F-1.1:
129
+ when: user invokes "Add masonry" from the space action menu
130
+ then: creation dialog shown with optional name and typename inputs
131
+
132
+ req F-1.2:
133
+ when: user confirms creation
134
+ then: op:createMasonry called; Masonry + View objects written to ECHO
135
+
136
+ req F-1.3:
137
+ when: op:createMasonry completes
138
+ then: new Masonry is navigable in the space navigator
139
+ ```
140
+
141
+ ```mdl
142
+ feat F-2: View Masonry
143
+
144
+ req F-2.1: MasonryContainer renders for both Article and Section surface roles.
145
+
146
+ req F-2.2:
147
+ when: MasonryContainer mounts
148
+ then: ECHO query derived from view.query runs; matching objects populate the grid
149
+
150
+ req F-2.3:
151
+ when: new objects matching the query are added by any peer
152
+ then: grid updates reactively without a full remount
153
+ tags: [collaborative]
154
+
155
+ req F-2.4: Objects are displayed alphabetically by label within the grid.
156
+ ```
157
+
158
+ ```mdl
159
+ feat F-3: Search
160
+
161
+ req F-3.1:
162
+ when: user types in the search bar
163
+ then: visible cards filtered client-side to those whose label matches
164
+
165
+ req F-3.2:
166
+ when: search input is cleared
167
+ then: all query results are shown again
168
+
169
+ req F-3.3: Search does not modify the underlying ECHO query or View.
170
+ ```
171
+
172
+ ```mdl
173
+ feat F-4: Card Surface Delegation
174
+
175
+ req F-4.1:
176
+ when: an Item is rendered
177
+ then: AppSurface.Card surface is opened for the object, allowing other
178
+ plugins to supply rich card body content
179
+
180
+ req F-4.2:
181
+ when: no plugin provides a card body for an object type
182
+ then: card renders with header only (no body content)
183
+
184
+ req F-4.3:
185
+ when: user opens the context menu (⋮) on a card
186
+ then: object menu items contributed by other plugins are shown
187
+ ```
188
+
189
+ ```mdl
190
+ feat F-5: Schema Resolution
191
+
192
+ req F-5.1:
193
+ when: MasonryContainer resolves cardSchema
194
+ then: static schemas (from AppCapabilities.Schema) are checked first
195
+
196
+ req F-5.2:
197
+ when: typename is not found in static schemas
198
+ then: db.schemaRegistry is queried reactively until the schema appears
199
+
200
+ req F-5.3:
201
+ when: cardSchema changes
202
+ then: ECHO query filter is updated and the grid re-renders
203
+ ```
204
+
205
+ ## Acceptance
206
+
207
+ ```mdl
208
+ test T-1: Plugin modules activate
209
+ given: MasonryPlugin added to a Composer test app
210
+ then:
211
+ - CreateObject module is active
212
+ - schema module is active
213
+ - ReactSurface module is active
214
+ ```
215
+
216
+ ```mdl
217
+ test T-2: Create masonry object
218
+ given: a space with a typed schema (e.g. Note)
219
+ when: op:createMasonry is invoked with typename = Note's typename
220
+ then:
221
+ - a Masonry object exists in the space
222
+ - Masonry.view references a View whose query matches Note typename
223
+ ```
224
+
225
+ ```mdl
226
+ test T-3: Grid renders query results
227
+ given: a Masonry backed by a View querying Note objects, 3 Notes in the space
228
+ when: MasonryContainer mounts
229
+ then: 3 Item tiles visible, each showing the Note's label
230
+ ```
231
+
232
+ ```mdl
233
+ test T-4: Search filters cards
234
+ given: MasonryContainer showing ["Alpha", "Beta", "Gamma"]
235
+ when: user types "al" in the search bar
236
+ then: only "Alpha" is visible
237
+ ```
238
+
239
+ ```mdl
240
+ test T-5: Search clear restores all cards
241
+ given: search bar contains "al", only "Alpha" visible
242
+ when: user clears the search input
243
+ then: all 3 cards are visible again
244
+ ```
245
+
246
+ ```mdl
247
+ test T-6: Reactive update on peer write
248
+ given: MasonryContainer open, query returning 2 objects
249
+ when: a peer writes a third matching object to ECHO
250
+ then: grid shows 3 tiles without a page reload
251
+ tags: [collaborative]
252
+ ```
253
+
254
+ ```mdl
255
+ test T-7: Card body delegated to Surface
256
+ given: a plugin that provides AppSurface.Card for Note objects
257
+ when: a Note appears in the masonry grid
258
+ then: the Note card renders the plugin-provided body below the Card.Toolbar
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Appendix: Extension Definitions
264
+
265
+ Extension block types used in this document are defined below using
266
+ the core `ext` primitive — the only construct the base language provides.
267
+
268
+ ```mdl
269
+ ext type
270
+ uri: org.dxos.mdl.type@1.0
271
+ desc: A named data structure with typed fields and optional literals.
272
+ fields:
273
+ desc?: Prose
274
+ fields?: FieldMap # name[?]: TypeExpr (# inline comment)
275
+ literals?: UnionList # a | b | c
276
+ extends?: TypeRef[]
277
+ ```
278
+
279
+ ```mdl
280
+ ext feat
281
+ uri: org.dxos.mdl.feat@1.0
282
+ desc: A named feature grouping one or more requirements.
283
+ fields:
284
+ desc?: Prose
285
+ req: RequirementList
286
+ nesting: self # feat blocks may contain feat blocks
287
+ ```
288
+
289
+ ```mdl
290
+ ext test
291
+ uri: org.dxos.mdl.test@1.0
292
+ desc: An acceptance scenario expressed as given / when / then steps.
293
+ fields:
294
+ given?: Step | Step[]
295
+ when?: Step | Step[]
296
+ then: Step | Step[]
297
+ tags?: TagList
298
+ ```
299
+
300
+ ```mdl
301
+ ext component
302
+ uri: org.dxos.mdl.component@1.0
303
+ desc: A UI component with props, internal state, slots, actions, and events.
304
+ fields:
305
+ desc?: Prose
306
+ props?: FieldMap # external inputs (immutable inside component)
307
+ state?: FieldMap # internal reactive state
308
+ slots?: FieldMap # named ReactNode injection points
309
+ actions?: ActionMap # methods the component exposes or handles
310
+ emits?: EventMap # events the component raises to its parent
311
+ layout?: CodeBlock # ASCII sketch of visual structure (non-normative)
312
+ ```
313
+
314
+ ```mdl
315
+ ext op
316
+ uri: org.dxos.mdl.op@1.0
317
+ desc: |
318
+ A named operation with typed inputs, outputs, and declared errors.
319
+ Pure ops have no effects or requires. Effectful ops declare both.
320
+ fields:
321
+ desc?: Prose
322
+ input?: FieldMap # named input parameters
323
+ output?: TypeExpr # return type
324
+ errors?: ErrorMap # name: Prose (when this error occurs)
325
+ effects?: EffectList # echo:read | echo:write | http | fs | ...
326
+ requires?: ServiceList # injected service dependencies
327
+ note?: Prose # implementation guidance (non-normative)
328
+ ```
package/README.md CHANGED
@@ -12,4 +12,4 @@ A masonry plugin.
12
12
 
13
13
  Your ideas, issues, and code are most welcome. Please take a look at our [community code of conduct](https://github.com/dxos/dxos/blob/main/CODE_OF_CONDUCT.md), the [issue guide](https://github.com/dxos/dxos/blob/main/CONTRIBUTING.md#submitting-issues), and the [PR contribution guide](https://github.com/dxos/dxos/blob/main/CONTRIBUTING.md#submitting-prs).
14
14
 
15
- License: [MIT](./LICENSE) Copyright 2023 © DXOS
15
+ License: [FSL-1.1-Apache-2.0](./LICENSE) Copyright 2023 © DXOS
@@ -0,0 +1,96 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/containers/MasonryContainer/MasonryContainer.tsx
4
+ import React, { useEffect, useMemo, useState } from "react";
5
+ import { Surface, useCapabilities } from "@dxos/app-framework/ui";
6
+ import { AppCapabilities } from "@dxos/app-toolkit";
7
+ import { AppSurface, useObjectMenuItems, useSchemaFilter } from "@dxos/app-toolkit/ui";
8
+ import { Filter, Obj, Query, Type } from "@dxos/echo";
9
+ import { useObject, useQuery } from "@dxos/react-client/echo";
10
+ import { Card, Panel, Toolbar } from "@dxos/react-ui";
11
+ import { Masonry as MasonryComponent } from "@dxos/react-ui-masonry";
12
+ import { Menu } from "@dxos/react-ui-menu";
13
+ import { SearchList, useSearchListResults } from "@dxos/react-ui-search";
14
+ import { getTagFromQuery, getTypenameFromQuery } from "@dxos/schema";
15
+ import { isNonNullable } from "@dxos/util";
16
+ var MasonryContainer = ({ view: viewOrRef, role: _role }) => {
17
+ const [view] = useObject(viewOrRef);
18
+ const schemas = useCapabilities(AppCapabilities.Schema);
19
+ const db = view && Obj.getDatabase(view);
20
+ const typename = view?.query ? getTypenameFromQuery(view.query.ast) : void 0;
21
+ const tag = view?.query ? getTagFromQuery(view.query.ast) : void 0;
22
+ const [cardSchema, setCardSchema] = useState();
23
+ useEffect(() => {
24
+ const staticSchema = schemas.flat().find((schema) => Type.getTypename(schema) === typename);
25
+ if (staticSchema) {
26
+ setCardSchema(() => staticSchema);
27
+ return;
28
+ }
29
+ if (typename && db) {
30
+ const findInRegistry = () => db.graph.registry.list().filter(Type.isType).find((t) => Type.getTypename(t) === typename);
31
+ setCardSchema(() => findInRegistry());
32
+ return db.graph.registry.changed.on(() => {
33
+ setCardSchema(() => findInRegistry());
34
+ });
35
+ }
36
+ setCardSchema(void 0);
37
+ }, [
38
+ schemas,
39
+ typename,
40
+ db
41
+ ]);
42
+ const baseFilter = useSchemaFilter(cardSchema);
43
+ const query = useMemo(() => tag ? Query.select(baseFilter).select(Filter.tag(tag)) : Query.select(baseFilter), [
44
+ baseFilter,
45
+ tag
46
+ ]);
47
+ const objects = useQuery(db, query);
48
+ const sortedObjects = useMemo(() => objects.filter(isNonNullable).toSorted((a, b) => (Obj.getLabel(a) ?? "").localeCompare(Obj.getLabel(b) ?? "")), [
49
+ objects
50
+ ]);
51
+ const { results, handleSearch } = useSearchListResults({
52
+ items: sortedObjects,
53
+ extract: (obj) => Obj.getLabel(obj) ?? ""
54
+ });
55
+ return /* @__PURE__ */ React.createElement(MasonryComponent.Root, {
56
+ Tile: Item
57
+ }, /* @__PURE__ */ React.createElement(SearchList.Root, {
58
+ onSearch: handleSearch
59
+ }, /* @__PURE__ */ React.createElement(Panel.Root, null, /* @__PURE__ */ React.createElement(Panel.Toolbar, {
60
+ asChild: true
61
+ }, /* @__PURE__ */ React.createElement(Toolbar.Root, null, /* @__PURE__ */ React.createElement(SearchList.Input, {
62
+ placeholder: "Search..."
63
+ }))), /* @__PURE__ */ React.createElement(Panel.Content, null, /* @__PURE__ */ React.createElement(MasonryComponent.Content, null, /* @__PURE__ */ React.createElement(MasonryComponent.Viewport, {
64
+ items: results,
65
+ getId: (data) => data?.id
66
+ }))))));
67
+ };
68
+ var Item = ({ data }) => {
69
+ const objectMenuItems = useObjectMenuItems(data);
70
+ const icon = Obj.getIcon(data)?.icon ?? "ph--circle-dashed--regular";
71
+ return /* @__PURE__ */ React.createElement(Menu.Root, null, /* @__PURE__ */ React.createElement(Card.Root, null, /* @__PURE__ */ React.createElement(Card.Header, null, /* @__PURE__ */ React.createElement(Card.Icon, {
72
+ icon
73
+ }), /* @__PURE__ */ React.createElement(Card.Title, null, Obj.getLabel(data, {
74
+ fallback: "typename"
75
+ })), /* @__PURE__ */ React.createElement(Menu.Trigger, {
76
+ asChild: true,
77
+ disabled: !objectMenuItems?.length
78
+ }, /* @__PURE__ */ React.createElement(Toolbar.IconButton, {
79
+ iconOnly: true,
80
+ variant: "ghost",
81
+ icon: "ph--dots-three-vertical--regular",
82
+ label: "Actions"
83
+ })), /* @__PURE__ */ React.createElement(Menu.Content, {
84
+ items: objectMenuItems
85
+ })), /* @__PURE__ */ React.createElement(Surface.Surface, {
86
+ type: AppSurface.Card,
87
+ limit: 1,
88
+ data: {
89
+ subject: data
90
+ }
91
+ })));
92
+ };
93
+ export {
94
+ MasonryContainer as default
95
+ };
96
+ //# sourceMappingURL=MasonryContainer-XXJXCOOA.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/containers/MasonryContainer/MasonryContainer.tsx"],
4
+ "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useEffect, useMemo, useState } from 'react';\n\nimport { Surface, useCapabilities } from '@dxos/app-framework/ui';\nimport { AppCapabilities } from '@dxos/app-toolkit';\nimport { AppSurface, useObjectMenuItems, useSchemaFilter } from '@dxos/app-toolkit/ui';\nimport { Filter, Obj, Query, type Ref, Type, type View } from '@dxos/echo';\nimport { useObject, useQuery } from '@dxos/react-client/echo';\nimport { Card, Panel, Toolbar } from '@dxos/react-ui';\nimport { Masonry as MasonryComponent } from '@dxos/react-ui-masonry';\nimport { Menu } from '@dxos/react-ui-menu';\nimport { SearchList, useSearchListResults } from '@dxos/react-ui-search';\nimport { getTagFromQuery, getTypenameFromQuery } from '@dxos/schema';\nimport { isNonNullable } from '@dxos/util';\n\nexport type MasonryContainerProps = {\n view: View.View;\n role?: string;\n};\n\nexport const MasonryContainer = ({\n view: viewOrRef,\n role: _role,\n}: {\n view: View.View | Ref.Ref<View.View>;\n role?: string;\n}) => {\n const [view] = useObject(viewOrRef);\n const schemas = useCapabilities(AppCapabilities.Schema);\n const db = view && Obj.getDatabase(view);\n const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;\n const tag = view?.query ? getTagFromQuery(view.query.ast) : undefined;\n\n const [cardSchema, setCardSchema] = useState<Type.AnyEntity>();\n\n useEffect(() => {\n const staticSchema = schemas.flat().find((schema) => Type.getTypename(schema) === typename);\n if (staticSchema) {\n setCardSchema(() => staticSchema);\n return;\n }\n if (typename && db) {\n const findInRegistry = () =>\n db.graph.registry\n .list()\n .filter(Type.isType)\n .find((t) => Type.getTypename(t) === typename);\n setCardSchema(() => findInRegistry());\n return db.graph.registry.changed.on(() => {\n setCardSchema(() => findInRegistry());\n });\n }\n setCardSchema(undefined);\n }, [schemas, typename, db]);\n\n const baseFilter = useSchemaFilter(cardSchema);\n const query = useMemo(\n () => (tag ? Query.select(baseFilter).select(Filter.tag(tag)) : Query.select(baseFilter)),\n [baseFilter, tag],\n );\n const objects = useQuery(db, query);\n\n const sortedObjects = useMemo(\n () =>\n objects.filter(isNonNullable).toSorted((a, b) => (Obj.getLabel(a) ?? '').localeCompare(Obj.getLabel(b) ?? '')),\n [objects],\n );\n\n const { results, handleSearch } = useSearchListResults({\n items: sortedObjects,\n extract: (obj) => Obj.getLabel(obj) ?? '',\n });\n\n return (\n <MasonryComponent.Root Tile={Item}>\n <SearchList.Root onSearch={handleSearch}>\n <Panel.Root>\n <Panel.Toolbar asChild>\n <Toolbar.Root>\n <SearchList.Input placeholder='Search...' />\n </Toolbar.Root>\n </Panel.Toolbar>\n <Panel.Content>\n <MasonryComponent.Content>\n <MasonryComponent.Viewport items={results} getId={(data: any) => data?.id} />\n </MasonryComponent.Content>\n </Panel.Content>\n </Panel.Root>\n </SearchList.Root>\n </MasonryComponent.Root>\n );\n};\n\nconst Item = ({ data }: { data: any }) => {\n const objectMenuItems = useObjectMenuItems(data);\n const icon = Obj.getIcon(data)?.icon ?? 'ph--circle-dashed--regular';\n\n return (\n <Menu.Root>\n <Card.Root>\n <Card.Header>\n <Card.Icon icon={icon} />\n <Card.Title>{Obj.getLabel(data, { fallback: 'typename' })}</Card.Title>\n {/* TODO(wittjosiah): Reconcile with Card.Menu. */}\n <Menu.Trigger asChild disabled={!objectMenuItems?.length}>\n <Toolbar.IconButton iconOnly variant='ghost' icon='ph--dots-three-vertical--regular' label='Actions' />\n </Menu.Trigger>\n <Menu.Content items={objectMenuItems} />\n </Card.Header>\n <Surface.Surface\n type={AppSurface.Card}\n limit={1}\n data={{ subject: data } satisfies AppSurface.ObjectCardData}\n />\n </Card.Root>\n </Menu.Root>\n );\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["React", "useEffect", "useMemo", "useState", "Surface", "useCapabilities", "AppCapabilities", "AppSurface", "useObjectMenuItems", "useSchemaFilter", "Filter", "Obj", "Query", "Type", "useObject", "useQuery", "Card", "Panel", "Toolbar", "Masonry", "MasonryComponent", "Menu", "SearchList", "useSearchListResults", "getTagFromQuery", "getTypenameFromQuery", "isNonNullable", "MasonryContainer", "view", "viewOrRef", "role", "_role", "schemas", "Schema", "db", "getDatabase", "typename", "query", "ast", "undefined", "tag", "cardSchema", "setCardSchema", "staticSchema", "flat", "find", "schema", "getTypename", "findInRegistry", "graph", "registry", "list", "filter", "isType", "t", "changed", "on", "baseFilter", "select", "objects", "sortedObjects", "toSorted", "a", "b", "getLabel", "localeCompare", "results", "handleSearch", "items", "extract", "obj", "Root", "Tile", "Item", "onSearch", "asChild", "Input", "placeholder", "Content", "Viewport", "getId", "data", "id", "objectMenuItems", "icon", "getIcon", "Header", "Icon", "Title", "fallback", "Trigger", "disabled", "length", "IconButton", "iconOnly", "variant", "label", "type", "limit", "subject"]
7
+ }
@@ -0,0 +1,38 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/MasonryPlugin.tsx
4
+ import { Plugin } from "@dxos/app-framework";
5
+ import { AppPlugin } from "@dxos/app-toolkit";
6
+ import { CreateObject, ReactSurface } from "#capabilities";
7
+ import { meta } from "#meta";
8
+ import { translations } from "#translations";
9
+ import { Masonry } from "#types";
10
+
11
+ // raw-loader:/__w/dxos/dxos/packages/plugins/plugin-masonry/PLUGIN.mdl?raw
12
+ var PLUGIN_default = '---\nid: org.dxos.plugin.masonry\nname: MasonryPlugin\nversion: 0.1.0\n---\n\nA responsive masonry grid plugin for `DXOS` Composer.\n\nA `Masonry` is an ECHO object that wraps a `View` \u2014 a query-driven window onto\na typed collection \u2014 and renders the matching objects as a flowing, column-balanced\ncard grid.\nCards resize to fit their content; the grid reflows automatically as items are\nadded, removed, or the viewport changes.\nA search bar at the top filters cards client-side without altering the underlying\nquery.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype ColumnSlot\n fields:\n ids: string[] # ordered object IDs assigned to this column\n hidden?: boolean # column hidden from the viewport\n```\n\n```mdl\ntype Masonry\n typename: org.dxos.type.masonry\n fields:\n name?: string\n view: Ref<View> # ECHO View that drives the query\n arrangement?: ColumnSlot[] # persisted column assignments (optional)\n```\n\n## Components\n\n```mdl\ncomponent MasonryContainer\n desc: |\n Main surface rendered for the Article and Section roles.\n Resolves the View from the Masonry object, runs the ECHO query,\n filters results with the search bar, and delegates each card\n to a Surface slot so other plugins can supply card bodies.\n props:\n view: View | Ref<View>\n role?: string\n state:\n cardSchema?: Schema # resolved Effect Schema for the queried typename\n objects: EchoObject[] # live query results\n results: EchoObject[] # search-filtered subset of objects\n slots:\n card?: ReactNode # injected via AppSurface.Card per object\n actions:\n handleSearch(query: string) # filters results client-side\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [Search bar] \u2502 \u2190 SearchList.Input inside Toolbar\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Card \u2502 \u2502 Card \u2502 \u2026 \u2502 \u2190 MasonryComponent.Viewport (column-balanced)\n \u2502 \u2502[body]\u2502 \u2502[body]\u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent Item\n desc: |\n Individual tile rendered inside the masonry grid.\n Displays the object label and type icon in a Card.Toolbar, exposes a\n context menu for object-level actions, and delegates the card body to\n a Surface slot so other plugins can inject rich content.\n props:\n data: EchoObject\n state:\n icon: string # resolved from schema IconAnnotation\n objectMenuItems: MenuItem[] # contributed by other plugins\n slots:\n cardBody?: ReactNode # AppSurface.Card surface for the object\n actions:\n openMenu()\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [icon] Label [\u22EE menu] \u2502 \u2190 Card.Toolbar\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 [cardBody slot] \u2502 \u2190 Surface.Surface(AppSurface.Card)\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n## Operations\n\n```mdl\nop createMasonry\n desc: |\n Creates a new Masonry ECHO object backed by a ViewModel built from the\n selected typename, then adds it to the target space node.\n input:\n name?: string\n typename?: string # ECHO typename of card objects to query\n target: SpaceNode # space or folder to add the object into\n targetNodeId?: string\n output: Masonry\n effects: [echo:write]\n requires: [SpaceOperation.AddObject, ViewModel]\n note: |\n ViewModel.makeFromDatabase is called first to create the View;\n the resulting View reference is embedded in the Masonry before persisting.\n```\n\n## Features\n\n```mdl\nfeat F-1: Create Masonry\n\n req F-1.1:\n when: user invokes "Add masonry" from the space action menu\n then: creation dialog shown with optional name and typename inputs\n\n req F-1.2:\n when: user confirms creation\n then: op:createMasonry called; Masonry + View objects written to ECHO\n\n req F-1.3:\n when: op:createMasonry completes\n then: new Masonry is navigable in the space navigator\n```\n\n```mdl\nfeat F-2: View Masonry\n\n req F-2.1: MasonryContainer renders for both Article and Section surface roles.\n\n req F-2.2:\n when: MasonryContainer mounts\n then: ECHO query derived from view.query runs; matching objects populate the grid\n\n req F-2.3:\n when: new objects matching the query are added by any peer\n then: grid updates reactively without a full remount\n tags: [collaborative]\n\n req F-2.4: Objects are displayed alphabetically by label within the grid.\n```\n\n```mdl\nfeat F-3: Search\n\n req F-3.1:\n when: user types in the search bar\n then: visible cards filtered client-side to those whose label matches\n\n req F-3.2:\n when: search input is cleared\n then: all query results are shown again\n\n req F-3.3: Search does not modify the underlying ECHO query or View.\n```\n\n```mdl\nfeat F-4: Card Surface Delegation\n\n req F-4.1:\n when: an Item is rendered\n then: AppSurface.Card surface is opened for the object, allowing other\n plugins to supply rich card body content\n\n req F-4.2:\n when: no plugin provides a card body for an object type\n then: card renders with header only (no body content)\n\n req F-4.3:\n when: user opens the context menu (\u22EE) on a card\n then: object menu items contributed by other plugins are shown\n```\n\n```mdl\nfeat F-5: Schema Resolution\n\n req F-5.1:\n when: MasonryContainer resolves cardSchema\n then: static schemas (from AppCapabilities.Schema) are checked first\n\n req F-5.2:\n when: typename is not found in static schemas\n then: db.schemaRegistry is queried reactively until the schema appears\n\n req F-5.3:\n when: cardSchema changes\n then: ECHO query filter is updated and the grid re-renders\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Plugin modules activate\n given: MasonryPlugin added to a Composer test app\n then:\n - CreateObject module is active\n - schema module is active\n - ReactSurface module is active\n```\n\n```mdl\ntest T-2: Create masonry object\n given: a space with a typed schema (e.g. Note)\n when: op:createMasonry is invoked with typename = Note\'s typename\n then:\n - a Masonry object exists in the space\n - Masonry.view references a View whose query matches Note typename\n```\n\n```mdl\ntest T-3: Grid renders query results\n given: a Masonry backed by a View querying Note objects, 3 Notes in the space\n when: MasonryContainer mounts\n then: 3 Item tiles visible, each showing the Note\'s label\n```\n\n```mdl\ntest T-4: Search filters cards\n given: MasonryContainer showing ["Alpha", "Beta", "Gamma"]\n when: user types "al" in the search bar\n then: only "Alpha" is visible\n```\n\n```mdl\ntest T-5: Search clear restores all cards\n given: search bar contains "al", only "Alpha" visible\n when: user clears the search input\n then: all 3 cards are visible again\n```\n\n```mdl\ntest T-6: Reactive update on peer write\n given: MasonryContainer open, query returning 2 objects\n when: a peer writes a third matching object to ECHO\n then: grid shows 3 tiles without a page reload\n tags: [collaborative]\n```\n\n```mdl\ntest T-7: Card body delegated to Surface\n given: a plugin that provides AppSurface.Card for Note objects\n when: a Note appears in the masonry grid\n then: the Note card renders the plugin-provided body below the Card.Toolbar\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n';
13
+
14
+ // src/MasonryPlugin.tsx
15
+ var MasonryPlugin = Plugin.define(meta).pipe(AppPlugin.addCreateObjectModule({
16
+ activate: CreateObject
17
+ }), AppPlugin.addSchemaModule({
18
+ schema: [
19
+ Masonry.Masonry
20
+ ]
21
+ }), AppPlugin.addSurfaceModule({
22
+ activate: ReactSurface
23
+ }), AppPlugin.addTranslationsModule({
24
+ translations
25
+ }), AppPlugin.addPluginAssetModule({
26
+ asset: {
27
+ pluginId: meta.id,
28
+ path: "PLUGIN.mdl",
29
+ content: PLUGIN_default,
30
+ mimeType: "application/x-mdl"
31
+ }
32
+ }), Plugin.make);
33
+ var MasonryPlugin_default = MasonryPlugin;
34
+ export {
35
+ MasonryPlugin,
36
+ MasonryPlugin_default as default
37
+ };
38
+ //# sourceMappingURL=MasonryPlugin.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/MasonryPlugin.tsx", "raw-loader:/__w/dxos/dxos/packages/plugins/plugin-masonry/PLUGIN.mdl?raw"],
4
+ "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { AppPlugin } from '@dxos/app-toolkit';\n\nimport { CreateObject, ReactSurface } from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { Masonry } from '#types';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\n\nexport const MasonryPlugin = Plugin.define(meta).pipe(\n AppPlugin.addCreateObjectModule({ activate: CreateObject }),\n AppPlugin.addSchemaModule({ schema: [Masonry.Masonry] }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations }),\n AppPlugin.addPluginAssetModule({\n asset: { pluginId: meta.id, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },\n }),\n Plugin.make,\n);\n\nexport default MasonryPlugin;\n", "---\nid: org.dxos.plugin.masonry\nname: MasonryPlugin\nversion: 0.1.0\n---\n\nA responsive masonry grid plugin for `DXOS` Composer.\n\nA `Masonry` is an ECHO object that wraps a `View` \u2014 a query-driven window onto\na typed collection \u2014 and renders the matching objects as a flowing, column-balanced\ncard grid.\nCards resize to fit their content; the grid reflows automatically as items are\nadded, removed, or the viewport changes.\nA search bar at the top filters cards client-side without altering the underlying\nquery.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype ColumnSlot\n fields:\n ids: string[] # ordered object IDs assigned to this column\n hidden?: boolean # column hidden from the viewport\n```\n\n```mdl\ntype Masonry\n typename: org.dxos.type.masonry\n fields:\n name?: string\n view: Ref<View> # ECHO View that drives the query\n arrangement?: ColumnSlot[] # persisted column assignments (optional)\n```\n\n## Components\n\n```mdl\ncomponent MasonryContainer\n desc: |\n Main surface rendered for the Article and Section roles.\n Resolves the View from the Masonry object, runs the ECHO query,\n filters results with the search bar, and delegates each card\n to a Surface slot so other plugins can supply card bodies.\n props:\n view: View | Ref<View>\n role?: string\n state:\n cardSchema?: Schema # resolved Effect Schema for the queried typename\n objects: EchoObject[] # live query results\n results: EchoObject[] # search-filtered subset of objects\n slots:\n card?: ReactNode # injected via AppSurface.Card per object\n actions:\n handleSearch(query: string) # filters results client-side\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [Search bar] \u2502 \u2190 SearchList.Input inside Toolbar\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Card \u2502 \u2502 Card \u2502 \u2026 \u2502 \u2190 MasonryComponent.Viewport (column-balanced)\n \u2502 \u2502[body]\u2502 \u2502[body]\u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent Item\n desc: |\n Individual tile rendered inside the masonry grid.\n Displays the object label and type icon in a Card.Toolbar, exposes a\n context menu for object-level actions, and delegates the card body to\n a Surface slot so other plugins can inject rich content.\n props:\n data: EchoObject\n state:\n icon: string # resolved from schema IconAnnotation\n objectMenuItems: MenuItem[] # contributed by other plugins\n slots:\n cardBody?: ReactNode # AppSurface.Card surface for the object\n actions:\n openMenu()\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [icon] Label [\u22EE menu] \u2502 \u2190 Card.Toolbar\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 [cardBody slot] \u2502 \u2190 Surface.Surface(AppSurface.Card)\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n## Operations\n\n```mdl\nop createMasonry\n desc: |\n Creates a new Masonry ECHO object backed by a ViewModel built from the\n selected typename, then adds it to the target space node.\n input:\n name?: string\n typename?: string # ECHO typename of card objects to query\n target: SpaceNode # space or folder to add the object into\n targetNodeId?: string\n output: Masonry\n effects: [echo:write]\n requires: [SpaceOperation.AddObject, ViewModel]\n note: |\n ViewModel.makeFromDatabase is called first to create the View;\n the resulting View reference is embedded in the Masonry before persisting.\n```\n\n## Features\n\n```mdl\nfeat F-1: Create Masonry\n\n req F-1.1:\n when: user invokes \"Add masonry\" from the space action menu\n then: creation dialog shown with optional name and typename inputs\n\n req F-1.2:\n when: user confirms creation\n then: op:createMasonry called; Masonry + View objects written to ECHO\n\n req F-1.3:\n when: op:createMasonry completes\n then: new Masonry is navigable in the space navigator\n```\n\n```mdl\nfeat F-2: View Masonry\n\n req F-2.1: MasonryContainer renders for both Article and Section surface roles.\n\n req F-2.2:\n when: MasonryContainer mounts\n then: ECHO query derived from view.query runs; matching objects populate the grid\n\n req F-2.3:\n when: new objects matching the query are added by any peer\n then: grid updates reactively without a full remount\n tags: [collaborative]\n\n req F-2.4: Objects are displayed alphabetically by label within the grid.\n```\n\n```mdl\nfeat F-3: Search\n\n req F-3.1:\n when: user types in the search bar\n then: visible cards filtered client-side to those whose label matches\n\n req F-3.2:\n when: search input is cleared\n then: all query results are shown again\n\n req F-3.3: Search does not modify the underlying ECHO query or View.\n```\n\n```mdl\nfeat F-4: Card Surface Delegation\n\n req F-4.1:\n when: an Item is rendered\n then: AppSurface.Card surface is opened for the object, allowing other\n plugins to supply rich card body content\n\n req F-4.2:\n when: no plugin provides a card body for an object type\n then: card renders with header only (no body content)\n\n req F-4.3:\n when: user opens the context menu (\u22EE) on a card\n then: object menu items contributed by other plugins are shown\n```\n\n```mdl\nfeat F-5: Schema Resolution\n\n req F-5.1:\n when: MasonryContainer resolves cardSchema\n then: static schemas (from AppCapabilities.Schema) are checked first\n\n req F-5.2:\n when: typename is not found in static schemas\n then: db.schemaRegistry is queried reactively until the schema appears\n\n req F-5.3:\n when: cardSchema changes\n then: ECHO query filter is updated and the grid re-renders\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Plugin modules activate\n given: MasonryPlugin added to a Composer test app\n then:\n - CreateObject module is active\n - schema module is active\n - ReactSurface module is active\n```\n\n```mdl\ntest T-2: Create masonry object\n given: a space with a typed schema (e.g. Note)\n when: op:createMasonry is invoked with typename = Note's typename\n then:\n - a Masonry object exists in the space\n - Masonry.view references a View whose query matches Note typename\n```\n\n```mdl\ntest T-3: Grid renders query results\n given: a Masonry backed by a View querying Note objects, 3 Notes in the space\n when: MasonryContainer mounts\n then: 3 Item tiles visible, each showing the Note's label\n```\n\n```mdl\ntest T-4: Search filters cards\n given: MasonryContainer showing [\"Alpha\", \"Beta\", \"Gamma\"]\n when: user types \"al\" in the search bar\n then: only \"Alpha\" is visible\n```\n\n```mdl\ntest T-5: Search clear restores all cards\n given: search bar contains \"al\", only \"Alpha\" visible\n when: user clears the search input\n then: all 3 cards are visible again\n```\n\n```mdl\ntest T-6: Reactive update on peer write\n given: MasonryContainer open, query returning 2 objects\n when: a peer writes a third matching object to ECHO\n then: grid shows 3 tiles without a page reload\n tags: [collaborative]\n```\n\n```mdl\ntest T-7: Card body delegated to Surface\n given: a plugin that provides AppSurface.Card for Note objects\n when: a Note appears in the masonry grid\n then: the Note card renders the plugin-provided body below the Card.Toolbar\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n"],
5
+ "mappings": ";;;AAIA,SAASA,cAAc;AACvB,SAASC,iBAAiB;AAE1B,SAASC,cAAcC,oBAAoB;AAC3C,SAASC,YAAY;AACrB,SAASC,oBAAoB;AAC7B,SAASC,eAAe;;;ACVxB;;;ADeO,IAAMC,gBAAgBC,OAAOC,OAAOC,IAAAA,EAAMC,KAC/CC,UAAUC,sBAAsB;EAAEC,UAAUC;AAAa,CAAA,GACzDH,UAAUI,gBAAgB;EAAEC,QAAQ;IAACC,QAAQA;;AAAS,CAAA,GACtDN,UAAUO,iBAAiB;EAAEL,UAAUM;AAAa,CAAA,GACpDR,UAAUS,sBAAsB;EAAEC;AAAa,CAAA,GAC/CV,UAAUW,qBAAqB;EAC7BC,OAAO;IAAEC,UAAUf,KAAKgB;IAAIC,MAAM;IAAcC,SAASC;IAAYC,UAAU;EAAoB;AACrG,CAAA,GACAtB,OAAOuB,IAAI;AAGb,IAAA,wBAAexB;",
6
+ "names": ["Plugin", "AppPlugin", "CreateObject", "ReactSurface", "meta", "translations", "Masonry", "MasonryPlugin", "Plugin", "define", "meta", "pipe", "AppPlugin", "addCreateObjectModule", "activate", "CreateObject", "addSchemaModule", "schema", "Masonry", "addSurfaceModule", "ReactSurface", "addTranslationsModule", "translations", "addPluginAssetModule", "asset", "pluginId", "id", "path", "content", "pluginSpec", "mimeType", "make"]
7
+ }