@json-render/react 0.0.1
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.
- package/README.md +238 -0
- package/dist/index.d.mts +315 -0
- package/dist/index.d.ts +315 -0
- package/dist/index.js +796 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +769 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ActionProvider: () => ActionProvider,
|
|
34
|
+
ConfirmDialog: () => ConfirmDialog,
|
|
35
|
+
DataProvider: () => DataProvider,
|
|
36
|
+
JSONUIProvider: () => JSONUIProvider,
|
|
37
|
+
Renderer: () => Renderer,
|
|
38
|
+
ValidationProvider: () => ValidationProvider,
|
|
39
|
+
VisibilityProvider: () => VisibilityProvider,
|
|
40
|
+
createRendererFromCatalog: () => createRendererFromCatalog,
|
|
41
|
+
flatToTree: () => flatToTree,
|
|
42
|
+
useAction: () => useAction,
|
|
43
|
+
useActions: () => useActions,
|
|
44
|
+
useData: () => useData,
|
|
45
|
+
useDataBinding: () => useDataBinding,
|
|
46
|
+
useDataValue: () => useDataValue,
|
|
47
|
+
useFieldValidation: () => useFieldValidation,
|
|
48
|
+
useIsVisible: () => useIsVisible,
|
|
49
|
+
useUIStream: () => useUIStream,
|
|
50
|
+
useValidation: () => useValidation,
|
|
51
|
+
useVisibility: () => useVisibility
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
54
|
+
|
|
55
|
+
// src/contexts/data.tsx
|
|
56
|
+
var import_react = require("react");
|
|
57
|
+
var import_core = require("@json-render/core");
|
|
58
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
59
|
+
var DataContext = (0, import_react.createContext)(null);
|
|
60
|
+
function DataProvider({
|
|
61
|
+
initialData = {},
|
|
62
|
+
authState,
|
|
63
|
+
onDataChange,
|
|
64
|
+
children
|
|
65
|
+
}) {
|
|
66
|
+
const [data, setData] = (0, import_react.useState)(initialData);
|
|
67
|
+
const get = (0, import_react.useCallback)(
|
|
68
|
+
(path) => (0, import_core.getByPath)(data, path),
|
|
69
|
+
[data]
|
|
70
|
+
);
|
|
71
|
+
const set = (0, import_react.useCallback)(
|
|
72
|
+
(path, value2) => {
|
|
73
|
+
setData((prev) => {
|
|
74
|
+
const next = { ...prev };
|
|
75
|
+
(0, import_core.setByPath)(next, path, value2);
|
|
76
|
+
return next;
|
|
77
|
+
});
|
|
78
|
+
onDataChange?.(path, value2);
|
|
79
|
+
},
|
|
80
|
+
[onDataChange]
|
|
81
|
+
);
|
|
82
|
+
const update = (0, import_react.useCallback)(
|
|
83
|
+
(updates) => {
|
|
84
|
+
setData((prev) => {
|
|
85
|
+
const next = { ...prev };
|
|
86
|
+
for (const [path, value2] of Object.entries(updates)) {
|
|
87
|
+
(0, import_core.setByPath)(next, path, value2);
|
|
88
|
+
onDataChange?.(path, value2);
|
|
89
|
+
}
|
|
90
|
+
return next;
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
[onDataChange]
|
|
94
|
+
);
|
|
95
|
+
const value = (0, import_react.useMemo)(
|
|
96
|
+
() => ({
|
|
97
|
+
data,
|
|
98
|
+
authState,
|
|
99
|
+
get,
|
|
100
|
+
set,
|
|
101
|
+
update
|
|
102
|
+
}),
|
|
103
|
+
[data, authState, get, set, update]
|
|
104
|
+
);
|
|
105
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataContext.Provider, { value, children });
|
|
106
|
+
}
|
|
107
|
+
function useData() {
|
|
108
|
+
const ctx = (0, import_react.useContext)(DataContext);
|
|
109
|
+
if (!ctx) {
|
|
110
|
+
throw new Error("useData must be used within a DataProvider");
|
|
111
|
+
}
|
|
112
|
+
return ctx;
|
|
113
|
+
}
|
|
114
|
+
function useDataValue(path) {
|
|
115
|
+
const { get } = useData();
|
|
116
|
+
return get(path);
|
|
117
|
+
}
|
|
118
|
+
function useDataBinding(path) {
|
|
119
|
+
const { get, set } = useData();
|
|
120
|
+
const value = get(path);
|
|
121
|
+
const setValue = (0, import_react.useCallback)(
|
|
122
|
+
(newValue) => set(path, newValue),
|
|
123
|
+
[path, set]
|
|
124
|
+
);
|
|
125
|
+
return [value, setValue];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/contexts/visibility.tsx
|
|
129
|
+
var import_react2 = require("react");
|
|
130
|
+
var import_core2 = require("@json-render/core");
|
|
131
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
132
|
+
var VisibilityContext = (0, import_react2.createContext)(null);
|
|
133
|
+
function VisibilityProvider({ children }) {
|
|
134
|
+
const { data, authState } = useData();
|
|
135
|
+
const ctx = (0, import_react2.useMemo)(
|
|
136
|
+
() => ({
|
|
137
|
+
dataModel: data,
|
|
138
|
+
authState
|
|
139
|
+
}),
|
|
140
|
+
[data, authState]
|
|
141
|
+
);
|
|
142
|
+
const isVisible = (0, import_react2.useMemo)(
|
|
143
|
+
() => (condition) => (0, import_core2.evaluateVisibility)(condition, ctx),
|
|
144
|
+
[ctx]
|
|
145
|
+
);
|
|
146
|
+
const value = (0, import_react2.useMemo)(
|
|
147
|
+
() => ({ isVisible, ctx }),
|
|
148
|
+
[isVisible, ctx]
|
|
149
|
+
);
|
|
150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VisibilityContext.Provider, { value, children });
|
|
151
|
+
}
|
|
152
|
+
function useVisibility() {
|
|
153
|
+
const ctx = (0, import_react2.useContext)(VisibilityContext);
|
|
154
|
+
if (!ctx) {
|
|
155
|
+
throw new Error("useVisibility must be used within a VisibilityProvider");
|
|
156
|
+
}
|
|
157
|
+
return ctx;
|
|
158
|
+
}
|
|
159
|
+
function useIsVisible(condition) {
|
|
160
|
+
const { isVisible } = useVisibility();
|
|
161
|
+
return isVisible(condition);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/contexts/actions.tsx
|
|
165
|
+
var import_react3 = require("react");
|
|
166
|
+
var import_core3 = require("@json-render/core");
|
|
167
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
168
|
+
var ActionContext = (0, import_react3.createContext)(null);
|
|
169
|
+
function ActionProvider({
|
|
170
|
+
handlers: initialHandlers = {},
|
|
171
|
+
navigate,
|
|
172
|
+
children
|
|
173
|
+
}) {
|
|
174
|
+
const { data, set } = useData();
|
|
175
|
+
const [handlers, setHandlers] = (0, import_react3.useState)(initialHandlers);
|
|
176
|
+
const [loadingActions, setLoadingActions] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
|
|
177
|
+
const [pendingConfirmation, setPendingConfirmation] = (0, import_react3.useState)(null);
|
|
178
|
+
const registerHandler = (0, import_react3.useCallback)((name, handler) => {
|
|
179
|
+
setHandlers((prev) => ({ ...prev, [name]: handler }));
|
|
180
|
+
}, []);
|
|
181
|
+
const execute = (0, import_react3.useCallback)(
|
|
182
|
+
async (action) => {
|
|
183
|
+
const resolved = (0, import_core3.resolveAction)(action, data);
|
|
184
|
+
const handler = handlers[resolved.name];
|
|
185
|
+
if (!handler) {
|
|
186
|
+
console.warn(`No handler registered for action: ${resolved.name}`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (resolved.confirm) {
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
setPendingConfirmation({
|
|
192
|
+
action: resolved,
|
|
193
|
+
handler,
|
|
194
|
+
resolve: () => {
|
|
195
|
+
setPendingConfirmation(null);
|
|
196
|
+
resolve();
|
|
197
|
+
},
|
|
198
|
+
reject: () => {
|
|
199
|
+
setPendingConfirmation(null);
|
|
200
|
+
reject(new Error("Action cancelled"));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}).then(async () => {
|
|
204
|
+
setLoadingActions((prev) => new Set(prev).add(resolved.name));
|
|
205
|
+
try {
|
|
206
|
+
await (0, import_core3.executeAction)({
|
|
207
|
+
action: resolved,
|
|
208
|
+
handler,
|
|
209
|
+
setData: set,
|
|
210
|
+
navigate,
|
|
211
|
+
executeAction: async (name) => {
|
|
212
|
+
const subAction = { name };
|
|
213
|
+
await execute(subAction);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
} finally {
|
|
217
|
+
setLoadingActions((prev) => {
|
|
218
|
+
const next = new Set(prev);
|
|
219
|
+
next.delete(resolved.name);
|
|
220
|
+
return next;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
setLoadingActions((prev) => new Set(prev).add(resolved.name));
|
|
226
|
+
try {
|
|
227
|
+
await (0, import_core3.executeAction)({
|
|
228
|
+
action: resolved,
|
|
229
|
+
handler,
|
|
230
|
+
setData: set,
|
|
231
|
+
navigate,
|
|
232
|
+
executeAction: async (name) => {
|
|
233
|
+
const subAction = { name };
|
|
234
|
+
await execute(subAction);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
} finally {
|
|
238
|
+
setLoadingActions((prev) => {
|
|
239
|
+
const next = new Set(prev);
|
|
240
|
+
next.delete(resolved.name);
|
|
241
|
+
return next;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
[data, handlers, set, navigate]
|
|
246
|
+
);
|
|
247
|
+
const confirm = (0, import_react3.useCallback)(() => {
|
|
248
|
+
pendingConfirmation?.resolve();
|
|
249
|
+
}, [pendingConfirmation]);
|
|
250
|
+
const cancel = (0, import_react3.useCallback)(() => {
|
|
251
|
+
pendingConfirmation?.reject();
|
|
252
|
+
}, [pendingConfirmation]);
|
|
253
|
+
const value = (0, import_react3.useMemo)(
|
|
254
|
+
() => ({
|
|
255
|
+
handlers,
|
|
256
|
+
loadingActions,
|
|
257
|
+
pendingConfirmation,
|
|
258
|
+
execute,
|
|
259
|
+
confirm,
|
|
260
|
+
cancel,
|
|
261
|
+
registerHandler
|
|
262
|
+
}),
|
|
263
|
+
[handlers, loadingActions, pendingConfirmation, execute, confirm, cancel, registerHandler]
|
|
264
|
+
);
|
|
265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActionContext.Provider, { value, children });
|
|
266
|
+
}
|
|
267
|
+
function useActions() {
|
|
268
|
+
const ctx = (0, import_react3.useContext)(ActionContext);
|
|
269
|
+
if (!ctx) {
|
|
270
|
+
throw new Error("useActions must be used within an ActionProvider");
|
|
271
|
+
}
|
|
272
|
+
return ctx;
|
|
273
|
+
}
|
|
274
|
+
function useAction(action) {
|
|
275
|
+
const { execute, loadingActions } = useActions();
|
|
276
|
+
const isLoading = loadingActions.has(action.name);
|
|
277
|
+
const executeAction2 = (0, import_react3.useCallback)(() => execute(action), [execute, action]);
|
|
278
|
+
return { execute: executeAction2, isLoading };
|
|
279
|
+
}
|
|
280
|
+
function ConfirmDialog({ confirm, onConfirm, onCancel }) {
|
|
281
|
+
const isDanger = confirm.variant === "danger";
|
|
282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
283
|
+
"div",
|
|
284
|
+
{
|
|
285
|
+
style: {
|
|
286
|
+
position: "fixed",
|
|
287
|
+
inset: 0,
|
|
288
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
289
|
+
display: "flex",
|
|
290
|
+
alignItems: "center",
|
|
291
|
+
justifyContent: "center",
|
|
292
|
+
zIndex: 50
|
|
293
|
+
},
|
|
294
|
+
onClick: onCancel,
|
|
295
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
296
|
+
"div",
|
|
297
|
+
{
|
|
298
|
+
style: {
|
|
299
|
+
backgroundColor: "white",
|
|
300
|
+
borderRadius: "8px",
|
|
301
|
+
padding: "24px",
|
|
302
|
+
maxWidth: "400px",
|
|
303
|
+
width: "100%",
|
|
304
|
+
boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
|
|
305
|
+
},
|
|
306
|
+
onClick: (e) => e.stopPropagation(),
|
|
307
|
+
children: [
|
|
308
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
309
|
+
"h3",
|
|
310
|
+
{
|
|
311
|
+
style: {
|
|
312
|
+
margin: "0 0 8px 0",
|
|
313
|
+
fontSize: "18px",
|
|
314
|
+
fontWeight: 600
|
|
315
|
+
},
|
|
316
|
+
children: confirm.title
|
|
317
|
+
}
|
|
318
|
+
),
|
|
319
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
320
|
+
"p",
|
|
321
|
+
{
|
|
322
|
+
style: {
|
|
323
|
+
margin: "0 0 24px 0",
|
|
324
|
+
color: "#6b7280"
|
|
325
|
+
},
|
|
326
|
+
children: confirm.message
|
|
327
|
+
}
|
|
328
|
+
),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
330
|
+
"div",
|
|
331
|
+
{
|
|
332
|
+
style: {
|
|
333
|
+
display: "flex",
|
|
334
|
+
gap: "12px",
|
|
335
|
+
justifyContent: "flex-end"
|
|
336
|
+
},
|
|
337
|
+
children: [
|
|
338
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
339
|
+
"button",
|
|
340
|
+
{
|
|
341
|
+
onClick: onCancel,
|
|
342
|
+
style: {
|
|
343
|
+
padding: "8px 16px",
|
|
344
|
+
borderRadius: "6px",
|
|
345
|
+
border: "1px solid #d1d5db",
|
|
346
|
+
backgroundColor: "white",
|
|
347
|
+
cursor: "pointer"
|
|
348
|
+
},
|
|
349
|
+
children: confirm.cancelLabel ?? "Cancel"
|
|
350
|
+
}
|
|
351
|
+
),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
353
|
+
"button",
|
|
354
|
+
{
|
|
355
|
+
onClick: onConfirm,
|
|
356
|
+
style: {
|
|
357
|
+
padding: "8px 16px",
|
|
358
|
+
borderRadius: "6px",
|
|
359
|
+
border: "none",
|
|
360
|
+
backgroundColor: isDanger ? "#dc2626" : "#3b82f6",
|
|
361
|
+
color: "white",
|
|
362
|
+
cursor: "pointer"
|
|
363
|
+
},
|
|
364
|
+
children: confirm.confirmLabel ?? "Confirm"
|
|
365
|
+
}
|
|
366
|
+
)
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/contexts/validation.tsx
|
|
378
|
+
var import_react4 = __toESM(require("react"));
|
|
379
|
+
var import_core4 = require("@json-render/core");
|
|
380
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
381
|
+
var ValidationContext = (0, import_react4.createContext)(null);
|
|
382
|
+
function ValidationProvider({
|
|
383
|
+
customFunctions = {},
|
|
384
|
+
children
|
|
385
|
+
}) {
|
|
386
|
+
const { data, authState } = useData();
|
|
387
|
+
const [fieldStates, setFieldStates] = (0, import_react4.useState)({});
|
|
388
|
+
const [fieldConfigs, setFieldConfigs] = (0, import_react4.useState)({});
|
|
389
|
+
const registerField = (0, import_react4.useCallback)((path, config) => {
|
|
390
|
+
setFieldConfigs((prev) => ({ ...prev, [path]: config }));
|
|
391
|
+
}, []);
|
|
392
|
+
const validate = (0, import_react4.useCallback)(
|
|
393
|
+
(path, config) => {
|
|
394
|
+
const value2 = data[path.split("/").filter(Boolean).join(".")];
|
|
395
|
+
const result = (0, import_core4.runValidation)(config, {
|
|
396
|
+
value: value2,
|
|
397
|
+
dataModel: data,
|
|
398
|
+
customFunctions,
|
|
399
|
+
authState
|
|
400
|
+
});
|
|
401
|
+
setFieldStates((prev) => ({
|
|
402
|
+
...prev,
|
|
403
|
+
[path]: {
|
|
404
|
+
touched: prev[path]?.touched ?? true,
|
|
405
|
+
validated: true,
|
|
406
|
+
result
|
|
407
|
+
}
|
|
408
|
+
}));
|
|
409
|
+
return result;
|
|
410
|
+
},
|
|
411
|
+
[data, customFunctions, authState]
|
|
412
|
+
);
|
|
413
|
+
const touch = (0, import_react4.useCallback)((path) => {
|
|
414
|
+
setFieldStates((prev) => ({
|
|
415
|
+
...prev,
|
|
416
|
+
[path]: {
|
|
417
|
+
...prev[path],
|
|
418
|
+
touched: true,
|
|
419
|
+
validated: prev[path]?.validated ?? false,
|
|
420
|
+
result: prev[path]?.result ?? null
|
|
421
|
+
}
|
|
422
|
+
}));
|
|
423
|
+
}, []);
|
|
424
|
+
const clear = (0, import_react4.useCallback)((path) => {
|
|
425
|
+
setFieldStates((prev) => {
|
|
426
|
+
const { [path]: _, ...rest } = prev;
|
|
427
|
+
return rest;
|
|
428
|
+
});
|
|
429
|
+
}, []);
|
|
430
|
+
const validateAll = (0, import_react4.useCallback)(() => {
|
|
431
|
+
let allValid = true;
|
|
432
|
+
for (const [path, config] of Object.entries(fieldConfigs)) {
|
|
433
|
+
const result = validate(path, config);
|
|
434
|
+
if (!result.valid) {
|
|
435
|
+
allValid = false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return allValid;
|
|
439
|
+
}, [fieldConfigs, validate]);
|
|
440
|
+
const value = (0, import_react4.useMemo)(
|
|
441
|
+
() => ({
|
|
442
|
+
customFunctions,
|
|
443
|
+
fieldStates,
|
|
444
|
+
validate,
|
|
445
|
+
touch,
|
|
446
|
+
clear,
|
|
447
|
+
validateAll,
|
|
448
|
+
registerField
|
|
449
|
+
}),
|
|
450
|
+
[customFunctions, fieldStates, validate, touch, clear, validateAll, registerField]
|
|
451
|
+
);
|
|
452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ValidationContext.Provider, { value, children });
|
|
453
|
+
}
|
|
454
|
+
function useValidation() {
|
|
455
|
+
const ctx = (0, import_react4.useContext)(ValidationContext);
|
|
456
|
+
if (!ctx) {
|
|
457
|
+
throw new Error("useValidation must be used within a ValidationProvider");
|
|
458
|
+
}
|
|
459
|
+
return ctx;
|
|
460
|
+
}
|
|
461
|
+
function useFieldValidation(path, config) {
|
|
462
|
+
const {
|
|
463
|
+
fieldStates,
|
|
464
|
+
validate: validateField,
|
|
465
|
+
touch: touchField,
|
|
466
|
+
clear: clearField,
|
|
467
|
+
registerField
|
|
468
|
+
} = useValidation();
|
|
469
|
+
import_react4.default.useEffect(() => {
|
|
470
|
+
if (config) {
|
|
471
|
+
registerField(path, config);
|
|
472
|
+
}
|
|
473
|
+
}, [path, config, registerField]);
|
|
474
|
+
const state = fieldStates[path] ?? {
|
|
475
|
+
touched: false,
|
|
476
|
+
validated: false,
|
|
477
|
+
result: null
|
|
478
|
+
};
|
|
479
|
+
const validate = (0, import_react4.useCallback)(
|
|
480
|
+
() => validateField(path, config ?? { checks: [] }),
|
|
481
|
+
[path, config, validateField]
|
|
482
|
+
);
|
|
483
|
+
const touch = (0, import_react4.useCallback)(() => touchField(path), [path, touchField]);
|
|
484
|
+
const clear = (0, import_react4.useCallback)(() => clearField(path), [path, clearField]);
|
|
485
|
+
return {
|
|
486
|
+
state,
|
|
487
|
+
validate,
|
|
488
|
+
touch,
|
|
489
|
+
clear,
|
|
490
|
+
errors: state.result?.errors ?? [],
|
|
491
|
+
isValid: state.result?.valid ?? true
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/renderer.tsx
|
|
496
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
497
|
+
function ElementRenderer({
|
|
498
|
+
element,
|
|
499
|
+
tree,
|
|
500
|
+
registry,
|
|
501
|
+
loading,
|
|
502
|
+
fallback
|
|
503
|
+
}) {
|
|
504
|
+
const isVisible = useIsVisible(element.visible);
|
|
505
|
+
const { execute } = useActions();
|
|
506
|
+
if (!isVisible) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
const Component = registry[element.type] ?? fallback;
|
|
510
|
+
if (!Component) {
|
|
511
|
+
console.warn(`No renderer for component type: ${element.type}`);
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
const children = element.children?.map((childKey) => {
|
|
515
|
+
const childElement = tree.elements[childKey];
|
|
516
|
+
if (!childElement) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
520
|
+
ElementRenderer,
|
|
521
|
+
{
|
|
522
|
+
element: childElement,
|
|
523
|
+
tree,
|
|
524
|
+
registry,
|
|
525
|
+
loading,
|
|
526
|
+
fallback
|
|
527
|
+
},
|
|
528
|
+
childKey
|
|
529
|
+
);
|
|
530
|
+
});
|
|
531
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
532
|
+
Component,
|
|
533
|
+
{
|
|
534
|
+
element,
|
|
535
|
+
onAction: execute,
|
|
536
|
+
loading,
|
|
537
|
+
children
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
function Renderer({ tree, registry, loading, fallback }) {
|
|
542
|
+
if (!tree || !tree.root) {
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
const rootElement = tree.elements[tree.root];
|
|
546
|
+
if (!rootElement) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
550
|
+
ElementRenderer,
|
|
551
|
+
{
|
|
552
|
+
element: rootElement,
|
|
553
|
+
tree,
|
|
554
|
+
registry,
|
|
555
|
+
loading,
|
|
556
|
+
fallback
|
|
557
|
+
}
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
function JSONUIProvider({
|
|
561
|
+
registry,
|
|
562
|
+
initialData,
|
|
563
|
+
authState,
|
|
564
|
+
actionHandlers,
|
|
565
|
+
navigate,
|
|
566
|
+
validationFunctions,
|
|
567
|
+
onDataChange,
|
|
568
|
+
children
|
|
569
|
+
}) {
|
|
570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
571
|
+
DataProvider,
|
|
572
|
+
{
|
|
573
|
+
initialData,
|
|
574
|
+
authState,
|
|
575
|
+
onDataChange,
|
|
576
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VisibilityProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ActionProvider, { handlers: actionHandlers, navigate, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ValidationProvider, { customFunctions: validationFunctions, children: [
|
|
577
|
+
children,
|
|
578
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConfirmationDialogManager, {})
|
|
579
|
+
] }) }) })
|
|
580
|
+
}
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
function ConfirmationDialogManager() {
|
|
584
|
+
const { pendingConfirmation, confirm, cancel } = useActions();
|
|
585
|
+
if (!pendingConfirmation?.action.confirm) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
589
|
+
ConfirmDialog,
|
|
590
|
+
{
|
|
591
|
+
confirm: pendingConfirmation.action.confirm,
|
|
592
|
+
onConfirm: confirm,
|
|
593
|
+
onCancel: cancel
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
function createRendererFromCatalog(_catalog, registry) {
|
|
598
|
+
return function CatalogRenderer(props) {
|
|
599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Renderer, { ...props, registry });
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/hooks.ts
|
|
604
|
+
var import_react5 = require("react");
|
|
605
|
+
var import_core5 = require("@json-render/core");
|
|
606
|
+
function parsePatchLine(line) {
|
|
607
|
+
try {
|
|
608
|
+
const trimmed = line.trim();
|
|
609
|
+
if (!trimmed || trimmed.startsWith("//")) {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
return JSON.parse(trimmed);
|
|
613
|
+
} catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function applyPatch(tree, patch) {
|
|
618
|
+
const newTree = { ...tree, elements: { ...tree.elements } };
|
|
619
|
+
switch (patch.op) {
|
|
620
|
+
case "set":
|
|
621
|
+
case "add":
|
|
622
|
+
case "replace": {
|
|
623
|
+
if (patch.path === "/root") {
|
|
624
|
+
newTree.root = patch.value;
|
|
625
|
+
return newTree;
|
|
626
|
+
}
|
|
627
|
+
if (patch.path.startsWith("/elements/")) {
|
|
628
|
+
const pathParts = patch.path.slice("/elements/".length).split("/");
|
|
629
|
+
const elementKey = pathParts[0];
|
|
630
|
+
if (!elementKey) return newTree;
|
|
631
|
+
if (pathParts.length === 1) {
|
|
632
|
+
newTree.elements[elementKey] = patch.value;
|
|
633
|
+
} else {
|
|
634
|
+
const element = newTree.elements[elementKey];
|
|
635
|
+
if (element) {
|
|
636
|
+
const propPath = "/" + pathParts.slice(1).join("/");
|
|
637
|
+
const newElement = { ...element };
|
|
638
|
+
(0, import_core5.setByPath)(newElement, propPath, patch.value);
|
|
639
|
+
newTree.elements[elementKey] = newElement;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
case "remove": {
|
|
646
|
+
if (patch.path.startsWith("/elements/")) {
|
|
647
|
+
const elementKey = patch.path.slice("/elements/".length).split("/")[0];
|
|
648
|
+
if (elementKey) {
|
|
649
|
+
const { [elementKey]: _, ...rest } = newTree.elements;
|
|
650
|
+
newTree.elements = rest;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return newTree;
|
|
657
|
+
}
|
|
658
|
+
function useUIStream({
|
|
659
|
+
api,
|
|
660
|
+
onComplete,
|
|
661
|
+
onError
|
|
662
|
+
}) {
|
|
663
|
+
const [tree, setTree] = (0, import_react5.useState)(null);
|
|
664
|
+
const [isStreaming, setIsStreaming] = (0, import_react5.useState)(false);
|
|
665
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
666
|
+
const abortControllerRef = (0, import_react5.useRef)(null);
|
|
667
|
+
const clear = (0, import_react5.useCallback)(() => {
|
|
668
|
+
setTree(null);
|
|
669
|
+
setError(null);
|
|
670
|
+
}, []);
|
|
671
|
+
const send = (0, import_react5.useCallback)(
|
|
672
|
+
async (prompt, context) => {
|
|
673
|
+
abortControllerRef.current?.abort();
|
|
674
|
+
abortControllerRef.current = new AbortController();
|
|
675
|
+
setIsStreaming(true);
|
|
676
|
+
setError(null);
|
|
677
|
+
let currentTree = { root: "", elements: {} };
|
|
678
|
+
setTree(currentTree);
|
|
679
|
+
try {
|
|
680
|
+
const response = await fetch(api, {
|
|
681
|
+
method: "POST",
|
|
682
|
+
headers: { "Content-Type": "application/json" },
|
|
683
|
+
body: JSON.stringify({
|
|
684
|
+
prompt,
|
|
685
|
+
context,
|
|
686
|
+
currentTree
|
|
687
|
+
}),
|
|
688
|
+
signal: abortControllerRef.current.signal
|
|
689
|
+
});
|
|
690
|
+
if (!response.ok) {
|
|
691
|
+
throw new Error(`HTTP error: ${response.status}`);
|
|
692
|
+
}
|
|
693
|
+
const reader = response.body?.getReader();
|
|
694
|
+
if (!reader) {
|
|
695
|
+
throw new Error("No response body");
|
|
696
|
+
}
|
|
697
|
+
const decoder = new TextDecoder();
|
|
698
|
+
let buffer = "";
|
|
699
|
+
while (true) {
|
|
700
|
+
const { done, value } = await reader.read();
|
|
701
|
+
if (done) break;
|
|
702
|
+
buffer += decoder.decode(value, { stream: true });
|
|
703
|
+
const lines = buffer.split("\n");
|
|
704
|
+
buffer = lines.pop() ?? "";
|
|
705
|
+
for (const line of lines) {
|
|
706
|
+
const patch = parsePatchLine(line);
|
|
707
|
+
if (patch) {
|
|
708
|
+
currentTree = applyPatch(currentTree, patch);
|
|
709
|
+
setTree({ ...currentTree });
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (buffer.trim()) {
|
|
714
|
+
const patch = parsePatchLine(buffer);
|
|
715
|
+
if (patch) {
|
|
716
|
+
currentTree = applyPatch(currentTree, patch);
|
|
717
|
+
setTree({ ...currentTree });
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
onComplete?.(currentTree);
|
|
721
|
+
} catch (err) {
|
|
722
|
+
if (err.name === "AbortError") {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
726
|
+
setError(error2);
|
|
727
|
+
onError?.(error2);
|
|
728
|
+
} finally {
|
|
729
|
+
setIsStreaming(false);
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
[api, onComplete, onError]
|
|
733
|
+
);
|
|
734
|
+
(0, import_react5.useEffect)(() => {
|
|
735
|
+
return () => {
|
|
736
|
+
abortControllerRef.current?.abort();
|
|
737
|
+
};
|
|
738
|
+
}, []);
|
|
739
|
+
return {
|
|
740
|
+
tree,
|
|
741
|
+
isStreaming,
|
|
742
|
+
error,
|
|
743
|
+
send,
|
|
744
|
+
clear
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
function flatToTree(elements) {
|
|
748
|
+
const elementMap = {};
|
|
749
|
+
let root = "";
|
|
750
|
+
for (const element of elements) {
|
|
751
|
+
elementMap[element.key] = {
|
|
752
|
+
key: element.key,
|
|
753
|
+
type: element.type,
|
|
754
|
+
props: element.props,
|
|
755
|
+
children: [],
|
|
756
|
+
visible: element.visible
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
for (const element of elements) {
|
|
760
|
+
if (element.parentKey) {
|
|
761
|
+
const parent = elementMap[element.parentKey];
|
|
762
|
+
if (parent) {
|
|
763
|
+
if (!parent.children) {
|
|
764
|
+
parent.children = [];
|
|
765
|
+
}
|
|
766
|
+
parent.children.push(element.key);
|
|
767
|
+
}
|
|
768
|
+
} else {
|
|
769
|
+
root = element.key;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return { root, elements: elementMap };
|
|
773
|
+
}
|
|
774
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
775
|
+
0 && (module.exports = {
|
|
776
|
+
ActionProvider,
|
|
777
|
+
ConfirmDialog,
|
|
778
|
+
DataProvider,
|
|
779
|
+
JSONUIProvider,
|
|
780
|
+
Renderer,
|
|
781
|
+
ValidationProvider,
|
|
782
|
+
VisibilityProvider,
|
|
783
|
+
createRendererFromCatalog,
|
|
784
|
+
flatToTree,
|
|
785
|
+
useAction,
|
|
786
|
+
useActions,
|
|
787
|
+
useData,
|
|
788
|
+
useDataBinding,
|
|
789
|
+
useDataValue,
|
|
790
|
+
useFieldValidation,
|
|
791
|
+
useIsVisible,
|
|
792
|
+
useUIStream,
|
|
793
|
+
useValidation,
|
|
794
|
+
useVisibility
|
|
795
|
+
});
|
|
796
|
+
//# sourceMappingURL=index.js.map
|