@json-render/react-native 0.8.0 → 0.9.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/dist/index.mjs CHANGED
@@ -7,76 +7,115 @@ import {
7
7
  schema
8
8
  } from "./chunk-T27PQA6P.mjs";
9
9
 
10
- // src/contexts/state.tsx
10
+ // ../react-state/dist/index.mjs
11
11
  import {
12
12
  createContext,
13
13
  useContext,
14
- useState,
15
14
  useCallback,
16
- useMemo,
17
15
  useEffect,
18
- useRef
16
+ useMemo,
17
+ useRef,
18
+ useSyncExternalStore
19
19
  } from "react";
20
- import { getByPath, setByPath } from "@json-render/core";
20
+ import {
21
+ getByPath,
22
+ createStateStore
23
+ } from "@json-render/core";
24
+ import { flattenToPointers } from "@json-render/core/store-utils";
21
25
  import { jsx } from "react/jsx-runtime";
22
26
  var StateContext = createContext(null);
27
+ function computeInitialFlat(isControlled, initialState) {
28
+ if (isControlled) return null;
29
+ if (Object.keys(initialState).length === 0) return {};
30
+ return flattenToPointers(initialState);
31
+ }
23
32
  function StateProvider({
33
+ store: externalStore,
24
34
  initialState = {},
25
35
  onStateChange,
26
36
  children
27
37
  }) {
28
- const [state, setStateInternal] = useState(initialState);
29
- const stateRef = useRef(state);
30
- stateRef.current = state;
31
- const initialStateJsonRef = useRef(JSON.stringify(initialState));
38
+ const internalStoreRef = useRef(void 0);
39
+ if (!externalStore && !internalStoreRef.current) {
40
+ internalStoreRef.current = createStateStore(initialState);
41
+ }
42
+ const store = externalStore ?? internalStoreRef.current;
43
+ const storeRef = useRef(store);
44
+ storeRef.current = store;
45
+ const isControlledRef = useRef(!!externalStore);
46
+ isControlledRef.current = !!externalStore;
47
+ const initialModeRef = useRef(externalStore ? "controlled" : "uncontrolled");
48
+ const modeWarnedRef = useRef(false);
49
+ if (process.env.NODE_ENV !== "production") {
50
+ const currentMode = externalStore ? "controlled" : "uncontrolled";
51
+ if (currentMode !== initialModeRef.current && !modeWarnedRef.current) {
52
+ modeWarnedRef.current = true;
53
+ console.warn(
54
+ `StateProvider: switching from ${initialModeRef.current} to ${currentMode} mode is not supported.`
55
+ );
56
+ }
57
+ }
58
+ const prevInitialStateRef = useRef(initialState);
59
+ const prevFlatRef = useRef(
60
+ computeInitialFlat(!!externalStore, initialState)
61
+ );
32
62
  useEffect(() => {
33
- const newJson = JSON.stringify(initialState);
34
- if (newJson !== initialStateJsonRef.current) {
35
- initialStateJsonRef.current = newJson;
36
- if (initialState && Object.keys(initialState).length > 0) {
37
- setStateInternal((prev) => ({ ...prev, ...initialState }));
63
+ if (externalStore) return;
64
+ if (initialState === prevInitialStateRef.current) return;
65
+ prevInitialStateRef.current = initialState;
66
+ const nextFlat = initialState && Object.keys(initialState).length > 0 ? flattenToPointers(initialState) : {};
67
+ const prevFlat = prevFlatRef.current ?? {};
68
+ const allKeys = /* @__PURE__ */ new Set([
69
+ ...Object.keys(prevFlat),
70
+ ...Object.keys(nextFlat)
71
+ ]);
72
+ const updates = {};
73
+ for (const key of allKeys) {
74
+ if (prevFlat[key] !== nextFlat[key]) {
75
+ updates[key] = key in nextFlat ? nextFlat[key] : void 0;
38
76
  }
39
77
  }
40
- }, [initialState]);
41
- const get = useCallback(
42
- (path) => getByPath(stateRef.current, path),
43
- []
44
- );
45
- const set = useCallback(
46
- (path, value2) => {
47
- setStateInternal((prev) => {
48
- const next = { ...prev };
49
- setByPath(next, path, value2);
50
- return next;
51
- });
52
- onStateChange?.(path, value2);
53
- },
54
- [onStateChange]
78
+ prevFlatRef.current = nextFlat;
79
+ if (Object.keys(updates).length > 0) {
80
+ store.update(updates);
81
+ }
82
+ }, [externalStore, initialState, store]);
83
+ const state = useSyncExternalStore(
84
+ store.subscribe,
85
+ store.getSnapshot,
86
+ store.getServerSnapshot ?? store.getSnapshot
55
87
  );
56
- const update = useCallback(
57
- (updates) => {
58
- const entries = Object.entries(updates);
59
- setStateInternal((prev) => {
60
- const next = { ...prev };
61
- for (const [path, value2] of entries) {
62
- setByPath(next, path, value2);
88
+ const onStateChangeRef = useRef(onStateChange);
89
+ onStateChangeRef.current = onStateChange;
90
+ const set = useCallback((path, value2) => {
91
+ const s = storeRef.current;
92
+ const prev = s.getSnapshot();
93
+ s.set(path, value2);
94
+ if (!isControlledRef.current && s.getSnapshot() !== prev) {
95
+ onStateChangeRef.current?.([{ path, value: value2 }]);
96
+ }
97
+ }, []);
98
+ const update = useCallback((updates) => {
99
+ const s = storeRef.current;
100
+ const prev = s.getSnapshot();
101
+ s.update(updates);
102
+ if (!isControlledRef.current && s.getSnapshot() !== prev) {
103
+ const changes = [];
104
+ for (const [path, value2] of Object.entries(updates)) {
105
+ if (getByPath(prev, path) !== value2) {
106
+ changes.push({ path, value: value2 });
63
107
  }
64
- return next;
65
- });
66
- for (const [path, value2] of entries) {
67
- onStateChange?.(path, value2);
68
108
  }
69
- },
70
- [onStateChange]
71
- );
109
+ if (changes.length > 0) {
110
+ onStateChangeRef.current?.(changes);
111
+ }
112
+ }
113
+ }, []);
114
+ const get = useCallback((path) => storeRef.current.get(path), []);
115
+ const getSnapshot = useCallback(() => storeRef.current.getSnapshot(), []);
72
116
  const value = useMemo(
73
- () => ({
74
- state,
75
- get,
76
- set,
77
- update
78
- }),
79
- [state, get, set, update]
117
+ () => ({ state, get, set, update, getSnapshot }),
118
+ [state, get, set, update, getSnapshot]
80
119
  );
81
120
  return /* @__PURE__ */ jsx(StateContext.Provider, { value, children });
82
121
  }
@@ -146,7 +185,7 @@ function useIsVisible(condition) {
146
185
  import {
147
186
  createContext as createContext3,
148
187
  useContext as useContext3,
149
- useState as useState2,
188
+ useState,
150
189
  useCallback as useCallback2,
151
190
  useMemo as useMemo3
152
191
  } from "react";
@@ -194,10 +233,10 @@ function ActionProvider({
194
233
  navigate,
195
234
  children
196
235
  }) {
197
- const { state, get, set } = useStateStore();
198
- const [handlers, setHandlers] = useState2(initialHandlers);
199
- const [loadingActions, setLoadingActions] = useState2(/* @__PURE__ */ new Set());
200
- const [pendingConfirmation, setPendingConfirmation] = useState2(null);
236
+ const { get, set, getSnapshot } = useStateStore();
237
+ const [handlers, setHandlers] = useState(initialHandlers);
238
+ const [loadingActions, setLoadingActions] = useState(/* @__PURE__ */ new Set());
239
+ const [pendingConfirmation, setPendingConfirmation] = useState(null);
201
240
  const registerHandler = useCallback2(
202
241
  (name, handler) => {
203
242
  setHandlers((prev) => ({ ...prev, [name]: handler }));
@@ -206,7 +245,7 @@ function ActionProvider({
206
245
  );
207
246
  const execute = useCallback2(
208
247
  async (binding) => {
209
- const resolved = resolveAction(binding, state);
248
+ const resolved = resolveAction(binding, getSnapshot());
210
249
  if (resolved.action === "setState" && resolved.params) {
211
250
  const statePath = resolved.params.statePath;
212
251
  const value2 = resolved.params.value;
@@ -329,7 +368,7 @@ function ActionProvider({
329
368
  });
330
369
  }
331
370
  },
332
- [state, handlers, get, set, navigate]
371
+ [handlers, get, set, getSnapshot, navigate]
333
372
  );
334
373
  const confirm = useCallback2(() => {
335
374
  pendingConfirmation?.resolve();
@@ -460,10 +499,10 @@ var styles = StyleSheet.create({
460
499
  });
461
500
 
462
501
  // src/contexts/validation.tsx
463
- import React4, {
502
+ import React3, {
464
503
  createContext as createContext4,
465
504
  useContext as useContext4,
466
- useState as useState3,
505
+ useState as useState2,
467
506
  useCallback as useCallback3,
468
507
  useMemo as useMemo4
469
508
  } from "react";
@@ -511,8 +550,8 @@ function ValidationProvider({
511
550
  children
512
551
  }) {
513
552
  const { state } = useStateStore();
514
- const [fieldStates, setFieldStates] = useState3({});
515
- const [fieldConfigs, setFieldConfigs] = useState3({});
553
+ const [fieldStates, setFieldStates] = useState2({});
554
+ const [fieldConfigs, setFieldConfigs] = useState2({});
516
555
  const registerField = useCallback3(
517
556
  (path, config) => {
518
557
  setFieldConfigs((prev) => {
@@ -618,7 +657,7 @@ function useFieldValidation(path, config) {
618
657
  clear: clearField,
619
658
  registerField
620
659
  } = useValidation();
621
- React4.useEffect(() => {
660
+ React3.useEffect(() => {
622
661
  if (config) {
623
662
  registerField(path, config);
624
663
  }
@@ -660,8 +699,11 @@ function useRepeatScope() {
660
699
  return useContext5(RepeatScopeContext);
661
700
  }
662
701
 
702
+ // src/index.ts
703
+ import { createStateStore as createStateStore2 } from "@json-render/core";
704
+
663
705
  // src/renderer.tsx
664
- import React6, {
706
+ import React5, {
665
707
  useCallback as useCallback5,
666
708
  useMemo as useMemo5
667
709
  } from "react";
@@ -692,9 +734,9 @@ import {
692
734
  } from "react-native";
693
735
 
694
736
  // src/hooks.ts
695
- import { useState as useState4, useCallback as useCallback4, useRef as useRef2, useEffect as useEffect2 } from "react";
737
+ import { useState as useState3, useCallback as useCallback4, useRef as useRef2, useEffect as useEffect2 } from "react";
696
738
  import {
697
- setByPath as setByPath2,
739
+ setByPath,
698
740
  getByPath as getByPath2,
699
741
  removeByPath,
700
742
  validateSpec,
@@ -757,7 +799,7 @@ function setSpecValue(newSpec, path, value) {
757
799
  if (path.startsWith("/state/")) {
758
800
  if (!newSpec.state) newSpec.state = {};
759
801
  const statePath = path.slice("/state".length);
760
- setByPath2(newSpec.state, statePath, value);
802
+ setByPath(newSpec.state, statePath, value);
761
803
  return;
762
804
  }
763
805
  if (path.startsWith("/elements/")) {
@@ -771,7 +813,7 @@ function setSpecValue(newSpec, path, value) {
771
813
  if (element) {
772
814
  const propPath = "/" + pathParts.slice(1).join("/");
773
815
  const newElement = { ...element };
774
- setByPath2(
816
+ setByPath(
775
817
  newElement,
776
818
  propPath,
777
819
  value
@@ -855,10 +897,10 @@ function useUIStream({
855
897
  validate: enableValidation = false,
856
898
  maxRetries = 5
857
899
  }) {
858
- const [spec, setSpec] = useState4(null);
859
- const [isStreaming, setIsStreaming] = useState4(false);
860
- const [error, setError] = useState4(null);
861
- const [rawLines, setRawLines] = useState4([]);
900
+ const [spec, setSpec] = useState3(null);
901
+ const [isStreaming, setIsStreaming] = useState3(false);
902
+ const [error, setError] = useState3(null);
903
+ const [rawLines, setRawLines] = useState3([]);
862
904
  const abortControllerRef = useRef2(null);
863
905
  const stop = useCallback4(() => {
864
906
  abortControllerRef.current?.abort();
@@ -1891,7 +1933,7 @@ var modalStyles = StyleSheet2.create({
1891
1933
 
1892
1934
  // src/renderer.tsx
1893
1935
  import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
1894
- var ElementErrorBoundary = class extends React6.Component {
1936
+ var ElementErrorBoundary = class extends React5.Component {
1895
1937
  constructor(props) {
1896
1938
  super(props);
1897
1939
  this.state = { hasError: false };
@@ -1913,7 +1955,7 @@ var ElementErrorBoundary = class extends React6.Component {
1913
1955
  return this.props.children;
1914
1956
  }
1915
1957
  };
1916
- var ElementRenderer = React6.memo(function ElementRenderer2({
1958
+ var ElementRenderer = React5.memo(function ElementRenderer2({
1917
1959
  element,
1918
1960
  spec,
1919
1961
  registry,
@@ -1923,6 +1965,7 @@ var ElementRenderer = React6.memo(function ElementRenderer2({
1923
1965
  const repeatScope = useRepeatScope();
1924
1966
  const { ctx } = useVisibility();
1925
1967
  const { execute } = useActions();
1968
+ const { getSnapshot } = useStateStore();
1926
1969
  const fullCtx = useMemo5(
1927
1970
  () => repeatScope ? {
1928
1971
  ...ctx,
@@ -1935,23 +1978,27 @@ var ElementRenderer = React6.memo(function ElementRenderer2({
1935
1978
  const isVisible = element.visible === void 0 ? true : evaluateVisibility2(element.visible, fullCtx);
1936
1979
  const onBindings = element.on;
1937
1980
  const emit = useCallback5(
1938
- (eventName) => {
1981
+ async (eventName) => {
1939
1982
  const binding = onBindings?.[eventName];
1940
1983
  if (!binding) return;
1941
1984
  const actionBindings = Array.isArray(binding) ? binding : [binding];
1942
1985
  for (const b of actionBindings) {
1943
1986
  if (!b.params) {
1944
- execute(b);
1987
+ await execute(b);
1945
1988
  continue;
1946
1989
  }
1990
+ const liveCtx = {
1991
+ ...fullCtx,
1992
+ stateModel: getSnapshot()
1993
+ };
1947
1994
  const resolved = {};
1948
1995
  for (const [key, val] of Object.entries(b.params)) {
1949
- resolved[key] = resolveActionParam(val, fullCtx);
1996
+ resolved[key] = resolveActionParam(val, liveCtx);
1950
1997
  }
1951
- execute({ ...b, params: resolved });
1998
+ await execute({ ...b, params: resolved });
1952
1999
  }
1953
2000
  },
1954
- [onBindings, execute, fullCtx]
2001
+ [onBindings, execute, fullCtx, getSnapshot]
1955
2002
  );
1956
2003
  if (!isVisible) {
1957
2004
  return null;
@@ -2089,6 +2136,7 @@ function Renderer({
2089
2136
  }
2090
2137
  function JSONUIProvider({
2091
2138
  registry,
2139
+ store,
2092
2140
  initialState,
2093
2141
  handlers,
2094
2142
  navigate,
@@ -2096,10 +2144,18 @@ function JSONUIProvider({
2096
2144
  onStateChange,
2097
2145
  children
2098
2146
  }) {
2099
- return /* @__PURE__ */ jsx7(StateProvider, { initialState, onStateChange, children: /* @__PURE__ */ jsx7(VisibilityProvider, { children: /* @__PURE__ */ jsx7(ActionProvider, { handlers, navigate, children: /* @__PURE__ */ jsxs3(ValidationProvider, { customFunctions: validationFunctions, children: [
2100
- children,
2101
- /* @__PURE__ */ jsx7(ConfirmationDialogManager, {})
2102
- ] }) }) }) });
2147
+ return /* @__PURE__ */ jsx7(
2148
+ StateProvider,
2149
+ {
2150
+ store,
2151
+ initialState,
2152
+ onStateChange,
2153
+ children: /* @__PURE__ */ jsx7(VisibilityProvider, { children: /* @__PURE__ */ jsx7(ActionProvider, { handlers, navigate, children: /* @__PURE__ */ jsxs3(ValidationProvider, { customFunctions: validationFunctions, children: [
2154
+ children,
2155
+ /* @__PURE__ */ jsx7(ConfirmationDialogManager, {})
2156
+ ] }) }) })
2157
+ }
2158
+ );
2103
2159
  }
2104
2160
  function ConfirmationDialogManager() {
2105
2161
  const { pendingConfirmation, confirm, cancel } = useActions();
@@ -2164,6 +2220,7 @@ function createRenderer(catalog, components) {
2164
2220
  const registry = components;
2165
2221
  return function CatalogRenderer({
2166
2222
  spec,
2223
+ store,
2167
2224
  state,
2168
2225
  onAction,
2169
2226
  onStateChange,
@@ -2179,18 +2236,26 @@ function createRenderer(catalog, components) {
2179
2236
  has: () => true
2180
2237
  }
2181
2238
  ) : void 0;
2182
- return /* @__PURE__ */ jsx7(StateProvider, { initialState: state, onStateChange, children: /* @__PURE__ */ jsx7(VisibilityProvider, { children: /* @__PURE__ */ jsx7(ActionProvider, { handlers: actionHandlers, children: /* @__PURE__ */ jsxs3(ValidationProvider, { children: [
2183
- /* @__PURE__ */ jsx7(
2184
- Renderer,
2185
- {
2186
- spec,
2187
- registry,
2188
- loading,
2189
- fallback
2190
- }
2191
- ),
2192
- /* @__PURE__ */ jsx7(ConfirmationDialogManager, {})
2193
- ] }) }) }) });
2239
+ return /* @__PURE__ */ jsx7(
2240
+ StateProvider,
2241
+ {
2242
+ store,
2243
+ initialState: state,
2244
+ onStateChange,
2245
+ children: /* @__PURE__ */ jsx7(VisibilityProvider, { children: /* @__PURE__ */ jsx7(ActionProvider, { handlers: actionHandlers, children: /* @__PURE__ */ jsxs3(ValidationProvider, { children: [
2246
+ /* @__PURE__ */ jsx7(
2247
+ Renderer,
2248
+ {
2249
+ spec,
2250
+ registry,
2251
+ loading,
2252
+ fallback
2253
+ }
2254
+ ),
2255
+ /* @__PURE__ */ jsx7(ConfirmationDialogManager, {})
2256
+ ] }) }) })
2257
+ }
2258
+ );
2194
2259
  };
2195
2260
  }
2196
2261
  export {
@@ -2204,6 +2269,7 @@ export {
2204
2269
  VisibilityProvider,
2205
2270
  createRenderer,
2206
2271
  createStandardActionHandlers,
2272
+ createStateStore2 as createStateStore,
2207
2273
  defineRegistry,
2208
2274
  elementTreeSchema,
2209
2275
  flatToTree,