Skip to content

Commit d944a21

Browse files
committed
update registry page
1 parent bba3872 commit d944a21

File tree

1 file changed

+139
-112
lines changed
  • apps/web/app/(main)/docs/registry

1 file changed

+139
-112
lines changed

apps/web/app/(main)/docs/registry/page.tsx

Lines changed: 139 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -14,82 +14,86 @@ export default function RegistryPage() {
1414
life.
1515
</p>
1616

17-
{/* Components Section */}
18-
<h2 className="text-xl font-semibold mt-12 mb-4">Component Registry</h2>
17+
{/* defineRegistry */}
18+
<h2 className="text-xl font-semibold mt-12 mb-4">defineRegistry</h2>
1919
<p className="text-sm text-muted-foreground mb-4">
20-
Create a registry that maps catalog component types to React components:
20+
Use <code>defineRegistry</code> to create a type-safe registry from your
21+
catalog. Pass your components, actions, or both in a single call:
2122
</p>
22-
<Code lang="tsx">{`import { useAction } from '@json-render/react';
23-
24-
const registry = {
25-
Card: ({ props, children }) => (
26-
<div className="card">
27-
<h2>{props.title}</h2>
28-
{props.description && <p>{props.description}</p>}
29-
{children}
30-
</div>
31-
),
32-
33-
Button: ({ props }) => {
34-
const executeAction = useAction(props.action);
35-
return (
36-
<button onClick={() => executeAction({})}>
23+
<Code lang="tsx">{`import { defineRegistry } from '@json-render/react';
24+
import { myCatalog } from './catalog';
25+
26+
export const { registry, handlers, executeAction } = defineRegistry(myCatalog, {
27+
components: {
28+
Card: ({ props, children }) => (
29+
<div className="card">
30+
<h2>{props.title}</h2>
31+
{props.description && <p>{props.description}</p>}
32+
{children}
33+
</div>
34+
),
35+
36+
Button: ({ props, onAction }) => (
37+
<button onClick={() => onAction?.({ name: props.action })}>
3738
{props.label}
3839
</button>
39-
);
40+
),
4041
},
41-
};`}</Code>
4242
43+
actions: {
44+
submit_form: async (params, setData) => {
45+
const res = await fetch('/api/submit', {
46+
method: 'POST',
47+
body: JSON.stringify(params),
48+
});
49+
const result = await res.json();
50+
setData((prev) => ({ ...prev, formResult: result }));
51+
},
52+
53+
export_data: async (params) => {
54+
const blob = await generateExport(params.format);
55+
downloadBlob(blob, \`export.\${params.format}\`);
56+
},
57+
},
58+
});`}</Code>
59+
60+
<p className="text-sm text-muted-foreground mt-4 mb-4">
61+
The returned object contains:
62+
</p>
63+
<ul className="list-disc list-inside text-sm text-muted-foreground mb-4 space-y-1">
64+
<li>
65+
<code>registry</code> - component registry for{" "}
66+
<code>{"<Renderer />"}</code>
67+
</li>
68+
<li>
69+
<code>handlers</code> - factory for ActionProvider-compatible handlers
70+
</li>
71+
<li>
72+
<code>executeAction</code> - imperative action dispatch (for use
73+
outside the React tree)
74+
</li>
75+
</ul>
76+
77+
{/* Component Props */}
4378
<h2 className="text-xl font-semibold mt-12 mb-4">Component Props</h2>
4479
<p className="text-sm text-muted-foreground mb-4">
45-
Each component receives these props:
80+
Each component in the registry receives a <code>ComponentContext</code>{" "}
81+
object:
4682
</p>
47-
<Code lang="typescript">{`interface ComponentProps<T = Record<string, unknown>> {
48-
props: T; // Component props from the spec
83+
<Code lang="typescript">{`interface ComponentContext {
84+
props: T; // Type-safe props from your catalog
4985
children?: React.ReactNode; // Rendered children (for slot components)
50-
}
51-
52-
// Type-safe props by extracting from your catalog
53-
type CardProps = ComponentProps<{
54-
title: string;
55-
description: string | null;
56-
}>;
57-
58-
// Use hooks for actions and data within components
59-
import { useAction, useDataValue } from '@json-render/react';`}</Code>
86+
onAction?: (action: ActionTrigger) => void; // Dispatch an action
87+
loading?: boolean; // Whether the renderer is in a loading state
88+
}`}</Code>
6089

61-
<h2 className="text-xl font-semibold mt-12 mb-4">Using Data Binding</h2>
62-
<p className="text-sm text-muted-foreground mb-4">
63-
Use hooks to read and write data:
90+
<p className="text-sm text-muted-foreground mt-4 mb-4">
91+
Props are automatically inferred from your catalog, so{" "}
92+
<code>props.title</code> is typed as <code>string</code> if your catalog
93+
defines it that way.
6494
</p>
65-
<Code lang="tsx">{`import { useDataValue, useDataBinding } from '@json-render/react';
66-
67-
const Metric = ({ props }) => {
68-
// Read-only value from data context
69-
const value = useDataValue(props.valuePath);
70-
71-
return (
72-
<div className="metric">
73-
<span className="label">{props.label}</span>
74-
<span className="value">{formatValue(value)}</span>
75-
</div>
76-
);
77-
};
7895

79-
const TextField = ({ props }) => {
80-
// Two-way binding to data context
81-
const [value, setValue] = useDataBinding(props.valuePath);
82-
83-
return (
84-
<input
85-
value={value || ''}
86-
onChange={(e) => setValue(e.target.value)}
87-
placeholder={props.placeholder}
88-
/>
89-
);
90-
};`}</Code>
91-
92-
{/* Actions Section */}
96+
{/* Action Handlers */}
9397
<h2 className="text-xl font-semibold mt-12 mb-4">Action Handlers</h2>
9498
<p className="text-sm text-muted-foreground mb-4">
9599
Instead of AI generating arbitrary code, it declares <em>intent</em> by
@@ -117,9 +121,6 @@ const catalog = defineCatalog(schema, {
117121
export_data: {
118122
params: z.object({
119123
format: z.enum(['csv', 'pdf', 'json']),
120-
filters: z.object({
121-
dateRange: z.string().nullable(),
122-
}).nullable(),
123124
}),
124125
},
125126
navigate: {
@@ -130,78 +131,104 @@ const catalog = defineCatalog(schema, {
130131
},
131132
});`}</Code>
132133

133-
<h3 className="text-lg font-medium mt-8 mb-3">ActionProvider</h3>
134+
<h3 className="text-lg font-medium mt-8 mb-3">
135+
Implementing Action Handlers
136+
</h3>
134137
<p className="text-sm text-muted-foreground mb-4">
135-
Provide action handlers to your app:
138+
Action handlers receive <code>(params, setData, data)</code> and are
139+
defined inside <code>defineRegistry</code>:
136140
</p>
137-
<Code lang="tsx">{`import { ActionProvider } from '@json-render/react';
138-
139-
function App() {
140-
const handlers = {
141-
submit_form: async (params) => {
141+
<Code lang="tsx">{`export const { handlers, executeAction } = defineRegistry(catalog, {
142+
actions: {
143+
submit_form: async (params, setData) => {
142144
const response = await fetch('/api/submit', {
143145
method: 'POST',
144146
body: JSON.stringify({ formId: params.formId }),
145147
});
146-
return response.json();
148+
const result = await response.json();
149+
setData((prev) => ({ ...prev, formResult: result }));
147150
},
148-
151+
149152
export_data: async (params) => {
150-
const blob = await generateExport(params.format, params.filters);
153+
const blob = await generateExport(params.format);
151154
downloadBlob(blob, \`export.\${params.format}\`);
152155
},
153-
156+
154157
navigate: (params) => {
155158
window.location.href = params.url;
156159
},
157-
};
160+
},
161+
});`}</Code>
158162

159-
return (
160-
<ActionProvider handlers={handlers}>
161-
{/* Your UI */}
162-
</ActionProvider>
163-
);
164-
}`}</Code>
163+
{/* Using Data Binding */}
164+
<h2 className="text-xl font-semibold mt-12 mb-4">Using Data Binding</h2>
165+
<p className="text-sm text-muted-foreground mb-4">
166+
Use hooks inside your registry components to read and write data:
167+
</p>
168+
<Code lang="tsx">{`import { useData } from '@json-render/react';
169+
import { getByPath } from '@json-render/core';
165170
166-
<h3 className="text-lg font-medium mt-8 mb-3">
167-
Using Actions in Components
168-
</h3>
169-
<Code lang="tsx">{`import { useAction } from '@json-render/react';
171+
// Inside defineRegistry components:
172+
173+
Metric: ({ props }) => {
174+
const { data } = useData();
175+
const value = getByPath(data, props.valuePath);
170176
171-
// Using the useAction hook (recommended)
172-
const Button = ({ props }) => {
173-
const executeAction = useAction(props.action);
174-
175177
return (
176-
<button onClick={() => executeAction({})}>
177-
{props.label}
178-
</button>
178+
<div className="metric">
179+
<span className="label">{props.label}</span>
180+
<span className="value">{formatValue(value)}</span>
181+
</div>
179182
);
180-
};
183+
},
184+
185+
TextField: ({ props }) => {
186+
const { data, set } = useData();
187+
const value = getByPath(data, props.valuePath) as string;
181188
182-
// Or for standalone use
183-
function SubmitButton() {
184-
const submitForm = useAction('submit_form');
185-
186189
return (
187-
<button onClick={() => submitForm({ formId: 'contact' })}>
188-
Submit
189-
</button>
190+
<input
191+
value={value || ''}
192+
onChange={(e) => set(props.valuePath, e.target.value)}
193+
placeholder={props.placeholder}
194+
/>
190195
);
191-
}`}</Code>
196+
},`}</Code>
192197

193198
{/* Renderer Section */}
194199
<h2 className="text-xl font-semibold mt-12 mb-4">Using the Renderer</h2>
195-
<Code lang="tsx">{`import { Renderer, ActionProvider } from '@json-render/react';
200+
<p className="text-sm text-muted-foreground mb-4">
201+
Wire everything together with providers and the{" "}
202+
<code>{"<Renderer />"}</code> component:
203+
</p>
204+
<Code lang="tsx">{`import { useMemo, useRef } from 'react';
205+
import {
206+
Renderer,
207+
DataProvider,
208+
VisibilityProvider,
209+
ActionProvider,
210+
} from '@json-render/react';
211+
import { registry, handlers } from './registry';
212+
213+
function App({ spec, data, setData }) {
214+
const dataRef = useRef(data);
215+
const setDataRef = useRef(setData);
216+
dataRef.current = data;
217+
setDataRef.current = setData;
218+
219+
const actionHandlers = useMemo(
220+
() => handlers(() => setDataRef.current, () => dataRef.current),
221+
[],
222+
);
196223
197-
function App() {
198224
return (
199-
<ActionProvider handlers={actionHandlers}>
200-
<Renderer
201-
spec={uiSpec}
202-
registry={registry}
203-
/>
204-
</ActionProvider>
225+
<DataProvider initialData={data}>
226+
<VisibilityProvider>
227+
<ActionProvider handlers={actionHandlers}>
228+
<Renderer spec={spec} registry={registry} />
229+
</ActionProvider>
230+
</VisibilityProvider>
231+
</DataProvider>
205232
);
206233
}`}</Code>
207234

0 commit comments

Comments
 (0)