cogsbox-state 0.5.475-canary.4 → 0.5.475-canary.6

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.475-canary.4",
3
+ "version": "0.5.475-canary.6",
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
@@ -230,9 +230,18 @@ export type EndType<
230
230
  $update: UpdateType<T>;
231
231
  $_path: string[];
232
232
  $_stateKey: string;
233
- $isolate: (
234
- renderFn: (state: StateObject<T, TPlugins>) => React.ReactNode
235
- ) => JSX.Element;
233
+ $isolate: {
234
+ // Overload 1: Just the render function (Default behavior)
235
+ (
236
+ renderFn: (state: StateObject<T, TPlugins>) => React.ReactNode
237
+ ): JSX.Element;
238
+
239
+ // Overload 2: Dependencies array + render function (Optimized behavior)
240
+ (
241
+ dependencies: any[],
242
+ renderFn: (state: StateObject<T, TPlugins>) => React.ReactNode
243
+ ): JSX.Element;
244
+ };
236
245
  $formElement: (
237
246
  control: FormControl<T>,
238
247
  opts?: PerPathFormOptsType<T, TPlugins>
@@ -314,6 +323,7 @@ export type StateObject<
314
323
  };
315
324
 
316
325
  export type CogsUpdate<T extends unknown> = UpdateType<T>;
326
+
317
327
  type EffectiveSetStateArg<
318
328
  T,
319
329
  UpdateType extends 'update' | 'insert' | 'cut',
