cogsbox-state 0.5.427 → 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 +780 -845
- 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 +15 -18
- package/dist/store.js +165 -186
- package/dist/store.js.map +1 -1
- package/dist/useValidateZodPath.d.ts +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 +6 -2
- package/src/CogsState.tsx +387 -716
- package/src/Functions.tsx +47 -39
- package/src/store.ts +171 -205
- 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,51 +87,42 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
|
|
|
86
87
|
},
|
|
87
88
|
}));
|
|
88
89
|
|
|
89
|
-
export type
|
|
90
|
-
|
|
90
|
+
export type ShadowMetadata = {
|
|
91
|
+
id: string;
|
|
92
|
+
arrayKeys?: string[];
|
|
91
93
|
virtualizer?: {
|
|
92
94
|
itemHeight?: number;
|
|
93
|
-
domRef?:
|
|
95
|
+
domRef?: HTMLElement | null;
|
|
94
96
|
};
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
syncInfo?: { status: string };
|
|
98
|
+
lastUpdated?: number;
|
|
97
99
|
};
|
|
98
|
-
|
|
99
|
-
// THE NEW, CORRECT, RECURSIVE TYPE FOR THE SHADOW STATE
|
|
100
|
-
// A ShadowNode is either:
|
|
101
|
-
// 1. An array of ItemMeta (if it represents a user's array).
|
|
102
|
-
// 2. An object that can be indexed by any string, whose values are other ShadowNodes.
|
|
103
|
-
export type ShadowNode = ItemMeta[] | { [key: string]: ShadowNode };
|
|
104
|
-
|
|
105
|
-
// This is the top-level type for the store, mapping state keys to our ShadowNode structure.
|
|
106
|
-
export type ShadowStateStore = {
|
|
107
|
-
[key: string]: ShadowNode;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
100
|
export type CogsGlobalState = {
|
|
111
101
|
// --- Shadow State and Subscription System ---
|
|
112
|
-
shadowStateStore:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
initializeShadowState:
|
|
116
|
-
updateShadowAtPath:
|
|
102
|
+
shadowStateStore: Map<string, ShadowMetadata>;
|
|
103
|
+
|
|
104
|
+
// These method signatures stay the same
|
|
105
|
+
initializeShadowState: (key: string, initialState: any) => void;
|
|
106
|
+
updateShadowAtPath: (key: string, path: string[], newValue: any) => void;
|
|
117
107
|
insertShadowArrayElement: (
|
|
118
108
|
key: string,
|
|
119
109
|
arrayPath: string[],
|
|
120
|
-
|
|
110
|
+
newItem: any
|
|
121
111
|
) => void;
|
|
122
|
-
removeShadowArrayElement: (
|
|
112
|
+
removeShadowArrayElement: (key: string, arrayPath: string[]) => void;
|
|
113
|
+
getShadowMetadata: (
|
|
123
114
|
key: string,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
) => void;
|
|
127
|
-
getShadowMetadata: (key: string, path: string[]) => ShadowNode | null;
|
|
115
|
+
path: string[]
|
|
116
|
+
) => ShadowMetadata | undefined;
|
|
128
117
|
setShadowMetadata: (
|
|
129
118
|
key: string,
|
|
130
119
|
path: string[],
|
|
131
|
-
metadata:
|
|
120
|
+
metadata: Omit<ShadowMetadata, "id">
|
|
132
121
|
) => void;
|
|
133
|
-
|
|
122
|
+
|
|
123
|
+
shadowStateSubscribers: Map<string, Set<() => void>>; // Stores subscribers for shadow state updates
|
|
124
|
+
subscribeToShadowState: (key: string, callback: () => void) => () => void;
|
|
125
|
+
|
|
134
126
|
selectedIndicesMap: Map<string, Map<string, number>>; // stateKey -> (parentPath -> selectedIndex)
|
|
135
127
|
getSelectedIndex: (
|
|
136
128
|
stateKey: string,
|
|
@@ -253,192 +245,163 @@ export type CogsGlobalState = {
|
|
|
253
245
|
};
|
|
254
246
|
|
|
255
247
|
export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
256
|
-
shadowStateStore:
|
|
257
|
-
|
|
258
|
-
const shadow = get().shadowStateStore[key];
|
|
259
|
-
if (!shadow) return null;
|
|
260
|
-
|
|
261
|
-
let current: any = shadow;
|
|
262
|
-
for (const segment of path) {
|
|
263
|
-
current = current?.[segment];
|
|
264
|
-
if (!current) return null;
|
|
265
|
-
}
|
|
248
|
+
shadowStateStore: new Map(),
|
|
249
|
+
shadowStateSubscribers: new Map(),
|
|
266
250
|
|
|
267
|
-
|
|
268
|
-
|
|
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
|
+
});
|
|
269
259
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
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
|
+
}
|
|
281
270
|
}
|
|
282
|
-
return
|
|
283
|
-
}
|
|
284
|
-
return {}; // Leaf node - empty object for metadata
|
|
271
|
+
return { shadowStateSubscribers: newSubs };
|
|
272
|
+
});
|
|
285
273
|
};
|
|
286
|
-
|
|
287
|
-
set((state) => ({
|
|
288
|
-
shadowStateStore: {
|
|
289
|
-
...state.shadowStateStore,
|
|
290
|
-
[key]: createShadowStructure(initialState),
|
|
291
|
-
},
|
|
292
|
-
}));
|
|
293
274
|
},
|
|
275
|
+
initializeShadowState: (key: string, initialState: any) => {
|
|
276
|
+
const newShadowStore = new Map<string, ShadowMetadata>();
|
|
294
277
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const newShadow = { ...state.shadowStateStore };
|
|
298
|
-
if (!newShadow[key]) return state;
|
|
299
|
-
|
|
300
|
-
let current: any = newShadow[key];
|
|
301
|
-
const pathCopy = [...path];
|
|
302
|
-
const lastSegment = pathCopy.pop();
|
|
303
|
-
|
|
304
|
-
// Navigate to parent
|
|
305
|
-
for (const segment of pathCopy) {
|
|
306
|
-
if (!current[segment]) current[segment] = {};
|
|
307
|
-
current = current[segment];
|
|
308
|
-
}
|
|
278
|
+
const processValue = (value: any, path: string[]) => {
|
|
279
|
+
const nodeKey = [key, ...path].join(".");
|
|
309
280
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (Array.isArray(newValue)) {
|
|
313
|
-
current[lastSegment] = new Array(newValue.length);
|
|
314
|
-
} else if (typeof newValue === "object" && newValue !== null) {
|
|
315
|
-
current[lastSegment] = {};
|
|
316
|
-
} else {
|
|
317
|
-
current[lastSegment] = current[lastSegment] || {};
|
|
318
|
-
}
|
|
319
|
-
}
|
|
281
|
+
if (Array.isArray(value)) {
|
|
282
|
+
const childIds: string[] = [];
|
|
320
283
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
284
|
+
value.forEach((item) => {
|
|
285
|
+
if (typeof item === "object" && item !== null && !item.id) {
|
|
286
|
+
item.id = ulid();
|
|
287
|
+
}
|
|
324
288
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
arrayPath: string[],
|
|
328
|
-
newItem: any
|
|
329
|
-
) => {
|
|
330
|
-
set((state) => {
|
|
331
|
-
const newShadow = { ...state.shadowStateStore };
|
|
332
|
-
if (!newShadow[key]) return state;
|
|
289
|
+
const itemId = `id:${item.id}`;
|
|
290
|
+
childIds.push(itemId);
|
|
333
291
|
|
|
334
|
-
|
|
292
|
+
const itemPath = [...path, itemId];
|
|
293
|
+
processValue(item, itemPath);
|
|
294
|
+
});
|
|
335
295
|
|
|
336
|
-
|
|
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() });
|
|
337
303
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
304
|
+
Object.keys(value).forEach((k) => {
|
|
305
|
+
processValue(value[k], [...path, k]);
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
newShadowStore.set(nodeKey, { id: ulid() });
|
|
341
309
|
}
|
|
310
|
+
};
|
|
342
311
|
|
|
343
|
-
|
|
344
|
-
// Create shadow structure based on the actual new item
|
|
345
|
-
const createShadowStructure = (obj: any): any => {
|
|
346
|
-
if (Array.isArray(obj)) {
|
|
347
|
-
return obj.map((item) => createShadowStructure(item));
|
|
348
|
-
}
|
|
349
|
-
if (typeof obj === "object" && obj !== null) {
|
|
350
|
-
const shadow: any = {};
|
|
351
|
-
for (const k in obj) {
|
|
352
|
-
shadow[k] = createShadowStructure(obj[k]);
|
|
353
|
-
}
|
|
354
|
-
return shadow;
|
|
355
|
-
}
|
|
356
|
-
return {}; // Leaf nodes get empty object for metadata
|
|
357
|
-
};
|
|
312
|
+
processValue(initialState, []);
|
|
358
313
|
|
|
359
|
-
|
|
360
|
-
|
|
314
|
+
set({ shadowStateStore: newShadowStore });
|
|
315
|
+
},
|
|
316
|
+
getShadowMetadata: (key: string, path: string[]) => {
|
|
317
|
+
const fullKey = [key, ...path].join(".");
|
|
318
|
+
return get().shadowStateStore.get(fullKey);
|
|
319
|
+
},
|
|
361
320
|
|
|
362
|
-
|
|
363
|
-
|
|
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
|
+
}
|
|
364
332
|
},
|
|
365
|
-
|
|
333
|
+
|
|
334
|
+
insertShadowArrayElement: (
|
|
366
335
|
key: string,
|
|
367
336
|
arrayPath: string[],
|
|
368
|
-
|
|
337
|
+
newItem: any
|
|
369
338
|
) => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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[];
|
|
373
343
|
|
|
374
|
-
|
|
375
|
-
current = current?.[segment];
|
|
376
|
-
}
|
|
344
|
+
if (!parentMeta || !parentMeta.arrayKeys) return;
|
|
377
345
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
346
|
+
const newItemId = `id:${newItem.id}`;
|
|
347
|
+
const newIndex = newArrayState.findIndex((item) => item.id === newItem.id);
|
|
381
348
|
|
|
382
|
-
|
|
383
|
-
});
|
|
384
|
-
},
|
|
385
|
-
shadowStateSubscribers: new Map<string, Set<() => void>>(), // key -> Set of callbacks
|
|
349
|
+
if (newIndex === -1) return;
|
|
386
350
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const subsForKey = newSubs.get(key) || new Set();
|
|
391
|
-
subsForKey.add(callback);
|
|
392
|
-
newSubs.set(key, subsForKey);
|
|
393
|
-
return { shadowStateSubscribers: newSubs };
|
|
394
|
-
});
|
|
395
|
-
// Return an unsubscribe function
|
|
396
|
-
return () => {
|
|
397
|
-
set((state) => {
|
|
398
|
-
const newSubs = new Map(state.shadowStateSubscribers);
|
|
399
|
-
const subsForKey = newSubs.get(key);
|
|
400
|
-
if (subsForKey) {
|
|
401
|
-
subsForKey.delete(callback);
|
|
402
|
-
}
|
|
403
|
-
return { shadowStateSubscribers: newSubs };
|
|
404
|
-
});
|
|
405
|
-
};
|
|
406
|
-
},
|
|
351
|
+
const newArrayKeys = [...parentMeta.arrayKeys];
|
|
352
|
+
newArrayKeys.splice(newIndex, 0, newItemId);
|
|
353
|
+
newShadowStore.set(arrayKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
407
354
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
+
};
|
|
413
366
|
|
|
414
|
-
|
|
367
|
+
processNewItem(newItem, [...arrayPath, newItemId]);
|
|
415
368
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (!current[segment]) current[segment] = {};
|
|
419
|
-
current = current[segment];
|
|
420
|
-
}
|
|
369
|
+
set({ shadowStateStore: newShadowStore });
|
|
370
|
+
},
|
|
421
371
|
|
|
422
|
-
|
|
423
|
-
|
|
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];
|
|
424
376
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
current.virtualizer.itemHeight = newHeight;
|
|
429
|
-
}
|
|
377
|
+
const parentPath = itemPath.slice(0, -1);
|
|
378
|
+
const parentKey = [key, ...parentPath].join(".");
|
|
379
|
+
const parentMeta = newShadowStore.get(parentKey);
|
|
430
380
|
|
|
431
|
-
|
|
432
|
-
|
|
381
|
+
if (parentMeta && parentMeta.arrayKeys) {
|
|
382
|
+
const newArrayKeys = parentMeta.arrayKeys.filter(
|
|
383
|
+
(id) => id !== itemIdToRemove
|
|
384
|
+
);
|
|
385
|
+
newShadowStore.set(parentKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
386
|
+
}
|
|
433
387
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
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);
|
|
439
392
|
}
|
|
440
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 });
|
|
441
403
|
},
|
|
404
|
+
|
|
442
405
|
selectedIndicesMap: new Map<string, Map<string, number>>(),
|
|
443
406
|
|
|
444
407
|
// Add the new methods
|
|
@@ -745,41 +708,44 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
745
708
|
getNestedState: (key: string, path: string[]) => {
|
|
746
709
|
const rootState = get().cogsStateStore[key];
|
|
747
710
|
|
|
748
|
-
const
|
|
749
|
-
if (pathArray.length === 0
|
|
711
|
+
const resolvePath = (obj: any, pathArray: string[]): any => {
|
|
712
|
+
if (pathArray.length === 0 || obj === undefined) {
|
|
713
|
+
return obj;
|
|
714
|
+
}
|
|
750
715
|
|
|
751
|
-
const
|
|
716
|
+
const currentSegment = pathArray[0];
|
|
752
717
|
const remainingPath = pathArray.slice(1);
|
|
753
718
|
|
|
754
|
-
|
|
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 === "[*]") {
|
|
755
734
|
if (!Array.isArray(obj)) {
|
|
756
735
|
console.warn("Asterisk notation used on non-array value");
|
|
757
736
|
return undefined;
|
|
758
737
|
}
|
|
759
|
-
|
|
760
738
|
if (remainingPath.length === 0) return obj;
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
const results = obj.map((item) =>
|
|
764
|
-
getValueWithAsterisk(item, remainingPath)
|
|
765
|
-
);
|
|
766
|
-
|
|
767
|
-
// If the next path segment exists and returns arrays, flatten them
|
|
768
|
-
if (Array.isArray(results[0])) {
|
|
769
|
-
return results.flat();
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
return results;
|
|
739
|
+
const results = obj.map((item) => resolvePath(item, remainingPath));
|
|
740
|
+
return Array.isArray(results[0]) ? results.flat() : results;
|
|
773
741
|
}
|
|
774
742
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
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);
|
|
779
746
|
};
|
|
780
747
|
|
|
781
|
-
|
|
782
|
-
return getValueWithAsterisk(rootState, path);
|
|
748
|
+
return resolvePath(rootState, path);
|
|
783
749
|
},
|
|
784
750
|
setInitialStateOptions: (key, value) => {
|
|
785
751
|
set((prev) => ({
|