cogsbox-state 0.5.465 → 0.5.466

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/src/store.ts CHANGED
@@ -7,7 +7,7 @@ import type {
7
7
  UpdateTypeDetail,
8
8
  } from './CogsState.js';
9
9
 
10
- import { startTransition, type ReactNode } from 'react';
10
+ import { type ReactNode } from 'react';
11
11
 
12
12
  export type FreshValuesObject = {
13
13
  pathsToValues?: string[];
@@ -28,7 +28,6 @@ export type FormRefStoreState = {
28
28
  registerFormRef: (id: string, ref: React.RefObject<any>) => void;
29
29
  getFormRef: (id: string) => React.RefObject<any> | undefined;
30
30
  removeFormRef: (id: string) => void;
31
- // New method to get all refs for a stateKey
32
31
  getFormRefsByStateKey: (
33
32
  stateKey: string
34
33
  ) => Map<string, React.RefObject<any>>;
@@ -53,7 +52,6 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
53
52
  return { formRefs: newRefs };
54
53
  }),
55
54
 
56
- // Get all refs that start with the stateKey prefix
57
55
  getFormRefsByStateKey: (stateKey) => {
58
56
  const allRefs = get().formRefs;
59
57
  const stateKeyPrefix = stateKey + '.';
@@ -83,26 +81,25 @@ export type ComponentsType = {
83
81
  };
84
82
 
85
83
  export type ValidationStatus =
86
- | 'NOT_VALIDATED' // Never run
87
- | 'VALIDATING' // Currently running
88
- | 'VALID' // Passed
89
- | 'INVALID'; // Failed
84
+ | 'NOT_VALIDATED'
85
+ | 'VALIDATING'
86
+ | 'VALID'
87
+ | 'INVALID';
90
88
 
91
89
  export type ValidationError = {
92
90
  source: 'client' | 'sync_engine' | 'api';
93
91
  message: string;
94
- severity: 'warning' | 'error'; // warning = gentle, error = blocking
95
- code?: string; // Optional error code
92
+ severity: 'warning' | 'error';
93
+ code?: string;
96
94
  };
97
95
 
98
96
  export type ValidationState = {
99
97
  status: ValidationStatus;
100
98
  errors: ValidationError[];
101
99
  lastValidated?: number;
102
- validatedValue?: any; // Value when last validated
100
+ validatedValue?: any;
103
101
  };
104
102
 
105
- // This is the new definition for the metadata object
106
103
  export type ShadowMetadata = {
107
104
  id?: string;
108
105
  stateSource?: 'default' | 'server' | 'localStorage';
@@ -156,28 +153,23 @@ export type ShadowMetadata = {
156
153
  >;
157
154
  } & ComponentsType;
158
155
 
159
- // The shadow node itself can have a value and the metadata object.
160
156
  type ShadowNode = {
161
157
  value?: any;
162
158
  _meta?: ShadowMetadata;
163
- [key: string]: any; // For nested data properties
159
+ [key: string]: any;
164
160
  };
165
161
 
166
162
  export type CogsGlobalState = {
167
163
  // NEW shadow store
168
- shadowStateStore: Map<string, ShadowNode>; // Changed ShadowMetadata to ShadowNode
164
+ shadowStateStore: Map<string, ShadowNode>;
169
165
  setTransformCache: (
170
166
  key: string,
171
167
  path: string[],
172
168
  cacheKey: string,
173
169
  cacheData: any
174
170
  ) => void;
175
- // NEW functions
176
171
  initializeShadowState: (key: string, initialState: any) => void;
177
-
178
- // REFACTORED: getShadowNode gets the whole object (data + _meta)
179
172
  getShadowNode: (key: string, path: string[]) => ShadowNode | undefined;
180
- // REFACTORED: getShadowMetadata now returns just the _meta field
181
173
  getShadowMetadata: (
182
174
  key: string,
183
175
  path: string[]
@@ -191,6 +183,18 @@ export type CogsGlobalState = {
191
183
  log?: boolean
192
184
  ) => any;
193
185
  updateShadowAtPath: (key: string, path: string[], newValue: any) => void;
186
+ insertManyShadowArrayElements: (
187
+ key: string,
188
+ arrayPath: string[],
189
+ newItems: any[],
190
+ index?: number
191
+ ) => void;
192
+ addItemsToArrayNode: (
193
+ key: string,
194
+ arrayPath: string[],
195
+ newItems: any,
196
+ newKeys: string[]
197
+ ) => void;
194
198
  insertShadowArrayElement: (
195
199
  key: string,
196
200
  arrayPath: string[],
@@ -209,13 +213,11 @@ export type CogsGlobalState = {
209
213
  dependencyPath: string[],
210
214
  fullComponentId: string
211
215
  ) => void;
212
-
213
216
  markAsDirty: (
214
217
  key: string,
215
218
  path: string[],
216
219
  options: { bubble: boolean }
217
220
  ) => void;
218
- // These method signatures stay the same
219
221
 
220
222
  pathSubscribers: Map<string, Set<(newValue: any) => void>>;
221
223
  subscribeToPath: (
@@ -230,12 +232,8 @@ export type CogsGlobalState = {
230
232
  clearSelectedIndex: ({ arrayKey }: { arrayKey: string }) => void;
231
233
  clearSelectedIndexesForState: (stateKey: string) => void;
232
234
 
233
- // --- Core State and Updaters ---
234
-
235
235
  initialStateOptions: { [key: string]: OptionsType };
236
-
237
236
  initialStateGlobal: { [key: string]: StateValue };
238
-
239
237
  updateInitialStateGlobal: (key: string, newState: StateValue) => void;
240
238
 
241
239
  getInitialOptions: (key: string) => OptionsType | undefined;
@@ -260,46 +258,35 @@ export type CogsGlobalState = {
260
258
  getSyncInfo: (key: string) => SyncInfo | null;
261
259
  };
262
260
 
263
- // ✅ CHANGE 1: `METADATA_KEYS` now only contains `_meta` and `value`.
264
- // The other keys are now properties of the `ShadowMetadata` type.
265
- export const METADATA_KEYS = new Set(['_meta', 'value']);
266
-
267
- /**
268
- * The single source of truth for converting a regular JS value/object
269
- * into the shadow state tree format with the new `_meta` structure.
270
- */
271
- // ✅ CHANGE 2: `buildShadowNode` now creates the `_meta` field.
272
261
  export function buildShadowNode(value: any): ShadowNode {
273
- // Primitives and null are wrapped.
274
262
  if (value === null || typeof value !== 'object') {
275
263
  return { value };
276
264
  }
277
265
 
278
- // Arrays are converted to an object with id-keyed children and metadata in `_meta`.
279
266
  if (Array.isArray(value)) {
280
- const arrayNode: ShadowNode = { _meta: { arrayKeys: [] } }; // Initialize with _meta and arrayKeys
267
+ const arrayNode: ShadowNode = { _meta: { arrayKeys: [] } };
281
268
  const idKeys: string[] = [];
269
+
282
270
  value.forEach((item) => {
283
271
  const itemId = `id:${ulid()}`;
284
- arrayNode[itemId] = buildShadowNode(item); // Recurse for each item
272
+ arrayNode[itemId] = buildShadowNode(item);
285
273
  idKeys.push(itemId);
286
274
  });
287
- arrayNode._meta!.arrayKeys = idKeys; // Set the final ordered keys
275
+
276
+ arrayNode._meta!.arrayKeys = idKeys;
288
277
  return arrayNode;
289
278
  }
290
279
 
291
- // Plain objects are recursively processed.
292
280
  if (value.constructor === Object) {
293
- const objectNode: ShadowNode = { _meta: {} }; // Initialize with an empty meta object
281
+ const objectNode: ShadowNode = { _meta: {} };
294
282
  for (const key in value) {
295
283
  if (Object.prototype.hasOwnProperty.call(value, key)) {
296
- objectNode[key] = buildShadowNode(value[key]); // Recurse for each property
284
+ objectNode[key] = buildShadowNode(value[key]);
297
285
  }
298
286
  }
299
287
  return objectNode;
300
288
  }
301
289
 
302
- // Fallback for other object types (Date, etc.) - treat them as primitives.
303
290
  return { value };
304
291
  }
305
292
 
@@ -312,7 +299,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
312
299
  cacheKey: string,
313
300
  cacheData: any
314
301
  ) => {
315
- // This function now uses setShadowMetadata which correctly places the data.
316
302
  const metadata = get().getShadowMetadata(key, path) || {};
317
303
  if (!metadata.transformCaches) {
318
304
  metadata.transformCaches = new Map();
@@ -350,7 +336,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
350
336
  newShadowStore.delete(`[${key}`);
351
337
 
352
338
  const newRoot = buildShadowNode(initialState);
353
- // Ensure _meta exists before assigning to it
339
+
354
340
  if (!newRoot._meta) newRoot._meta = {};
355
341
  Object.assign(newRoot._meta, preservedMetadata);
356
342
 
@@ -361,7 +347,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
361
347
  });
362
348
  },
363
349
 
364
- // ✅ NEW HELPER: Gets the entire node (data and metadata).
365
350
  getShadowNode: (key: string, path: string[]): ShadowNode | undefined => {
366
351
  const store = get().shadowStateStore;
367
352
  let current: any = store.get(key) || store.get(`[${key}`);
@@ -377,7 +362,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
377
362
  return current;
378
363
  },
379
364
 
380
- // ✅ REFACTORED: Returns only the `_meta` part of a node.
381
365
  getShadowMetadata: (
382
366
  key: string,
383
367
  path: string[]
@@ -386,7 +370,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
386
370
  return node?._meta;
387
371
  },
388
372
 
389
- // ✅ REFACTORED: Sets data within the `_meta` object.
390
373
  setShadowMetadata: (
391
374
  key: string,
392
375
  path: string[],
@@ -408,11 +391,10 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
408
391
  let current = clonedRoot;
409
392
  for (const segment of path) {
410
393
  const nextNode = current[segment] || {};
411
- current[segment] = { ...nextNode }; // Clone for immutability
394
+ current[segment] = { ...nextNode };
412
395
  current = current[segment];
413
396
  }
414
397
 
415
- // Ensure _meta object exists and merge the new metadata into it
416
398
  current._meta = { ...(current._meta || {}), ...newMetadata };
417
399
 
418
400
  return { shadowStateStore: newStore };
@@ -430,9 +412,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
430
412
 
431
413
  const nodeKeys = Object.keys(node);
432
414
 
433
- // ✅ FIX: A node is a primitive wrapper ONLY if its keys are 'value' and/or '_meta'.
434
- // This prevents objects in your data that happen to have a "value" property from being
435
- // incorrectly treated as wrappers.
436
415
  const isPrimitiveWrapper =
437
416
  Object.prototype.hasOwnProperty.call(node, 'value') &&
438
417
  nodeKeys.every((k) => k === 'value' || k === '_meta');
@@ -456,61 +435,77 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
456
435
  );
457
436
  }
458
437
 
459
- // Object Reconstruction (This part is also correct)
460
438
  const result: any = {};
461
439
  for (const propKey of nodeKeys) {
462
- // We correctly ignore metadata and array item keys here.
463
440
  if (propKey !== '_meta' && !propKey.startsWith('id:')) {
464
441
  result[propKey] = get().getShadowValue(key, [...path, propKey]);
465
442
  }
466
443
  }
467
444
  return result;
468
445
  },
469
-
470
- // ✅ REFACTORED: Correctly preserves `_meta` on updates.
471
446
  updateShadowAtPath: (key, path, newValue) => {
472
447
  set((state) => {
473
448
  const newStore = new Map(state.shadowStateStore);
474
449
  const rootKey = newStore.has(`[${key}`) ? `[${key}` : key;
475
450
  let root = newStore.get(rootKey);
476
-
477
451
  if (!root) return state;
478
-
479
452
  const clonedRoot: any = { ...root };
480
453
  newStore.set(rootKey, clonedRoot);
454
+ let parentNode = clonedRoot;
455
+ for (let i = 0; i < path.length - 1; i++) {
456
+ parentNode[path[i]!] = { ...(parentNode[path[i]!] || {}) };
457
+ parentNode = parentNode[path[i]!];
458
+ }
459
+ const targetNode =
460
+ path.length === 0 ? parentNode : parentNode[path[path.length - 1]!];
461
+
462
+ if (!targetNode) {
463
+ parentNode[path[path.length - 1]!] = buildShadowNode(newValue);
464
+ return { shadowStateStore: newStore };
465
+ }
481
466
 
482
- if (path.length === 0) {
483
- const newRootStructure = buildShadowNode(newValue);
484
- // Preserve the top-level metadata
485
- if (clonedRoot._meta) {
486
- newRootStructure._meta = {
487
- ...(newRootStructure._meta || {}),
488
- ...clonedRoot._meta,
489
- };
467
+ function intelligentMerge(nodeToUpdate: any, plainValue: any) {
468
+ if (
469
+ typeof plainValue !== 'object' ||
470
+ plainValue === null ||
471
+ Array.isArray(plainValue)
472
+ ) {
473
+ const oldMeta = nodeToUpdate._meta;
474
+ const newNode = buildShadowNode(plainValue);
475
+ if (oldMeta) {
476
+ newNode._meta = { ...oldMeta, ...(newNode._meta || {}) };
477
+ }
478
+ Object.keys(nodeToUpdate).forEach((key) => delete nodeToUpdate[key]);
479
+ Object.assign(nodeToUpdate, newNode);
480
+ return;
490
481
  }
491
- newStore.set(rootKey, newRootStructure);
492
- } else {
493
- let current = clonedRoot;
494
- const parentPath = path.slice(0, -1);
495
- for (const segment of parentPath) {
496
- current[segment] = { ...current[segment] };
497
- current = current[segment];
482
+
483
+ const plainValueKeys = new Set(Object.keys(plainValue));
484
+
485
+ for (const propKey of plainValueKeys) {
486
+ const childValue = plainValue[propKey];
487
+ if (nodeToUpdate[propKey]) {
488
+ intelligentMerge(nodeToUpdate[propKey], childValue);
489
+ } else {
490
+ nodeToUpdate[propKey] = buildShadowNode(childValue);
491
+ }
498
492
  }
499
493
 
500
- const lastSegment = path[path.length - 1]!;
501
- const existingNode = current[lastSegment] || {};
502
- const newNodeStructure = buildShadowNode(newValue);
494
+ for (const nodeKey in nodeToUpdate) {
495
+ if (
496
+ nodeKey === '_meta' ||
497
+ !Object.prototype.hasOwnProperty.call(nodeToUpdate, nodeKey)
498
+ )
499
+ continue;
503
500
 
504
- // This merge is critical: it preserves existing metadata during an update.
505
- if (existingNode._meta) {
506
- newNodeStructure._meta = {
507
- ...(newNodeStructure._meta || {}),
508
- ...existingNode._meta,
509
- };
501
+ if (!plainValueKeys.has(nodeKey)) {
502
+ delete nodeToUpdate[nodeKey];
503
+ }
510
504
  }
511
- current[lastSegment] = newNodeStructure;
512
505
  }
513
506
 
507
+ intelligentMerge(targetNode, newValue);
508
+
514
509
  get().notifyPathSubscribers([key, ...path].join('.'), {
515
510
  type: 'UPDATE',
516
511
  newValue,
@@ -518,8 +513,33 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
518
513
  return { shadowStateStore: newStore };
519
514
  });
520
515
  },
516
+ addItemsToArrayNode: (key, arrayPath, newItems, newKeys) => {
517
+ set((state) => {
518
+ const newStore = new Map(state.shadowStateStore);
519
+ const rootKey = newStore.has(`[${key}`) ? `[${key}` : key;
520
+ let root = newStore.get(rootKey);
521
+ if (!root) {
522
+ console.error('Root not found for state key:', key);
523
+ return state;
524
+ }
525
+
526
+ const clonedRoot = { ...root };
527
+ newStore.set(rootKey, clonedRoot);
528
+
529
+ let current = clonedRoot;
530
+ for (const segment of arrayPath) {
531
+ const nextNode = current[segment] || {};
532
+ current[segment] = { ...nextNode };
533
+ current = current[segment];
534
+ }
535
+
536
+ Object.assign(current, newItems);
537
+ current._meta = { ...(current._meta || {}), arrayKeys: newKeys };
538
+
539
+ return { shadowStateStore: newStore };
540
+ });
541
+ },
521
542
 
522
- // ✅ REFACTORED: Works with `_meta.arrayKeys`.
523
543
  insertShadowArrayElement: (key, arrayPath, newItem, index) => {
524
544
  const arrayNode = get().getShadowNode(key, arrayPath);
525
545
  if (!arrayNode?._meta?.arrayKeys) {
@@ -530,53 +550,67 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
530
550
  }
531
551
 
532
552
  const newItemId = `id:${ulid()}`;
533
- const newItemNode = buildShadowNode(newItem);
553
+ const itemsToAdd = { [newItemId]: buildShadowNode(newItem) };
534
554
 
535
- // Update the `arrayKeys` in the metadata
536
555
  const currentKeys = arrayNode._meta.arrayKeys;
537
556
  const newKeys = [...currentKeys];
538
- if (index !== undefined && index >= 0 && index <= newKeys.length) {
539
- newKeys.splice(index, 0, newItemId);
540
- } else {
541
- newKeys.push(newItemId);
557
+ const insertionPoint =
558
+ index !== undefined && index >= 0 && index <= newKeys.length
559
+ ? index
560
+ : newKeys.length;
561
+ newKeys.splice(insertionPoint, 0, newItemId);
562
+
563
+ get().addItemsToArrayNode(key, arrayPath, itemsToAdd, newKeys);
564
+
565
+ const arrayKey = [key, ...arrayPath].join('.');
566
+ get().notifyPathSubscribers(arrayKey, {
567
+ type: 'INSERT',
568
+ path: arrayKey,
569
+ itemKey: `${arrayKey}.${newItemId}`,
570
+ index: insertionPoint,
571
+ });
572
+ },
573
+ insertManyShadowArrayElements: (key, arrayPath, newItems, index) => {
574
+ if (!newItems || newItems.length === 0) {
575
+ return;
542
576
  }
543
577
 
544
- // Update transform caches if they exist
545
- if (arrayNode._meta.transformCaches) {
546
- arrayNode._meta.transformCaches.forEach((cache) => {
547
- if (cache.validIds && Array.isArray(cache.validIds)) {
548
- const matchesFilters = cache.transforms.every((transform) =>
549
- transform.type === 'filter' ? transform.fn(newItem) : true
550
- );
551
- if (matchesFilters) {
552
- cache.validIds = [...cache.validIds];
553
- if (index !== undefined) {
554
- cache.validIds.splice(index, 0, newItemId);
555
- } else {
556
- cache.validIds.push(newItemId);
557
- }
558
- }
559
- }
560
- });
578
+ const arrayNode = get().getShadowNode(key, arrayPath);
579
+ if (!arrayNode?._meta?.arrayKeys) {
580
+ console.error(
581
+ `Array not found at path: ${[key, ...arrayPath].join('.')}`
582
+ );
583
+ return;
561
584
  }
562
585
 
563
- // Directly set the new item and updated metadata on the node before setting state
564
- arrayNode[newItemId] = newItemNode;
565
- arrayNode._meta.arrayKeys = newKeys;
586
+ const itemsToAdd: Record<string, any> = {};
587
+ const newIds: string[] = [];
566
588
 
567
- get().setShadowMetadata(key, arrayPath, { arrayKeys: newKeys });
589
+ newItems.forEach((item) => {
590
+ const newItemId = `id:${ulid()}`;
591
+ newIds.push(newItemId);
592
+ itemsToAdd[newItemId] = buildShadowNode(item);
593
+ });
594
+
595
+ const currentKeys = arrayNode._meta.arrayKeys;
596
+ const finalKeys = [...currentKeys];
597
+ const insertionPoint =
598
+ index !== undefined && index >= 0 && index <= finalKeys.length
599
+ ? index
600
+ : finalKeys.length;
601
+ finalKeys.splice(insertionPoint, 0, ...newIds);
602
+
603
+ get().addItemsToArrayNode(key, arrayPath, itemsToAdd, finalKeys);
568
604
 
569
- // Trigger notifications
570
605
  const arrayKey = [key, ...arrayPath].join('.');
571
606
  get().notifyPathSubscribers(arrayKey, {
572
- type: 'INSERT',
607
+ type: 'INSERT_MANY',
573
608
  path: arrayKey,
574
- itemKey: `${arrayKey}.${newItemId}`,
575
- index: index ?? newKeys.length - 1,
609
+ count: newItems.length,
610
+ index: insertionPoint,
576
611
  });
577
612
  },
578
613
 
579
- // ✅ REFACTORED: Works with `_meta.arrayKeys`.
580
614
  removeShadowArrayElement: (key, itemPath) => {
581
615
  if (itemPath.length === 0) return;
582
616
 
@@ -587,13 +621,9 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
587
621
  const arrayNode = get().getShadowNode(key, arrayPath);
588
622
  if (!arrayNode?._meta?.arrayKeys) return;
589
623
 
590
- // Filter the item's ID from the `arrayKeys` metadata
591
624
  const newKeys = arrayNode._meta.arrayKeys.filter((k) => k !== itemId);
592
-
593
- // Delete the item's data from the node
594
625
  delete arrayNode[itemId];
595
626
 
596
- // Persist the modified array node back to the store
597
627
  get().setShadowMetadata(key, arrayPath, { arrayKeys: newKeys });
598
628
 
599
629
  const arrayKey = [key, ...arrayPath].join('.');
@@ -604,9 +634,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
604
634
  });
605
635
  },
606
636
 
607
- // The rest of the functions are updated to use the new helpers (`getShadowMetadata`, `setShadowMetadata`)
608
- // which abstracts away the `_meta` implementation detail.
609
-
610
637
  addPathComponent: (stateKey, dependencyPath, fullComponentId) => {
611
638
  const metadata = get().getShadowMetadata(stateKey, dependencyPath) || {};
612
639
  const newPathComponents = new Set(metadata.pathComponents);
@@ -646,15 +673,14 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
646
673
  }
647
674
  },
648
675
 
649
- // ✅ REFACTORED: `markAsDirty` now correctly writes to `_meta.isDirty`.
650
676
  markAsDirty: (key, path, options = { bubble: true }) => {
651
677
  const setDirtyOnPath = (pathToMark: string[]) => {
652
678
  const node = get().getShadowNode(key, pathToMark);
653
679
  if (node?._meta?.isDirty) {
654
- return true; // Already dirty, stop bubbling
680
+ return true;
655
681
  }
656
682
  get().setShadowMetadata(key, pathToMark, { isDirty: true });
657
- return false; // Was not dirty before
683
+ return false;
658
684
  };
659
685
 
660
686
  setDirtyOnPath(path);
@@ -664,7 +690,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
664
690
  while (parentPath.length > 0) {
665
691
  parentPath.pop();
666
692
  if (setDirtyOnPath(parentPath)) {
667
- break; // Stop if parent was already dirty
693
+ break;
668
694
  }
669
695
  }
670
696
  }
@@ -708,7 +734,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
708
734
  subs.forEach((callback) => callback(newValue));
709
735
  }
710
736
  },
711
-
712
737
  selectedIndicesMap: new Map<string, string>(),
713
738
  getSelectedIndex: (arrayKey, validIds) => {
714
739
  const itemKey = get().selectedIndicesMap.get(arrayKey);
@@ -723,39 +748,51 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
723
748
  return arrayKeys ? arrayKeys.indexOf(itemKey) : -1;
724
749
  },
725
750
 
726
- setSelectedIndex: (arrayKey, itemKey) => {
751
+ setSelectedIndex: (arrayKey: string, itemKey: string | undefined) => {
727
752
  set((state) => {
728
- const newMap = new Map(state.selectedIndicesMap);
729
- const oldSelection = newMap.get(arrayKey);
730
- if (oldSelection) {
731
- get().notifyPathSubscribers(oldSelection, { type: 'THIS_UNSELECTED' });
732
- }
753
+ const newMap = new Map(state.selectedIndicesMap); // CREATE A NEW MAP!
733
754
 
734
755
  if (itemKey === undefined) {
735
756
  newMap.delete(arrayKey);
736
757
  } else {
758
+ if (newMap.has(arrayKey)) {
759
+ get().notifyPathSubscribers(newMap.get(arrayKey)!, {
760
+ type: 'THIS_UNSELECTED',
761
+ });
762
+ }
737
763
  newMap.set(arrayKey, itemKey);
738
764
  get().notifyPathSubscribers(itemKey, { type: 'THIS_SELECTED' });
739
765
  }
740
766
 
741
767
  get().notifyPathSubscribers(arrayKey, { type: 'GET_SELECTED' });
742
- return { selectedIndicesMap: newMap };
768
+
769
+ return {
770
+ ...state,
771
+ selectedIndicesMap: newMap,
772
+ };
743
773
  });
744
774
  },
745
775
 
746
- clearSelectedIndex: ({ arrayKey }) => {
776
+ clearSelectedIndex: ({ arrayKey }: { arrayKey: string }): void => {
747
777
  set((state) => {
748
- const newMap = new Map(state.selectedIndicesMap);
778
+ const newMap = new Map(state.selectedIndicesMap); // CREATE A NEW MAP!
749
779
  const actualKey = newMap.get(arrayKey);
750
780
  if (actualKey) {
751
- get().notifyPathSubscribers(actualKey, { type: 'CLEAR_SELECTION' });
781
+ get().notifyPathSubscribers(actualKey, {
782
+ type: 'CLEAR_SELECTION',
783
+ });
752
784
  }
785
+
753
786
  newMap.delete(arrayKey);
754
- get().notifyPathSubscribers(arrayKey, { type: 'CLEAR_SELECTION' });
755
- return { selectedIndicesMap: newMap };
787
+ get().notifyPathSubscribers(arrayKey, {
788
+ type: 'CLEAR_SELECTION',
789
+ });
790
+ return {
791
+ ...state,
792
+ selectedIndicesMap: newMap,
793
+ };
756
794
  });
757
795
  },
758
-
759
796
  clearSelectedIndexesForState: (stateKey) => {
760
797
  set((state) => {
761
798
  const newMap = new Map(state.selectedIndicesMap);
@@ -1,11 +0,0 @@
1
- import { FormOptsType } from './CogsState';
2
- import { default as React } from 'react';
3
-
4
- export type ValidationWrapperProps = {
5
- formOpts?: FormOptsType;
6
- path: string[];
7
- stateKey: string;
8
- children: React.ReactNode;
9
- };
10
- export declare function ValidationWrapper({ formOpts, path, stateKey, children, }: ValidationWrapperProps): import("react/jsx-runtime").JSX.Element;
11
- //# sourceMappingURL=Functions.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Functions.d.ts","sourceRoot":"","sources":["../src/Functions.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,sBAAsB,2CAgDxB"}
@@ -1,29 +0,0 @@
1
- import { jsx as o, Fragment as w } from "react/jsx-runtime";
2
- import d from "react";
3
- import { getGlobalStore as M } from "./store.js";
4
- function F({
5
- formOpts: a,
6
- path: e,
7
- stateKey: s,
8
- children: n
9
- }) {
10
- const { getInitialOptions: c, getShadowMetadata: v, getShadowValue: S } = M.getState(), i = c(s), g = v(s, e)?.validation, f = g?.status || "NOT_VALIDATED", r = (g?.errors || []).map((t) => ({
11
- ...t,
12
- path: e
13
- })), l = r.filter((t) => t.severity === "error").map((t) => t.message), m = r.filter((t) => t.severity === "warning").map((t) => t.message), h = l[0] || m[0];
14
- return /* @__PURE__ */ o(w, { children: i?.formElements?.validation && !a?.validation?.disable ? i.formElements.validation({
15
- children: /* @__PURE__ */ o(d.Fragment, { children: n }, e.toString()),
16
- status: f,
17
- // Now passes the new ValidationStatus type
18
- message: a?.validation?.hideMessage ? "" : a?.validation?.message || h || "",
19
- hasErrors: l.length > 0,
20
- hasWarnings: m.length > 0,
21
- allErrors: r,
22
- path: e,
23
- getData: () => S(s, e)
24
- }) : /* @__PURE__ */ o(d.Fragment, { children: n }, e.toString()) });
25
- }
26
- export {
27
- F as ValidationWrapper
28
- };
29
- //# sourceMappingURL=Functions.jsx.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Functions.jsx","sources":["../src/Functions.tsx"],"sourcesContent":["import { type FormOptsType } from './CogsState';\r\nimport React from 'react';\r\nimport { getGlobalStore, ValidationError } from './store';\r\nimport { get } from 'http';\r\n\r\nexport type ValidationWrapperProps = {\r\n formOpts?: FormOptsType;\r\n path: string[];\r\n stateKey: string;\r\n children: React.ReactNode;\r\n};\r\n\r\nexport function ValidationWrapper({\r\n formOpts,\r\n path,\r\n stateKey,\r\n children,\r\n}: ValidationWrapperProps) {\r\n const { getInitialOptions, getShadowMetadata, getShadowValue } =\r\n getGlobalStore.getState();\r\n const thisStateOpts = getInitialOptions(stateKey!);\r\n\r\n const shadowMeta = getShadowMetadata(stateKey!, path);\r\n const validationState = shadowMeta?.validation;\r\n\r\n const status = validationState?.status || 'NOT_VALIDATED';\r\n\r\n const errors = (validationState?.errors || []).map((err) => ({\r\n ...err,\r\n path: path,\r\n })) as ValidationError[];\r\n const errorMessages = errors\r\n .filter((err) => err.severity === 'error')\r\n .map((err) => err.message);\r\n const warningMessages = errors\r\n .filter((err) => err.severity === 'warning')\r\n .map((err) => err.message);\r\n\r\n // Use first error, or first warning if no errors\r\n const message = errorMessages[0] || warningMessages[0];\r\n\r\n return (\r\n <>\r\n {thisStateOpts?.formElements?.validation &&\r\n !formOpts?.validation?.disable ? (\r\n thisStateOpts.formElements!.validation!({\r\n children: (\r\n <React.Fragment key={path.toString()}>{children}</React.Fragment>\r\n ),\r\n status, // Now passes the new ValidationStatus type\r\n message: formOpts?.validation?.hideMessage\r\n ? ''\r\n : formOpts?.validation?.message || message || '',\r\n\r\n hasErrors: errorMessages.length > 0,\r\n hasWarnings: warningMessages.length > 0,\r\n allErrors: errors,\r\n path: path,\r\n getData: () => getShadowValue(stateKey!, path),\r\n })\r\n ) : (\r\n <React.Fragment key={path.toString()}>{children}</React.Fragment>\r\n )}\r\n </>\r\n );\r\n}\r\n"],"names":["ValidationWrapper","formOpts","path","stateKey","children","getInitialOptions","getShadowMetadata","getShadowValue","getGlobalStore","thisStateOpts","validationState","status","errors","err","errorMessages","warningMessages","message","jsx","Fragment","React"],"mappings":";;;AAYO,SAASA,EAAkB;AAAA,EAChC,UAAAC;AAAA,EACA,MAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AACF,GAA2B;AACzB,QAAM,EAAE,mBAAAC,GAAmB,mBAAAC,GAAmB,gBAAAC,EAAA,IAC5CC,EAAe,SAAA,GACXC,IAAgBJ,EAAkBF,CAAS,GAG3CO,IADaJ,EAAkBH,GAAWD,CAAI,GAChB,YAE9BS,IAASD,GAAiB,UAAU,iBAEpCE,KAAUF,GAAiB,UAAU,CAAA,GAAI,IAAI,CAACG,OAAS;AAAA,IAC3D,GAAGA;AAAA,IACH,MAAAX;AAAA,EAAA,EACA,GACIY,IAAgBF,EACnB,OAAO,CAACC,MAAQA,EAAI,aAAa,OAAO,EACxC,IAAI,CAACA,MAAQA,EAAI,OAAO,GACrBE,IAAkBH,EACrB,OAAO,CAACC,MAAQA,EAAI,aAAa,SAAS,EAC1C,IAAI,CAACA,MAAQA,EAAI,OAAO,GAGrBG,IAAUF,EAAc,CAAC,KAAKC,EAAgB,CAAC;AAErD,SACE,gBAAAE,EAAAC,GAAA,EACG,UAAAT,GAAe,cAAc,cAC9B,CAACR,GAAU,YAAY,UACrBQ,EAAc,aAAc,WAAY;AAAA,IACtC,4BACGU,EAAM,UAAN,EAAsC,UAAAf,KAAlBF,EAAK,UAAsB;AAAA,IAElD,QAAAS;AAAA;AAAA,IACA,SAASV,GAAU,YAAY,cAC3B,KACAA,GAAU,YAAY,WAAWe,KAAW;AAAA,IAEhD,WAAWF,EAAc,SAAS;AAAA,IAClC,aAAaC,EAAgB,SAAS;AAAA,IACtC,WAAWH;AAAA,IACX,MAAAV;AAAA,IACA,SAAS,MAAMK,EAAeJ,GAAWD,CAAI;AAAA,EAAA,CAC9C,IAED,gBAAAe,EAACE,EAAM,UAAN,EAAsC,UAAAf,EAAA,GAAlBF,EAAK,SAAA,CAAsB,GAEpD;AAEJ;"}