cogsbox-state 0.5.278 → 0.5.280

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-state",
3
- "version": "0.5.278",
3
+ "version": "0.5.280",
4
4
  "description": "React state management library with form controls and server sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/CogsState.tsx CHANGED
@@ -37,6 +37,7 @@ import { z } from "zod";
37
37
  import { formRefStore, getGlobalStore, type ComponentsType } from "./store.js";
38
38
  import { useCogsConfig } from "./CogsStateClient.js";
39
39
  import { applyPatch } from "fast-json-patch";
40
+ import useMeasure from "react-use-measure";
40
41
 
41
42
  type Prettify<T> = { [K in keyof T]: T[K] } & {};
42
43
 
@@ -1464,6 +1465,7 @@ function createProxyHandler<T>(
1464
1465
  }
1465
1466
  startTransition(() => {
1466
1467
  updateInitialStateGlobal(stateKey, newState);
1468
+ getGlobalStore.getState().initializeShadowState(stateKey, newState);
1467
1469
  setUpdaterState(stateKey, newUpdaterState);
1468
1470
  setState(stateKey, newState);
1469
1471
  const stateEntry = getGlobalStore
@@ -2131,7 +2133,53 @@ function createProxyHandler<T>(
2131
2133
  });
2132
2134
  };
2133
2135
  }
2136
+ if (prop === "stateList") {
2137
+ return (
2138
+ callbackfn: (
2139
+ value: InferArrayElement<T>,
2140
+ setter: StateObject<InferArrayElement<T>>,
2141
+ index: number,
2142
+ array: T,
2143
+ arraySetter: StateObject<T>
2144
+ ) => any
2145
+ ) => {
2146
+ const arrayToMap = getGlobalStore
2147
+ .getState()
2148
+ .getNestedState(stateKey, path) as any[];
2149
+
2150
+ if (!Array.isArray(arrayToMap)) {
2151
+ console.warn(
2152
+ `stateList called on a non-array value at path: ${path.join(".")}.`
2153
+ );
2154
+ return null;
2155
+ }
2134
2156
 
2157
+ const indicesToMap =
2158
+ meta?.validIndices ||
2159
+ Array.from({ length: arrayToMap.length }, (_, i) => i);
2160
+
2161
+ return indicesToMap.map((originalIndex, localIndex) => {
2162
+ const item = arrayToMap[originalIndex];
2163
+ const finalPath = [...path, originalIndex.toString()];
2164
+ const setter = rebuildStateShape(item, finalPath, meta);
2165
+ const itemComponentId = `${componentId}-${path.join(".")}-${originalIndex}`;
2166
+
2167
+ return createElement(CogsItemWrapper, {
2168
+ key: originalIndex,
2169
+ stateKey,
2170
+ itemComponentId,
2171
+ itemPath: finalPath,
2172
+ children: callbackfn(
2173
+ item,
2174
+ setter,
2175
+ localIndex,
2176
+ arrayToMap as any,
2177
+ rebuildStateShape(arrayToMap as any, path, meta)
2178
+ ),
2179
+ });
2180
+ });
2181
+ };
2182
+ }
2135
2183
  if (prop === "stateFlattenOn") {
2136
2184
  return (fieldName: string) => {
2137
2185
  const arrayToMap = currentState as any[];
@@ -2850,7 +2898,7 @@ export function $cogsSignalStore(proxy: {
2850
2898
  );
2851
2899
  return createElement("text", {}, String(value));
2852
2900
  }
2853
- // This is an internal component. It should NOT be exported.
2901
+
2854
2902
  function CogsItemWrapper({
2855
2903
  stateKey,
2856
2904
  itemComponentId,
@@ -2862,10 +2910,17 @@ function CogsItemWrapper({
2862
2910
  itemPath: string[];
2863
2911
  children: React.ReactNode;
2864
2912
  }) {
2865
- // This is a real component, so we can safely call hooks.
2866
2913
  const [, forceUpdate] = useState({});
2914
+ const [ref, bounds] = useMeasure();
2915
+
2916
+ useEffect(() => {
2917
+ if (bounds.height > 0) {
2918
+ getGlobalStore
2919
+ .getState()
2920
+ .setShadowMetadata(stateKey, itemPath, { itemHeight: bounds.height });
2921
+ }
2922
+ }, [bounds.height]);
2867
2923
 
2868
- // We use useLayoutEffect to register the component and clean up when it unmounts.
2869
2924
  useLayoutEffect(() => {
2870
2925
  const fullComponentId = `${stateKey}////${itemComponentId}`;
2871
2926
  const stateEntry = getGlobalStore
@@ -2874,15 +2929,13 @@ function CogsItemWrapper({
2874
2929
  components: new Map(),
2875
2930
  };
2876
2931
 
2877
- // Register the component with its unique ID and its specific, atomic path.
2878
2932
  stateEntry.components.set(fullComponentId, {
2879
2933
  forceUpdate: () => forceUpdate({}),
2880
- paths: new Set([itemPath.join(".")]), // ATOMIC: Subscribes only to this item's path.
2934
+ paths: new Set([itemPath.join(".")]),
2881
2935
  });
2882
2936
 
2883
2937
  getGlobalStore.getState().stateComponents.set(stateKey, stateEntry);
2884
2938
 
2885
- // Return a cleanup function to unregister on unmount.
2886
2939
  return () => {
2887
2940
  const currentEntry = getGlobalStore
2888
2941
  .getState()
@@ -2891,8 +2944,7 @@ function CogsItemWrapper({
2891
2944
  currentEntry.components.delete(fullComponentId);
2892
2945
  }
2893
2946
  };
2894
- }, [stateKey, itemComponentId, itemPath.join(".")]); // Effect dependency array is stable.
2947
+ }, [stateKey, itemComponentId, itemPath.join(".")]);
2895
2948
 
2896
- // Render the actual component the user provided.
2897
- return <>{children}</>;
2949
+ return <div ref={ref}>{children}</div>;
2898
2950
  }