@json-render/react 0.1.0 → 0.4.0

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/dist/index.mjs CHANGED
@@ -1,10 +1,17 @@
1
+ import {
2
+ elementTreeSchema,
3
+ schema
4
+ } from "./chunk-IGPI5WNB.mjs";
5
+
1
6
  // src/contexts/data.tsx
2
7
  import {
3
8
  createContext,
4
9
  useContext,
5
10
  useState,
6
11
  useCallback,
7
- useMemo
12
+ useMemo,
13
+ useEffect,
14
+ useRef
8
15
  } from "react";
9
16
  import {
10
17
  getByPath,
@@ -19,10 +26,17 @@ function DataProvider({
19
26
  children
20
27
  }) {
21
28
  const [data, setData] = useState(initialData);
22
- const get = useCallback(
23
- (path) => getByPath(data, path),
24
- [data]
25
- );
29
+ const initialDataJsonRef = useRef(JSON.stringify(initialData));
30
+ useEffect(() => {
31
+ const newJson = JSON.stringify(initialData);
32
+ if (newJson !== initialDataJsonRef.current) {
33
+ initialDataJsonRef.current = newJson;
34
+ if (initialData && Object.keys(initialData).length > 0) {
35
+ setData((prev) => ({ ...prev, ...initialData }));
36
+ }
37
+ }
38
+ }, [initialData]);
39
+ const get = useCallback((path) => getByPath(data, path), [data]);
26
40
  const set = useCallback(
27
41
  (path, value2) => {
28
42
  setData((prev) => {
@@ -67,12 +81,12 @@ function useData() {
67
81
  return ctx;
68
82
  }
69
83
  function useDataValue(path) {
70
- const { get } = useData();
71
- return get(path);
84
+ const { data } = useData();
85
+ return getByPath(data, path);
72
86
  }
73
87
  function useDataBinding(path) {
74
- const { get, set } = useData();
75
- const value = get(path);
88
+ const { data, set } = useData();
89
+ const value = getByPath(data, path);
76
90
  const setValue = useCallback(
77
91
  (newValue) => set(path, newValue),
78
92
  [path, set]
@@ -81,7 +95,11 @@ function useDataBinding(path) {
81
95
  }
82
96
 
83
97
  // src/contexts/visibility.tsx
84
- import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
98
+ import {
99
+ createContext as createContext2,
100
+ useContext as useContext2,
101
+ useMemo as useMemo2
102
+ } from "react";
85
103
  import {
86
104
  evaluateVisibility
87
105
  } from "@json-render/core";
@@ -141,9 +159,12 @@ function ActionProvider({
141
159
  const [handlers, setHandlers] = useState2(initialHandlers);
142
160
  const [loadingActions, setLoadingActions] = useState2(/* @__PURE__ */ new Set());
143
161
  const [pendingConfirmation, setPendingConfirmation] = useState2(null);
144
- const registerHandler = useCallback2((name, handler) => {
145
- setHandlers((prev) => ({ ...prev, [name]: handler }));
146
- }, []);
162
+ const registerHandler = useCallback2(
163
+ (name, handler) => {
164
+ setHandlers((prev) => ({ ...prev, [name]: handler }));
165
+ },
166
+ []
167
+ );
147
168
  const execute = useCallback2(
148
169
  async (action) => {
149
170
  const resolved = resolveAction(action, data);
@@ -226,7 +247,15 @@ function ActionProvider({
226
247
  cancel,
227
248
  registerHandler
228
249
  }),
229
- [handlers, loadingActions, pendingConfirmation, execute, confirm, cancel, registerHandler]
250
+ [
251
+ handlers,
252
+ loadingActions,
253
+ pendingConfirmation,
254
+ execute,
255
+ confirm,
256
+ cancel,
257
+ registerHandler
258
+ ]
230
259
  );
231
260
  return /* @__PURE__ */ jsx3(ActionContext.Provider, { value, children });
232
261
  }
@@ -243,7 +272,11 @@ function useAction(action) {
243
272
  const executeAction2 = useCallback2(() => execute(action), [execute, action]);
244
273
  return { execute: executeAction2, isLoading };
245
274
  }
246
- function ConfirmDialog({ confirm, onConfirm, onCancel }) {
275
+ function ConfirmDialog({
276
+ confirm,
277
+ onConfirm,
278
+ onCancel
279
+ }) {
247
280
  const isDanger = confirm.variant === "danger";
248
281
  return /* @__PURE__ */ jsx3(
249
282
  "div",
@@ -360,9 +393,12 @@ function ValidationProvider({
360
393
  const { data, authState } = useData();
361
394
  const [fieldStates, setFieldStates] = useState3({});
362
395
  const [fieldConfigs, setFieldConfigs] = useState3({});
363
- const registerField = useCallback3((path, config) => {
364
- setFieldConfigs((prev) => ({ ...prev, [path]: config }));
365
- }, []);
396
+ const registerField = useCallback3(
397
+ (path, config) => {
398
+ setFieldConfigs((prev) => ({ ...prev, [path]: config }));
399
+ },
400
+ []
401
+ );
366
402
  const validate = useCallback3(
367
403
  (path, config) => {
368
404
  const value2 = data[path.split("/").filter(Boolean).join(".")];
@@ -421,7 +457,15 @@ function ValidationProvider({
421
457
  validateAll,
422
458
  registerField
423
459
  }),
424
- [customFunctions, fieldStates, validate, touch, clear, validateAll, registerField]
460
+ [
461
+ customFunctions,
462
+ fieldStates,
463
+ validate,
464
+ touch,
465
+ clear,
466
+ validateAll,
467
+ registerField
468
+ ]
425
469
  );
426
470
  return /* @__PURE__ */ jsx4(ValidationContext.Provider, { value, children });
427
471
  }
@@ -470,7 +514,7 @@ function useFieldValidation(path, config) {
470
514
  import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
471
515
  function ElementRenderer({
472
516
  element,
473
- tree,
517
+ spec,
474
518
  registry,
475
519
  loading,
476
520
  fallback
@@ -486,7 +530,7 @@ function ElementRenderer({
486
530
  return null;
487
531
  }
488
532
  const children = element.children?.map((childKey) => {
489
- const childElement = tree.elements[childKey];
533
+ const childElement = spec.elements[childKey];
490
534
  if (!childElement) {
491
535
  return null;
492
536
  }
@@ -494,7 +538,7 @@ function ElementRenderer({
494
538
  ElementRenderer,
495
539
  {
496
540
  element: childElement,
497
- tree,
541
+ spec,
498
542
  registry,
499
543
  loading,
500
544
  fallback
@@ -502,21 +546,13 @@ function ElementRenderer({
502
546
  childKey
503
547
  );
504
548
  });
505
- return /* @__PURE__ */ jsx5(
506
- Component,
507
- {
508
- element,
509
- onAction: execute,
510
- loading,
511
- children
512
- }
513
- );
549
+ return /* @__PURE__ */ jsx5(Component, { element, onAction: execute, loading, children });
514
550
  }
515
- function Renderer({ tree, registry, loading, fallback }) {
516
- if (!tree || !tree.root) {
551
+ function Renderer({ spec, registry, loading, fallback }) {
552
+ if (!spec || !spec.root) {
517
553
  return null;
518
554
  }
519
- const rootElement = tree.elements[tree.root];
555
+ const rootElement = spec.elements[spec.root];
520
556
  if (!rootElement) {
521
557
  return null;
522
558
  }
@@ -524,7 +560,7 @@ function Renderer({ tree, registry, loading, fallback }) {
524
560
  ElementRenderer,
525
561
  {
526
562
  element: rootElement,
527
- tree,
563
+ spec,
528
564
  registry,
529
565
  loading,
530
566
  fallback
@@ -573,9 +609,49 @@ function createRendererFromCatalog(_catalog, registry) {
573
609
  return /* @__PURE__ */ jsx5(Renderer, { ...props, registry });
574
610
  };
575
611
  }
612
+ function createRenderer(catalog, components) {
613
+ const registry = components;
614
+ return function CatalogRenderer({
615
+ spec,
616
+ data,
617
+ onAction,
618
+ onDataChange,
619
+ loading,
620
+ authState,
621
+ fallback
622
+ }) {
623
+ const actionHandlers = onAction ? {
624
+ __default__: (params) => {
625
+ const actionName = params.__actionName__;
626
+ const actionParams = params.__actionParams__;
627
+ return onAction(actionName, actionParams);
628
+ }
629
+ } : void 0;
630
+ return /* @__PURE__ */ jsx5(
631
+ DataProvider,
632
+ {
633
+ initialData: data,
634
+ authState,
635
+ onDataChange,
636
+ children: /* @__PURE__ */ jsx5(VisibilityProvider, { children: /* @__PURE__ */ jsx5(ActionProvider, { handlers: actionHandlers, children: /* @__PURE__ */ jsxs2(ValidationProvider, { children: [
637
+ /* @__PURE__ */ jsx5(
638
+ Renderer,
639
+ {
640
+ spec,
641
+ registry,
642
+ loading,
643
+ fallback
644
+ }
645
+ ),
646
+ /* @__PURE__ */ jsx5(ConfirmationDialogManager, {})
647
+ ] }) }) })
648
+ }
649
+ );
650
+ };
651
+ }
576
652
 
577
653
  // src/hooks.ts
578
- import { useState as useState4, useCallback as useCallback4, useRef, useEffect } from "react";
654
+ import { useState as useState4, useCallback as useCallback4, useRef as useRef2, useEffect as useEffect2 } from "react";
579
655
  import { setByPath as setByPath2 } from "@json-render/core";
580
656
  function parsePatchLine(line) {
581
657
  try {
@@ -588,29 +664,33 @@ function parsePatchLine(line) {
588
664
  return null;
589
665
  }
590
666
  }
591
- function applyPatch(tree, patch) {
592
- const newTree = { ...tree, elements: { ...tree.elements } };
667
+ function applyPatch(spec, patch) {
668
+ const newSpec = { ...spec, elements: { ...spec.elements } };
593
669
  switch (patch.op) {
594
670
  case "set":
595
671
  case "add":
596
672
  case "replace": {
597
673
  if (patch.path === "/root") {
598
- newTree.root = patch.value;
599
- return newTree;
674
+ newSpec.root = patch.value;
675
+ return newSpec;
600
676
  }
601
677
  if (patch.path.startsWith("/elements/")) {
602
678
  const pathParts = patch.path.slice("/elements/".length).split("/");
603
679
  const elementKey = pathParts[0];
604
- if (!elementKey) return newTree;
680
+ if (!elementKey) return newSpec;
605
681
  if (pathParts.length === 1) {
606
- newTree.elements[elementKey] = patch.value;
682
+ newSpec.elements[elementKey] = patch.value;
607
683
  } else {
608
- const element = newTree.elements[elementKey];
684
+ const element = newSpec.elements[elementKey];
609
685
  if (element) {
610
686
  const propPath = "/" + pathParts.slice(1).join("/");
611
687
  const newElement = { ...element };
612
- setByPath2(newElement, propPath, patch.value);
613
- newTree.elements[elementKey] = newElement;
688
+ setByPath2(
689
+ newElement,
690
+ propPath,
691
+ patch.value
692
+ );
693
+ newSpec.elements[elementKey] = newElement;
614
694
  }
615
695
  }
616
696
  }
@@ -620,26 +700,26 @@ function applyPatch(tree, patch) {
620
700
  if (patch.path.startsWith("/elements/")) {
621
701
  const elementKey = patch.path.slice("/elements/".length).split("/")[0];
622
702
  if (elementKey) {
623
- const { [elementKey]: _, ...rest } = newTree.elements;
624
- newTree.elements = rest;
703
+ const { [elementKey]: _, ...rest } = newSpec.elements;
704
+ newSpec.elements = rest;
625
705
  }
626
706
  }
627
707
  break;
628
708
  }
629
709
  }
630
- return newTree;
710
+ return newSpec;
631
711
  }
632
712
  function useUIStream({
633
713
  api,
634
714
  onComplete,
635
715
  onError
636
716
  }) {
637
- const [tree, setTree] = useState4(null);
717
+ const [spec, setSpec] = useState4(null);
638
718
  const [isStreaming, setIsStreaming] = useState4(false);
639
719
  const [error, setError] = useState4(null);
640
- const abortControllerRef = useRef(null);
720
+ const abortControllerRef = useRef2(null);
641
721
  const clear = useCallback4(() => {
642
- setTree(null);
722
+ setSpec(null);
643
723
  setError(null);
644
724
  }, []);
645
725
  const send = useCallback4(
@@ -648,8 +728,9 @@ function useUIStream({
648
728
  abortControllerRef.current = new AbortController();
649
729
  setIsStreaming(true);
650
730
  setError(null);
651
- let currentTree = { root: "", elements: {} };
652
- setTree(currentTree);
731
+ const previousSpec = context?.previousSpec;
732
+ let currentSpec = previousSpec && previousSpec.root ? { ...previousSpec, elements: { ...previousSpec.elements } } : { root: "", elements: {} };
733
+ setSpec(currentSpec);
653
734
  try {
654
735
  const response = await fetch(api, {
655
736
  method: "POST",
@@ -657,12 +738,22 @@ function useUIStream({
657
738
  body: JSON.stringify({
658
739
  prompt,
659
740
  context,
660
- currentTree
741
+ currentSpec
661
742
  }),
662
743
  signal: abortControllerRef.current.signal
663
744
  });
664
745
  if (!response.ok) {
665
- throw new Error(`HTTP error: ${response.status}`);
746
+ let errorMessage = `HTTP error: ${response.status}`;
747
+ try {
748
+ const errorData = await response.json();
749
+ if (errorData.message) {
750
+ errorMessage = errorData.message;
751
+ } else if (errorData.error) {
752
+ errorMessage = errorData.error;
753
+ }
754
+ } catch {
755
+ }
756
+ throw new Error(errorMessage);
666
757
  }
667
758
  const reader = response.body?.getReader();
668
759
  if (!reader) {
@@ -679,19 +770,19 @@ function useUIStream({
679
770
  for (const line of lines) {
680
771
  const patch = parsePatchLine(line);
681
772
  if (patch) {
682
- currentTree = applyPatch(currentTree, patch);
683
- setTree({ ...currentTree });
773
+ currentSpec = applyPatch(currentSpec, patch);
774
+ setSpec({ ...currentSpec });
684
775
  }
685
776
  }
686
777
  }
687
778
  if (buffer.trim()) {
688
779
  const patch = parsePatchLine(buffer);
689
780
  if (patch) {
690
- currentTree = applyPatch(currentTree, patch);
691
- setTree({ ...currentTree });
781
+ currentSpec = applyPatch(currentSpec, patch);
782
+ setSpec({ ...currentSpec });
692
783
  }
693
784
  }
694
- onComplete?.(currentTree);
785
+ onComplete?.(currentSpec);
695
786
  } catch (err) {
696
787
  if (err.name === "AbortError") {
697
788
  return;
@@ -705,13 +796,13 @@ function useUIStream({
705
796
  },
706
797
  [api, onComplete, onError]
707
798
  );
708
- useEffect(() => {
799
+ useEffect2(() => {
709
800
  return () => {
710
801
  abortControllerRef.current?.abort();
711
802
  };
712
803
  }, []);
713
804
  return {
714
- tree,
805
+ spec,
715
806
  isStreaming,
716
807
  error,
717
808
  send,
@@ -753,8 +844,11 @@ export {
753
844
  Renderer,
754
845
  ValidationProvider,
755
846
  VisibilityProvider,
847
+ createRenderer,
756
848
  createRendererFromCatalog,
849
+ elementTreeSchema,
757
850
  flatToTree,
851
+ schema,
758
852
  useAction,
759
853
  useActions,
760
854
  useData,