cogsbox-state 0.5.426 → 0.5.428
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/CogsState.d.ts +1 -1
- package/dist/CogsState.jsx +793 -857
- package/dist/CogsState.jsx.map +1 -1
- package/dist/Functions.jsx +134 -136
- package/dist/Functions.jsx.map +1 -1
- package/dist/index.js +9 -8
- package/dist/store.d.ts +18 -8
- package/dist/store.js +165 -186
- package/dist/store.js.map +1 -1
- package/dist/utility.d.ts +1 -0
- package/dist/utility.js +165 -121
- package/dist/utility.js.map +1 -1
- package/package.json +7 -2
- package/src/CogsState.tsx +385 -694
- package/src/Functions.tsx +47 -39
- package/src/store.ts +172 -195
- package/src/utility.ts +88 -10
package/src/Functions.tsx
CHANGED
|
@@ -7,11 +7,17 @@ import {
|
|
|
7
7
|
type UpdateOpts,
|
|
8
8
|
} from "./CogsState";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getNestedValue,
|
|
12
|
+
isFunction,
|
|
13
|
+
updateNestedProperty,
|
|
14
|
+
updateNestedPropertyIds,
|
|
15
|
+
} from "./utility";
|
|
11
16
|
import { useEffect, useRef, useState } from "react";
|
|
12
17
|
import React from "react";
|
|
13
18
|
import { getGlobalStore, formRefStore } from "./store";
|
|
14
19
|
import { validateZodPathFunc } from "./useValidateZodPath";
|
|
20
|
+
import { ulid } from "ulid";
|
|
15
21
|
|
|
16
22
|
export function updateFn<U>(
|
|
17
23
|
setState: EffectiveSetState<U>,
|
|
@@ -23,7 +29,9 @@ export function updateFn<U>(
|
|
|
23
29
|
(prevState) => {
|
|
24
30
|
if (isFunction<U>(payload)) {
|
|
25
31
|
const nestedValue = payload(getNestedValue(prevState, path));
|
|
26
|
-
|
|
32
|
+
console.group("nestedValue", path, nestedValue);
|
|
33
|
+
let value = updateNestedPropertyIds(path, prevState, nestedValue);
|
|
34
|
+
console.group("updateFn", value);
|
|
27
35
|
if (typeof value == "string") {
|
|
28
36
|
value = value.trim();
|
|
29
37
|
}
|
|
@@ -32,7 +40,7 @@ export function updateFn<U>(
|
|
|
32
40
|
let value =
|
|
33
41
|
!path || path.length == 0
|
|
34
42
|
? payload
|
|
35
|
-
:
|
|
43
|
+
: updateNestedPropertyIds(path, prevState, payload);
|
|
36
44
|
if (typeof value == "string") {
|
|
37
45
|
value = value.trim();
|
|
38
46
|
}
|
|
@@ -44,7 +52,6 @@ export function updateFn<U>(
|
|
|
44
52
|
validationKey
|
|
45
53
|
);
|
|
46
54
|
}
|
|
47
|
-
|
|
48
55
|
export function pushFunc<U>(
|
|
49
56
|
setState: EffectiveSetState<U>,
|
|
50
57
|
payload: UpdateArg<U>,
|
|
@@ -52,68 +59,69 @@ export function pushFunc<U>(
|
|
|
52
59
|
stateKey: string,
|
|
53
60
|
index?: number
|
|
54
61
|
): void {
|
|
55
|
-
|
|
62
|
+
// --- THE FIX ---
|
|
63
|
+
// 1. Determine the newItem and its ID BEFORE calling setState.
|
|
64
|
+
const arrayBeforeUpdate =
|
|
65
|
+
(getGlobalStore.getState().getNestedState(stateKey, path) as any[]) || [];
|
|
66
|
+
|
|
67
|
+
const newItem = isFunction<U>(payload)
|
|
68
|
+
? payload(arrayBeforeUpdate as any)
|
|
69
|
+
: payload;
|
|
70
|
+
|
|
71
|
+
// 2. Ensure it has an ID.
|
|
72
|
+
if (typeof newItem === "object" && newItem !== null && !(newItem as any).id) {
|
|
73
|
+
(newItem as any).id = ulid();
|
|
74
|
+
}
|
|
75
|
+
const finalId = (newItem as any).id;
|
|
76
|
+
// --- END OF FIX ---
|
|
77
|
+
|
|
56
78
|
setState(
|
|
57
79
|
(prevState) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
returnedArray.splice(
|
|
65
|
-
index || Number(index) == 0 ? index : arrayToUpdate.length,
|
|
66
|
-
0,
|
|
67
|
-
isFunction<U>(payload)
|
|
68
|
-
? payload(index == -1 ? undefined : arrayToUpdate)
|
|
69
|
-
: payload
|
|
70
|
-
);
|
|
71
|
-
const value =
|
|
72
|
-
path.length == 0
|
|
73
|
-
? returnedArray
|
|
74
|
-
: updateNestedProperty([...path], prevState, returnedArray);
|
|
75
|
-
|
|
76
|
-
return value as U;
|
|
80
|
+
// The logic inside here is now much simpler.
|
|
81
|
+
// We already have the final `newItem`.
|
|
82
|
+
const arrayToUpdate = getNestedValue(prevState, [...path]) || [];
|
|
83
|
+
const newArray = [...arrayToUpdate];
|
|
84
|
+
newArray.splice(index ?? newArray.length, 0, newItem);
|
|
85
|
+
return updateNestedPropertyIds([...path], prevState, newArray);
|
|
77
86
|
},
|
|
78
|
-
[
|
|
79
|
-
...path,
|
|
80
|
-
index || index === 0 ? index?.toString() : (array!.length - 1).toString(),
|
|
81
|
-
],
|
|
87
|
+
[...path, `id:${finalId}`], // Now we use the ID that is guaranteed to be correct.
|
|
82
88
|
{
|
|
83
89
|
updateType: "insert",
|
|
84
90
|
}
|
|
85
91
|
);
|
|
86
92
|
}
|
|
87
|
-
|
|
88
93
|
export function cutFunc<U>(
|
|
89
94
|
setState: EffectiveSetState<U>,
|
|
90
95
|
path: string[],
|
|
91
96
|
stateKey: string,
|
|
92
97
|
index: number
|
|
93
98
|
): void {
|
|
94
|
-
|
|
99
|
+
// Get the ordered IDs to find the ID for this index
|
|
100
|
+
const arrayKey = [stateKey, ...path].join(".");
|
|
101
|
+
const arrayMeta = getGlobalStore.getState().shadowStateStore.get(arrayKey);
|
|
102
|
+
const itemId = arrayMeta?.arrayKeys?.[index];
|
|
103
|
+
|
|
104
|
+
if (!itemId) {
|
|
105
|
+
throw new Error(`No ID found for index ${index} in array`);
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
setState(
|
|
96
109
|
(prevState) => {
|
|
97
110
|
const arrayToUpdate = getNestedValue(prevState, [...path]);
|
|
98
111
|
if (index < 0 || index >= arrayToUpdate?.length) {
|
|
99
112
|
throw new Error(`Index ${index} does not exist in the array.`);
|
|
100
113
|
}
|
|
101
|
-
const indexToCut =
|
|
102
|
-
index || Number(index) == 0 ? index : arrayToUpdate.length - 1;
|
|
103
114
|
|
|
104
115
|
const updatedArray = [
|
|
105
|
-
...arrayToUpdate.slice(0,
|
|
106
|
-
...arrayToUpdate.slice(
|
|
116
|
+
...arrayToUpdate.slice(0, index),
|
|
117
|
+
...arrayToUpdate.slice(index + 1),
|
|
107
118
|
] as U;
|
|
108
119
|
|
|
109
120
|
return path.length == 0
|
|
110
121
|
? updatedArray
|
|
111
|
-
:
|
|
122
|
+
: updateNestedPropertyIds([...path], prevState, updatedArray);
|
|
112
123
|
},
|
|
113
|
-
[
|
|
114
|
-
...path,
|
|
115
|
-
index || index === 0 ? index?.toString() : (array!.length - 1).toString(),
|
|
116
|
-
],
|
|
124
|
+
[...path, itemId], // Use the ID here!
|
|
117
125
|
{ updateType: "cut" }
|
|
118
126
|
);
|
|
119
127
|
}
|
package/src/store.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
|
+
import { ulid } from "ulid";
|
|
2
3
|
import type {
|
|
3
4
|
OptionsType,
|
|
4
5
|
ReactivityType,
|
|
@@ -86,24 +87,21 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
|
|
|
86
87
|
},
|
|
87
88
|
}));
|
|
88
89
|
|
|
89
|
-
type ShadowMetadata = {
|
|
90
|
-
|
|
90
|
+
export type ShadowMetadata = {
|
|
91
|
+
id: string;
|
|
92
|
+
arrayKeys?: string[];
|
|
93
|
+
virtualizer?: {
|
|
94
|
+
itemHeight?: number;
|
|
95
|
+
domRef?: HTMLElement | null;
|
|
96
|
+
};
|
|
91
97
|
syncInfo?: { status: string };
|
|
92
|
-
|
|
98
|
+
lastUpdated?: number;
|
|
93
99
|
};
|
|
94
|
-
|
|
95
|
-
type ShadowState<T> =
|
|
96
|
-
T extends Array<infer U>
|
|
97
|
-
? Array<ShadowState<U>> & ShadowMetadata
|
|
98
|
-
: T extends object
|
|
99
|
-
? { [K in keyof T]: ShadowState<T[K]> } & ShadowMetadata
|
|
100
|
-
: ShadowMetadata;
|
|
101
|
-
|
|
102
100
|
export type CogsGlobalState = {
|
|
103
101
|
// --- Shadow State and Subscription System ---
|
|
104
|
-
shadowStateStore:
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
shadowStateStore: Map<string, ShadowMetadata>;
|
|
103
|
+
|
|
104
|
+
// These method signatures stay the same
|
|
107
105
|
initializeShadowState: (key: string, initialState: any) => void;
|
|
108
106
|
updateShadowAtPath: (key: string, path: string[], newValue: any) => void;
|
|
109
107
|
insertShadowArrayElement: (
|
|
@@ -111,15 +109,20 @@ export type CogsGlobalState = {
|
|
|
111
109
|
arrayPath: string[],
|
|
112
110
|
newItem: any
|
|
113
111
|
) => void;
|
|
114
|
-
removeShadowArrayElement: (
|
|
112
|
+
removeShadowArrayElement: (key: string, arrayPath: string[]) => void;
|
|
113
|
+
getShadowMetadata: (
|
|
115
114
|
key: string,
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
path: string[]
|
|
116
|
+
) => ShadowMetadata | undefined;
|
|
117
|
+
setShadowMetadata: (
|
|
118
|
+
key: string,
|
|
119
|
+
path: string[],
|
|
120
|
+
metadata: Omit<ShadowMetadata, "id">
|
|
118
121
|
) => void;
|
|
119
|
-
getShadowMetadata: (key: string, path: string[]) => any;
|
|
120
|
-
setShadowMetadata: (key: string, path: string[], metadata: any) => void;
|
|
121
122
|
|
|
122
|
-
//
|
|
123
|
+
shadowStateSubscribers: Map<string, Set<() => void>>; // Stores subscribers for shadow state updates
|
|
124
|
+
subscribeToShadowState: (key: string, callback: () => void) => () => void;
|
|
125
|
+
|
|
123
126
|
selectedIndicesMap: Map<string, Map<string, number>>; // stateKey -> (parentPath -> selectedIndex)
|
|
124
127
|
getSelectedIndex: (
|
|
125
128
|
stateKey: string,
|
|
@@ -242,192 +245,163 @@ export type CogsGlobalState = {
|
|
|
242
245
|
};
|
|
243
246
|
|
|
244
247
|
export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
245
|
-
shadowStateStore:
|
|
246
|
-
|
|
247
|
-
const shadow = get().shadowStateStore[key];
|
|
248
|
-
if (!shadow) return null;
|
|
249
|
-
|
|
250
|
-
let current = shadow;
|
|
251
|
-
for (const segment of path) {
|
|
252
|
-
current = current?.[segment];
|
|
253
|
-
if (!current) return null;
|
|
254
|
-
}
|
|
248
|
+
shadowStateStore: new Map(),
|
|
249
|
+
shadowStateSubscribers: new Map(),
|
|
255
250
|
|
|
256
|
-
|
|
257
|
-
|
|
251
|
+
subscribeToShadowState: (key: string, callback: () => void) => {
|
|
252
|
+
set((state) => {
|
|
253
|
+
const newSubs = new Map(state.shadowStateSubscribers);
|
|
254
|
+
const subsForKey = newSubs.get(key) || new Set();
|
|
255
|
+
subsForKey.add(callback);
|
|
256
|
+
newSubs.set(key, subsForKey);
|
|
257
|
+
return { shadowStateSubscribers: newSubs };
|
|
258
|
+
});
|
|
258
259
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
shadow[k] = createShadowStructure(obj[k]);
|
|
260
|
+
// Return unsubscribe function
|
|
261
|
+
return () => {
|
|
262
|
+
set((state) => {
|
|
263
|
+
const newSubs = new Map(state.shadowStateSubscribers);
|
|
264
|
+
const subsForKey = newSubs.get(key);
|
|
265
|
+
if (subsForKey) {
|
|
266
|
+
subsForKey.delete(callback);
|
|
267
|
+
if (subsForKey.size === 0) {
|
|
268
|
+
newSubs.delete(key);
|
|
269
|
+
}
|
|
270
270
|
}
|
|
271
|
-
return
|
|
272
|
-
}
|
|
273
|
-
return {}; // Leaf node - empty object for metadata
|
|
271
|
+
return { shadowStateSubscribers: newSubs };
|
|
272
|
+
});
|
|
274
273
|
};
|
|
275
|
-
|
|
276
|
-
set((state) => ({
|
|
277
|
-
shadowStateStore: {
|
|
278
|
-
...state.shadowStateStore,
|
|
279
|
-
[key]: createShadowStructure(initialState),
|
|
280
|
-
},
|
|
281
|
-
}));
|
|
282
274
|
},
|
|
275
|
+
initializeShadowState: (key: string, initialState: any) => {
|
|
276
|
+
const newShadowStore = new Map<string, ShadowMetadata>();
|
|
283
277
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const newShadow = { ...state.shadowStateStore };
|
|
287
|
-
if (!newShadow[key]) return state;
|
|
288
|
-
|
|
289
|
-
let current = newShadow[key];
|
|
290
|
-
const pathCopy = [...path];
|
|
291
|
-
const lastSegment = pathCopy.pop();
|
|
292
|
-
|
|
293
|
-
// Navigate to parent
|
|
294
|
-
for (const segment of pathCopy) {
|
|
295
|
-
if (!current[segment]) current[segment] = {};
|
|
296
|
-
current = current[segment];
|
|
297
|
-
}
|
|
278
|
+
const processValue = (value: any, path: string[]) => {
|
|
279
|
+
const nodeKey = [key, ...path].join(".");
|
|
298
280
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (Array.isArray(newValue)) {
|
|
302
|
-
current[lastSegment] = new Array(newValue.length);
|
|
303
|
-
} else if (typeof newValue === "object" && newValue !== null) {
|
|
304
|
-
current[lastSegment] = {};
|
|
305
|
-
} else {
|
|
306
|
-
current[lastSegment] = current[lastSegment] || {};
|
|
307
|
-
}
|
|
308
|
-
}
|
|
281
|
+
if (Array.isArray(value)) {
|
|
282
|
+
const childIds: string[] = [];
|
|
309
283
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
284
|
+
value.forEach((item) => {
|
|
285
|
+
if (typeof item === "object" && item !== null && !item.id) {
|
|
286
|
+
item.id = ulid();
|
|
287
|
+
}
|
|
313
288
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
arrayPath: string[],
|
|
317
|
-
newItem: any
|
|
318
|
-
) => {
|
|
319
|
-
set((state) => {
|
|
320
|
-
const newShadow = { ...state.shadowStateStore };
|
|
321
|
-
if (!newShadow[key]) return state;
|
|
289
|
+
const itemId = `id:${item.id}`;
|
|
290
|
+
childIds.push(itemId);
|
|
322
291
|
|
|
323
|
-
|
|
292
|
+
const itemPath = [...path, itemId];
|
|
293
|
+
processValue(item, itemPath);
|
|
294
|
+
});
|
|
324
295
|
|
|
325
|
-
|
|
296
|
+
const arrayContainerMetadata: ShadowMetadata = {
|
|
297
|
+
id: ulid(),
|
|
298
|
+
arrayKeys: childIds,
|
|
299
|
+
};
|
|
300
|
+
newShadowStore.set(nodeKey, arrayContainerMetadata);
|
|
301
|
+
} else if (typeof value === "object" && value !== null) {
|
|
302
|
+
newShadowStore.set(nodeKey, { id: ulid() });
|
|
326
303
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
304
|
+
Object.keys(value).forEach((k) => {
|
|
305
|
+
processValue(value[k], [...path, k]);
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
newShadowStore.set(nodeKey, { id: ulid() });
|
|
330
309
|
}
|
|
310
|
+
};
|
|
331
311
|
|
|
332
|
-
|
|
333
|
-
// Create shadow structure based on the actual new item
|
|
334
|
-
const createShadowStructure = (obj: any): any => {
|
|
335
|
-
if (Array.isArray(obj)) {
|
|
336
|
-
return obj.map((item) => createShadowStructure(item));
|
|
337
|
-
}
|
|
338
|
-
if (typeof obj === "object" && obj !== null) {
|
|
339
|
-
const shadow: any = {};
|
|
340
|
-
for (const k in obj) {
|
|
341
|
-
shadow[k] = createShadowStructure(obj[k]);
|
|
342
|
-
}
|
|
343
|
-
return shadow;
|
|
344
|
-
}
|
|
345
|
-
return {}; // Leaf nodes get empty object for metadata
|
|
346
|
-
};
|
|
312
|
+
processValue(initialState, []);
|
|
347
313
|
|
|
348
|
-
|
|
349
|
-
|
|
314
|
+
set({ shadowStateStore: newShadowStore });
|
|
315
|
+
},
|
|
316
|
+
getShadowMetadata: (key: string, path: string[]) => {
|
|
317
|
+
const fullKey = [key, ...path].join(".");
|
|
318
|
+
return get().shadowStateStore.get(fullKey);
|
|
319
|
+
},
|
|
350
320
|
|
|
351
|
-
|
|
352
|
-
|
|
321
|
+
setShadowMetadata: (key: string, path: string[], metadata: any) => {
|
|
322
|
+
const fullKey = [key, ...path].join(".");
|
|
323
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
324
|
+
const existing = newShadowStore.get(fullKey) || { id: ulid() };
|
|
325
|
+
newShadowStore.set(fullKey, { ...existing, ...metadata });
|
|
326
|
+
set({ shadowStateStore: newShadowStore });
|
|
327
|
+
|
|
328
|
+
if (metadata.virtualizer?.itemHeight) {
|
|
329
|
+
const subscribers = get().shadowStateSubscribers.get(key);
|
|
330
|
+
subscribers?.forEach((cb) => cb());
|
|
331
|
+
}
|
|
353
332
|
},
|
|
354
|
-
|
|
333
|
+
|
|
334
|
+
insertShadowArrayElement: (
|
|
355
335
|
key: string,
|
|
356
336
|
arrayPath: string[],
|
|
357
|
-
|
|
337
|
+
newItem: any
|
|
358
338
|
) => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
339
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
340
|
+
const arrayKey = [key, ...arrayPath].join(".");
|
|
341
|
+
const parentMeta = newShadowStore.get(arrayKey);
|
|
342
|
+
const newArrayState = get().getNestedState(key, arrayPath) as any[];
|
|
362
343
|
|
|
363
|
-
|
|
364
|
-
current = current?.[segment];
|
|
365
|
-
}
|
|
344
|
+
if (!parentMeta || !parentMeta.arrayKeys) return;
|
|
366
345
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
346
|
+
const newItemId = `id:${newItem.id}`;
|
|
347
|
+
const newIndex = newArrayState.findIndex((item) => item.id === newItem.id);
|
|
370
348
|
|
|
371
|
-
|
|
372
|
-
});
|
|
373
|
-
},
|
|
374
|
-
shadowStateSubscribers: new Map<string, Set<() => void>>(), // key -> Set of callbacks
|
|
349
|
+
if (newIndex === -1) return;
|
|
375
350
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const subsForKey = newSubs.get(key) || new Set();
|
|
380
|
-
subsForKey.add(callback);
|
|
381
|
-
newSubs.set(key, subsForKey);
|
|
382
|
-
return { shadowStateSubscribers: newSubs };
|
|
383
|
-
});
|
|
384
|
-
// Return an unsubscribe function
|
|
385
|
-
return () => {
|
|
386
|
-
set((state) => {
|
|
387
|
-
const newSubs = new Map(state.shadowStateSubscribers);
|
|
388
|
-
const subsForKey = newSubs.get(key);
|
|
389
|
-
if (subsForKey) {
|
|
390
|
-
subsForKey.delete(callback);
|
|
391
|
-
}
|
|
392
|
-
return { shadowStateSubscribers: newSubs };
|
|
393
|
-
});
|
|
394
|
-
};
|
|
395
|
-
},
|
|
351
|
+
const newArrayKeys = [...parentMeta.arrayKeys];
|
|
352
|
+
newArrayKeys.splice(newIndex, 0, newItemId);
|
|
353
|
+
newShadowStore.set(arrayKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
396
354
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
355
|
+
const processNewItem = (value: any, path: string[]) => {
|
|
356
|
+
const nodeKey = [key, ...path].join(".");
|
|
357
|
+
if (typeof value === "object" && value !== null) {
|
|
358
|
+
newShadowStore.set(nodeKey, { id: ulid() });
|
|
359
|
+
Object.keys(value).forEach((k) => {
|
|
360
|
+
processNewItem(value[k], [...path, k]);
|
|
361
|
+
});
|
|
362
|
+
} else {
|
|
363
|
+
newShadowStore.set(nodeKey, { id: ulid() });
|
|
364
|
+
}
|
|
365
|
+
};
|
|
402
366
|
|
|
403
|
-
|
|
367
|
+
processNewItem(newItem, [...arrayPath, newItemId]);
|
|
404
368
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (!current[segment]) current[segment] = {};
|
|
408
|
-
current = current[segment];
|
|
409
|
-
}
|
|
369
|
+
set({ shadowStateStore: newShadowStore });
|
|
370
|
+
},
|
|
410
371
|
|
|
411
|
-
|
|
412
|
-
|
|
372
|
+
removeShadowArrayElement: (key: string, itemPath: string[]) => {
|
|
373
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
374
|
+
const itemKey = [key, ...itemPath].join(".");
|
|
375
|
+
const itemIdToRemove = itemPath[itemPath.length - 1];
|
|
413
376
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
current.virtualizer.itemHeight = newHeight;
|
|
418
|
-
}
|
|
377
|
+
const parentPath = itemPath.slice(0, -1);
|
|
378
|
+
const parentKey = [key, ...parentPath].join(".");
|
|
379
|
+
const parentMeta = newShadowStore.get(parentKey);
|
|
419
380
|
|
|
420
|
-
|
|
421
|
-
|
|
381
|
+
if (parentMeta && parentMeta.arrayKeys) {
|
|
382
|
+
const newArrayKeys = parentMeta.arrayKeys.filter(
|
|
383
|
+
(id) => id !== itemIdToRemove
|
|
384
|
+
);
|
|
385
|
+
newShadowStore.set(parentKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
386
|
+
}
|
|
422
387
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
subscribers.forEach((callback) => callback());
|
|
388
|
+
const prefixToDelete = itemKey + ".";
|
|
389
|
+
for (const k of Array.from(newShadowStore.keys())) {
|
|
390
|
+
if (k === itemKey || k.startsWith(prefixToDelete)) {
|
|
391
|
+
newShadowStore.delete(k);
|
|
428
392
|
}
|
|
429
393
|
}
|
|
394
|
+
|
|
395
|
+
set({ shadowStateStore: newShadowStore });
|
|
396
|
+
},
|
|
397
|
+
updateShadowAtPath: (key: string, path: string[], newValue: any) => {
|
|
398
|
+
const fullKey = [key, ...path].join(".");
|
|
399
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
400
|
+
const existing = newShadowStore.get(fullKey) || { id: ulid() };
|
|
401
|
+
newShadowStore.set(fullKey, { ...existing, lastUpdated: Date.now() });
|
|
402
|
+
set({ shadowStateStore: newShadowStore });
|
|
430
403
|
},
|
|
404
|
+
|
|
431
405
|
selectedIndicesMap: new Map<string, Map<string, number>>(),
|
|
432
406
|
|
|
433
407
|
// Add the new methods
|
|
@@ -734,41 +708,44 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
734
708
|
getNestedState: (key: string, path: string[]) => {
|
|
735
709
|
const rootState = get().cogsStateStore[key];
|
|
736
710
|
|
|
737
|
-
const
|
|
738
|
-
if (pathArray.length === 0
|
|
711
|
+
const resolvePath = (obj: any, pathArray: string[]): any => {
|
|
712
|
+
if (pathArray.length === 0 || obj === undefined) {
|
|
713
|
+
return obj;
|
|
714
|
+
}
|
|
739
715
|
|
|
740
|
-
const
|
|
716
|
+
const currentSegment = pathArray[0];
|
|
741
717
|
const remainingPath = pathArray.slice(1);
|
|
742
718
|
|
|
743
|
-
|
|
719
|
+
// FIX: Handle ID-based array access like 'id:xyz'
|
|
720
|
+
if (
|
|
721
|
+
Array.isArray(obj) &&
|
|
722
|
+
typeof currentSegment === "string" &&
|
|
723
|
+
currentSegment.startsWith("id:")
|
|
724
|
+
) {
|
|
725
|
+
const targetId = currentSegment.split(":")[1];
|
|
726
|
+
const foundItem = obj.find(
|
|
727
|
+
(item) => item && String(item.id) === targetId
|
|
728
|
+
);
|
|
729
|
+
return resolvePath(foundItem, remainingPath);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Handle wildcard array access: '[*]'
|
|
733
|
+
if (currentSegment === "[*]") {
|
|
744
734
|
if (!Array.isArray(obj)) {
|
|
745
735
|
console.warn("Asterisk notation used on non-array value");
|
|
746
736
|
return undefined;
|
|
747
737
|
}
|
|
748
|
-
|
|
749
738
|
if (remainingPath.length === 0) return obj;
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
const results = obj.map((item) =>
|
|
753
|
-
getValueWithAsterisk(item, remainingPath)
|
|
754
|
-
);
|
|
755
|
-
|
|
756
|
-
// If the next path segment exists and returns arrays, flatten them
|
|
757
|
-
if (Array.isArray(results[0])) {
|
|
758
|
-
return results.flat();
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
return results;
|
|
739
|
+
const results = obj.map((item) => resolvePath(item, remainingPath));
|
|
740
|
+
return Array.isArray(results[0]) ? results.flat() : results;
|
|
762
741
|
}
|
|
763
742
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
return getValueWithAsterisk(value, remainingPath);
|
|
743
|
+
// Handle standard object property access and numeric array indices
|
|
744
|
+
const nextObj = obj[currentSegment as keyof typeof obj];
|
|
745
|
+
return resolvePath(nextObj, remainingPath);
|
|
768
746
|
};
|
|
769
747
|
|
|
770
|
-
|
|
771
|
-
return getValueWithAsterisk(rootState, path);
|
|
748
|
+
return resolvePath(rootState, path);
|
|
772
749
|
},
|
|
773
750
|
setInitialStateOptions: (key, value) => {
|
|
774
751
|
set((prev) => ({
|