@@ -349,7 +359,6 @@ export type UpdateTypeDetail = {
349
359
  oldValue: any;
350
360
  newValue: any;
351
361
  userId?: number;
352
-
353
362
  itemId?: string;
354
363
  insertAfterId?: string;
355
364
  metaData?: Record<string, any>;
@@ -511,14 +520,12 @@ const {
511
520
  addPathComponent,
512
521
  clearSelectedIndexesForState,
513
522
  addStateLog,
514
- setSyncInfo,
515
523
  clearSelectedIndex,
516
524
  getSyncInfo,
517
525
  notifyPathSubscribers,
518
526
  getPluginMetaDataMap,
519
527
  setPluginMetaData,
520
528
  removePluginMetaData,
521
- // Note: The old functions are no longer imported under their original names
522
529
  } = getGlobalStore.getState();
523
530
 
524
531
  const { notifyUpdate } = pluginStore.getState();
@@ -1069,9 +1076,7 @@ let isFlushScheduled = false;
1069
1076
  function scheduleFlush() {
1070
1077
  if (!isFlushScheduled) {
1071
1078
  isFlushScheduled = true;
1072
- console.log('Scheduling flush');
1073
1079
  queueMicrotask(() => {
1074
- console.log('Actually flushing');
1075
1080
  flushQueue();
1076
1081
  });
1077
1082
  }
@@ -2764,7 +2769,7 @@ function createProxyHandler<
2764
2769
  if (prop === '$cut') {
2765
2770
  return (index?: number, options?: { waitForSync?: boolean }) => {
2766
2771
  const shadowMeta = getShadowMetadata(stateKey, path);
2767
- console.log('shadowMeta ->>>>>>>>>>>>>>>>', shadowMeta);
2772
+
2768
2773
  if (!shadowMeta?.arrayKeys || shadowMeta.arrayKeys.length === 0)
2769
2774
  return;
2770
2775
 
@@ -2775,12 +2780,8 @@ function createProxyHandler<
2775
2780
  ? index
2776
2781
  : shadowMeta.arrayKeys.length - 1;
2777
2782
 
2778
- console.log('indexToCut ->>>>>>>>>>>>>>>>', indexToCut);
2779
-
2780
2783
  const idToCut = shadowMeta.arrayKeys[indexToCut];
2781
2784
  if (!idToCut) return;
2782
- console.log('idToCut ->>>>>>>>>>>>>>>>', idToCut);
2783
-
2784
2785
  effectiveSetState(null, [...path, idToCut], {
2785
2786
  updateType: 'cut',
2786
2787
  });
@@ -3011,7 +3012,6 @@ function createProxyHandler<
3011
3012
  getPluginMetaDataMap(stateKey, path)?.get(pluginName);
3012
3013
  }
3013
3014
  if (prop === '$addPluginMetaData') {
3014
- console.log('$addPluginMetaDat');
3015
3015
  return (pluginName: string, data: Record<string, any>) =>
3016
3016
  setPluginMetaData(stateKey, path, pluginName, data);
3017
3017
  }
@@ -3078,12 +3078,6 @@ function createProxyHandler<
3078
3078
  },
3079
3079
  metaData?: Record<string, any>
3080
3080
  ) => {
3081
- console.log(
3082
- 'getGlobalStore',
3083
- getGlobalStore
3084
- .getState()
3085
- .getShadowMetadata(stateKey, operation.path)
3086
- );
3087
3081
  let index: number | undefined;
3088
3082
  if (
3089
3083
  operation.insertAfterId &&
@@ -3269,11 +3263,29 @@ function createProxyHandler<
3269
3263
  };
3270
3264
  }
3271
3265
  if (prop === '$isolate') {
3272
- return (renderFn: (state: any) => React.ReactNode) => {
3266
+ // We accept (renderFn) OR (deps, renderFn)
3267
+ return (
3268
+ arg1: any[] | ((state: any) => React.ReactNode),
3269
+ arg2?: (state: any) => React.ReactNode
3270
+ ) => {
3271
+ // Check if the first argument is the dependency array
3272
+ const hasDependencies = Array.isArray(arg1);
3273
+
3274
+ // Normalize arguments
3275
+ const dependencies = hasDependencies ? arg1 : undefined;
3276
+ const renderFn = hasDependencies ? arg2 : arg1;
3277
+
3278
+ if (!renderFn || typeof renderFn !== 'function') {
3279
+ throw new Error(
3280
+ 'CogsState: $isolate requires a render function.'
3281
+ );
3282
+ }
3283
+
3273
3284
  return (
3274
3285
  <IsolatedComponentWrapper
3275
3286
  stateKey={stateKey}
3276
- path={path}
3287
+ path={path} // The path of the node calling $isolate (e.g. "form")
3288
+ dependencies={dependencies} // Pass the specific parts to watch
3277
3289
  rebuildStateShape={rebuildStateShape}
3278
3290
  renderFn={renderFn}
3279
3291
  />
@@ -633,36 +633,49 @@ const useImageLoaded = (ref: RefObject<HTMLElement>): boolean => {
633
633
  };
634
634
  // Components.tsx
635
635
 
636
- // Generic isolated component wrapper
637
636
  export function IsolatedComponentWrapper({
638
637
  stateKey,
639
- path,
638
+ path, // The path of the parent node (e.g. ['form'])
639
+ dependencies, // NEW: Optional array of Proxy objects or path arrays
640
640
  rebuildStateShape,
641
641
  renderFn,
642
642
  }: {
643
643
  stateKey: string;
644
644
  path: string[];
645
- rebuildStateShape: (options: {
646
- path: string[];
647
- componentId: string;
648
- meta?: any;
649
- }) => any;
645
+ dependencies?: any[]; // The explicit list of dependencies
646
+ rebuildStateShape: (options: any) => any;
650
647
  renderFn: (state: any) => React.ReactNode;
651
648
  }) {
652
649
  const [componentId] = useState(() => uuidv4());
653
650
  const [, forceUpdate] = useState({});
654
651
 
655
- const stateKeyPathKey = [stateKey, ...path].join('.');
656
652
  useRegisterComponent(stateKey, componentId, forceUpdate);
657
653
 
654
+ const pathsToSubscribe = useMemo(() => {
655
+ if (dependencies && dependencies.length > 0) {
656
+ return dependencies.map((dep) => {
657
+ return [stateKey, ...dep.$_path].join('.');
658
+ });
659
+ }
660
+
661
+ return [[stateKey, ...path].join('.')];
662
+ }, [stateKey, path, dependencies]);
663
+
664
+ // 2. Subscribe to ALL calculated paths
658
665
  useEffect(() => {
659
- const unsubscribe = getGlobalStore
660
- .getState()
661
- .subscribeToPath(stateKeyPathKey, () => {
666
+ const store = getGlobalStore.getState();
667
+
668
+ // Create an array of unsubscribe functions
669
+ const unsubs = pathsToSubscribe.map((fullPathKey) => {
670
+ return store.subscribeToPath(fullPathKey, () => {
662
671
  forceUpdate({});
663
672
  });
664
- return () => unsubscribe();
665
- }, [stateKeyPathKey]);
673
+ });
674
+
675
+ return () => {
676
+ unsubs.forEach((unsub) => unsub());
677
+ };
678
+ }, [pathsToSubscribe]);
666
679
 
667
680
  const baseState = rebuildStateShape({
668
681
  path: path,
@@ -672,7 +685,6 @@ export function IsolatedComponentWrapper({
672
685
 
673
686
  return <>{renderFn(baseState)}</>;
674
687
  }
675
-
676
688
  // 1. Define the MINIMAL props needed.
677
689
  type PluginWrapperProps = {
678
690
  children: React.ReactNode;