You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Recreate the Vue named slots implementation from #322 and add regression coverage for raw registries, lazy slots, diagnostics, loading, repeat scope, and prompt output.
Co-authored-by: wotnak <wotnak@pm.me>
Copy file name to clipboardExpand all lines: apps/web/app/(main)/docs/api/vue/page.mdx
+25-4Lines changed: 25 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,7 +95,7 @@ store.set("/count", 1);
95
95
96
96
## defineRegistry
97
97
98
-
Create a type-safe component registry from a catalog. Components receive `props`, `children`, `emit`, `on`, and `loading` with catalog-inferred types.
98
+
Create a type-safe component registry from a catalog. Components receive `props`, `children`, `slots`, `emit`, `on`, and `loading` with catalog-inferred types.
99
99
100
100
When the catalog declares actions, the `actions` field is required. When the catalog has no actions (e.g. `actions: {}`), the field is optional. When passing stubs, any `async () => {}` is sufficient.
101
101
@@ -105,8 +105,12 @@ import { defineRegistry } from "@json-render/vue";
children?:VNode|VNode[]; // Rendered children (for container components)
148
+
slots:Slots; // Vue-native slot functions
144
149
emit: (event:string) =>void; // Emit a named event (always defined)
145
150
on: (event:string) =>EventHandle; // Get event handle with metadata
146
151
loading?:boolean;
@@ -154,6 +159,22 @@ interface EventHandle {
154
159
}
155
160
```
156
161
162
+
Use `children` for the default slot. For other slots declared by the catalog, add a top-level `slots` map to the spec element:
163
+
164
+
```json
165
+
{
166
+
"type": "Layout",
167
+
"props": {},
168
+
"children": ["main-content"],
169
+
"slots": {
170
+
"header": ["page-heading"],
171
+
"footer": ["page-actions"]
172
+
}
173
+
}
174
+
```
175
+
176
+
The component renders these regions with Vue's native slot functions: `slots.header?.()`, `slots.footer?.()`, and so on. `slots.default?.()` renders the spec's `children`; `children` is a convenience alias for that rendered result. In the JSON spec, keep default content in `children` rather than adding a `default` entry to `slots`.
177
+
157
178
Use `emit("press")` for simple event firing. Use `on("click")` when you need metadata like `shouldPreventDefault`:
Copy file name to clipboardExpand all lines: apps/web/app/(main)/docs/specs/page.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -206,7 +206,7 @@ Each element in the map has a consistent shape:
206
206
-`type` — Component type from your catalog
207
207
-`props` — Component properties
208
208
-`children` — Array of child element keys
209
-
-`slots`: Optional map of named slots to child element keys. Use `children` for the default slot. Named slot rendering is currently supported by `@json-render/react`.
209
+
-`slots`: Optional map of named slots to child element keys. Use `children` for the default slot. Named slot rendering is supported by `@json-render/react` and `@json-render/vue`.
Components are written using Vue's `h()` render function. `children` is a `VNode | VNode[]` — pass it directly to your container element.
57
+
Components are written using Vue's `h()` render function. The `slots` context uses Vue's native slot functions, so render a region with `slots.header?.()`. For convenience and React parity, `children` contains the already-rendered result of `slots.default?.()`.
57
58
58
59
`defineRegistry` conditionally requires the `actions` field only when the catalog declares actions. Catalogs with `actions: {}` can omit it entirely.
59
60
@@ -64,11 +65,12 @@ import { catalog } from "./catalog";
Use `children` for the default slot and the element's top-level `slots` object for other slot names declared by the catalog:
172
+
173
+
```typescript
174
+
Layout: ({ children, slots }) =>
175
+
h("div", null, [
176
+
h("header", null, slots.header?.()),
177
+
h("main", null, children),
178
+
h("footer", null, slots.footer?.()),
179
+
]),
180
+
```
181
+
182
+
The corresponding spec maps element keys to each region:
183
+
184
+
```json
185
+
{
186
+
"type": "Layout",
187
+
"props": {},
188
+
"children": ["main-content"],
189
+
"slots": {
190
+
"header": ["page-heading"],
191
+
"footer": ["page-actions"]
192
+
}
193
+
}
194
+
```
195
+
196
+
Registry components receive Vue-native slot functions and render them with `slots.header?.()`, `slots.footer?.()`, and so on. `slots.default?.()` renders the spec's `children`; `children` is an alias for that rendered result. In the JSON spec, keep default content in `children` rather than adding a `default` entry to `slots`.
197
+
166
198
## Providers
167
199
168
200
### StateProvider
@@ -366,11 +398,12 @@ The `setState`, `pushState`, `removeState`, and `validateForm` actions are built
366
398
When using `defineRegistry`, components receive these props via their render function:
367
399
368
400
```typescript
369
-
importtype { VNode } from"vue";
401
+
importtype { Slots, VNode } from"vue";
370
402
371
403
interfaceComponentContext<P> {
372
404
props:P; // Typed props from the catalog (expressions resolved)
373
405
children?:VNode|VNode[]; // Rendered children (for container components)
406
+
slots:Slots; // Vue-native slot functions
374
407
emit: (event:string) =>void; // Emit a named event (always defined)
375
408
on: (event:string) =>EventHandle; // Get event handle with metadata
376
409
loading?:boolean; // Whether the parent is loading
0 commit comments