cogsbox-state 0.5.432 → 0.5.435
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/README.md +596 -238
- package/dist/CogsState.d.ts +103 -122
- package/dist/CogsState.jsx +1647 -1069
- package/dist/CogsState.jsx.map +1 -1
- package/dist/Functions.d.ts +1 -15
- package/dist/Functions.jsx +40 -188
- package/dist/Functions.jsx.map +1 -1
- package/dist/index.js +18 -19
- package/dist/store.d.ts +94 -92
- package/dist/store.js +232 -295
- package/dist/store.js.map +1 -1
- package/dist/useValidateZodPath.d.ts +1 -1
- package/dist/utility.d.ts +2 -2
- package/dist/utility.js +152 -169
- package/dist/utility.js.map +1 -1
- package/package.json +2 -1
- package/src/CogsState.tsx +3220 -1694
- package/src/Functions.tsx +167 -303
- package/src/store.ts +449 -440
- package/src/utility.ts +76 -95
- package/dist/useValidateZodPath.js +0 -59
- package/dist/useValidateZodPath.js.map +0 -1
package/src/store.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { create } from
|
|
2
|
-
import { ulid } from
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { ulid } from 'ulid';
|
|
3
3
|
import type {
|
|
4
4
|
OptionsType,
|
|
5
5
|
ReactivityType,
|
|
6
6
|
StateKeys,
|
|
7
|
-
SyncActionsType,
|
|
8
7
|
SyncInfo,
|
|
9
8
|
UpdateTypeDetail,
|
|
10
|
-
} from
|
|
9
|
+
} from './CogsState.js';
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
11
12
|
|
|
12
13
|
type StateUpdater<StateValue> =
|
|
13
14
|
| StateValue
|
|
@@ -29,18 +30,7 @@ export type TrieNode = {
|
|
|
29
30
|
subscribers: Set<string>;
|
|
30
31
|
children: Map<string, TrieNode>;
|
|
31
32
|
};
|
|
32
|
-
|
|
33
|
-
components: Map<
|
|
34
|
-
string,
|
|
35
|
-
{
|
|
36
|
-
forceUpdate: () => void;
|
|
37
|
-
paths: Set<string>;
|
|
38
|
-
deps?: any[];
|
|
39
|
-
depsFunction?: (state: any) => any[] | true;
|
|
40
|
-
reactiveType: ReactivityType[] | ReactivityType;
|
|
41
|
-
}
|
|
42
|
-
>;
|
|
43
|
-
};
|
|
33
|
+
|
|
44
34
|
export type FormRefStoreState = {
|
|
45
35
|
formRefs: Map<string, React.RefObject<any>>;
|
|
46
36
|
registerFormRef: (id: string, ref: React.RefObject<any>) => void;
|
|
@@ -74,7 +64,7 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
|
|
|
74
64
|
// Get all refs that start with the stateKey prefix
|
|
75
65
|
getFormRefsByStateKey: (stateKey) => {
|
|
76
66
|
const allRefs = get().formRefs;
|
|
77
|
-
const stateKeyPrefix = stateKey +
|
|
67
|
+
const stateKeyPrefix = stateKey + '.';
|
|
78
68
|
const filteredRefs = new Map();
|
|
79
69
|
|
|
80
70
|
allRefs.forEach((ref, id) => {
|
|
@@ -86,21 +76,95 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
|
|
|
86
76
|
return filteredRefs;
|
|
87
77
|
},
|
|
88
78
|
}));
|
|
89
|
-
|
|
79
|
+
export type ComponentsType = {
|
|
80
|
+
components?: Map<
|
|
81
|
+
string,
|
|
82
|
+
{
|
|
83
|
+
forceUpdate: () => void;
|
|
84
|
+
paths: Set<string>;
|
|
85
|
+
deps?: any[];
|
|
86
|
+
prevDeps?: any[];
|
|
87
|
+
depsFunction?: (state: any) => any[] | true;
|
|
88
|
+
reactiveType: ReactivityType[] | ReactivityType;
|
|
89
|
+
}
|
|
90
|
+
>;
|
|
91
|
+
};
|
|
90
92
|
export type ShadowMetadata = {
|
|
91
|
-
id
|
|
93
|
+
id?: string;
|
|
94
|
+
|
|
95
|
+
stateSource?: 'default' | 'server' | 'localStorage';
|
|
96
|
+
lastServerSync?: number;
|
|
97
|
+
isDirty?: boolean;
|
|
98
|
+
baseServerState?: any;
|
|
99
|
+
|
|
92
100
|
arrayKeys?: string[];
|
|
101
|
+
|
|
102
|
+
fields?: Record<string, any>;
|
|
93
103
|
virtualizer?: {
|
|
94
104
|
itemHeight?: number;
|
|
95
105
|
domRef?: HTMLElement | null;
|
|
96
106
|
};
|
|
97
107
|
syncInfo?: { status: string };
|
|
98
108
|
lastUpdated?: number;
|
|
99
|
-
|
|
109
|
+
value?: any;
|
|
110
|
+
classSignals?: Array<{
|
|
111
|
+
// <-- ADD THIS BLOCK
|
|
112
|
+
id: string;
|
|
113
|
+
effect: string;
|
|
114
|
+
lastClasses: string;
|
|
115
|
+
deps: any[];
|
|
116
|
+
}>;
|
|
117
|
+
signals?: Array<{
|
|
118
|
+
instanceId: string;
|
|
119
|
+
parentId: string;
|
|
120
|
+
position: number;
|
|
121
|
+
effect?: string;
|
|
122
|
+
}>;
|
|
123
|
+
mapWrappers?: Array<{
|
|
124
|
+
instanceId: string;
|
|
125
|
+
path: string[];
|
|
126
|
+
componentId: string;
|
|
127
|
+
meta?: any;
|
|
128
|
+
mapFn: (
|
|
129
|
+
setter: any,
|
|
130
|
+
index: number,
|
|
131
|
+
|
|
132
|
+
arraySetter: any
|
|
133
|
+
) => ReactNode;
|
|
134
|
+
containerRef: HTMLDivElement | null;
|
|
135
|
+
rebuildStateShape: any;
|
|
136
|
+
}>;
|
|
137
|
+
transformCaches?: Map<
|
|
138
|
+
string,
|
|
139
|
+
{
|
|
140
|
+
validIds: string[];
|
|
141
|
+
computedAt: number;
|
|
142
|
+
transforms: Array<{ type: 'filter' | 'sort'; fn: Function }>;
|
|
143
|
+
}
|
|
144
|
+
>;
|
|
145
|
+
pathComponents?: Set<string>;
|
|
146
|
+
streams?: Map<
|
|
147
|
+
string,
|
|
148
|
+
{
|
|
149
|
+
buffer: any[];
|
|
150
|
+
flushTimer: NodeJS.Timeout | null;
|
|
151
|
+
}
|
|
152
|
+
>;
|
|
153
|
+
} & ComponentsType;
|
|
154
|
+
export type CogsEvent =
|
|
155
|
+
| { type: 'INSERT'; path: string; itemKey: string; index: number }
|
|
156
|
+
| { type: 'REMOVE'; path: string; itemKey: string }
|
|
157
|
+
| { type: 'UPDATE'; path: string; newValue: any }
|
|
158
|
+
| { type: 'ITEMHEIGHT'; itemKey: string; height: number } // For full re-initializations (e.g., when a component is removed)
|
|
159
|
+
| { type: 'RELOAD'; path: string }; // For full re-initializations
|
|
100
160
|
export type CogsGlobalState = {
|
|
101
161
|
// --- Shadow State and Subscription System ---
|
|
102
162
|
shadowStateStore: Map<string, ShadowMetadata>;
|
|
103
|
-
|
|
163
|
+
markAsDirty: (
|
|
164
|
+
key: string,
|
|
165
|
+
path: string[],
|
|
166
|
+
options: { bubble: boolean }
|
|
167
|
+
) => void;
|
|
104
168
|
// These method signatures stay the same
|
|
105
169
|
initializeShadowState: (key: string, initialState: any) => void;
|
|
106
170
|
updateShadowAtPath: (key: string, path: string[], newValue: any) => void;
|
|
@@ -110,6 +174,12 @@ export type CogsGlobalState = {
|
|
|
110
174
|
newItem: any
|
|
111
175
|
) => void;
|
|
112
176
|
removeShadowArrayElement: (key: string, arrayPath: string[]) => void;
|
|
177
|
+
getShadowValue: (
|
|
178
|
+
key: string,
|
|
179
|
+
|
|
180
|
+
validArrayIds?: string[]
|
|
181
|
+
) => any;
|
|
182
|
+
|
|
113
183
|
getShadowMetadata: (
|
|
114
184
|
key: string,
|
|
115
185
|
path: string[]
|
|
@@ -117,60 +187,36 @@ export type CogsGlobalState = {
|
|
|
117
187
|
setShadowMetadata: (
|
|
118
188
|
key: string,
|
|
119
189
|
path: string[],
|
|
120
|
-
metadata: Omit<ShadowMetadata,
|
|
190
|
+
metadata: Omit<ShadowMetadata, 'id'>
|
|
121
191
|
) => void;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
getSelectedIndex: (
|
|
128
|
-
stateKey: string,
|
|
129
|
-
parentPath: string
|
|
130
|
-
) => number | undefined;
|
|
131
|
-
setSelectedIndex: (
|
|
132
|
-
stateKey: string,
|
|
133
|
-
parentPath: string,
|
|
134
|
-
index: number | undefined
|
|
192
|
+
setTransformCache: (
|
|
193
|
+
key: string,
|
|
194
|
+
path: string[],
|
|
195
|
+
cacheKey: string,
|
|
196
|
+
cacheData: any
|
|
135
197
|
) => void;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
198
|
+
|
|
199
|
+
pathSubscribers: Map<string, Set<(newValue: any) => void>>;
|
|
200
|
+
subscribeToPath: (
|
|
201
|
+
path: string,
|
|
202
|
+
callback: (newValue: any) => void
|
|
203
|
+
) => () => void;
|
|
204
|
+
notifyPathSubscribers: (updatedPath: string, newValue: any) => void;
|
|
205
|
+
|
|
206
|
+
selectedIndicesMap: Map<string, string>; // stateKey -> (parentPath -> selectedIndex)
|
|
207
|
+
getSelectedIndex: (stateKey: string, validArrayIds?: string[]) => number;
|
|
208
|
+
setSelectedIndex: (key: string, itemKey: string) => void;
|
|
209
|
+
clearSelectedIndex: ({ arrayKey }: { arrayKey: string }) => void;
|
|
143
210
|
clearSelectedIndexesForState: (stateKey: string) => void;
|
|
144
211
|
|
|
145
212
|
// --- Core State and Updaters ---
|
|
146
|
-
|
|
213
|
+
|
|
147
214
|
initialStateOptions: { [key: string]: OptionsType };
|
|
148
|
-
|
|
149
|
-
isLoadingGlobal: { [key: string]: boolean };
|
|
215
|
+
|
|
150
216
|
initialStateGlobal: { [key: string]: StateValue };
|
|
151
|
-
|
|
152
|
-
serverState: { [key: string]: StateValue };
|
|
153
|
-
|
|
154
|
-
getUpdaterState: (key: string) => StateUpdater<StateValue>;
|
|
155
|
-
setUpdaterState: (key: string, newUpdater: any) => void;
|
|
156
|
-
getKeyState: <StateKey extends StateKeys>(key: StateKey) => StateValue;
|
|
157
|
-
getNestedState: <StateKey extends StateKeys>(
|
|
158
|
-
key: StateKey,
|
|
159
|
-
path: string[]
|
|
160
|
-
) => StateValue;
|
|
161
|
-
setState: <StateKey extends StateKeys>(
|
|
162
|
-
key: StateKey,
|
|
163
|
-
value: StateUpdater<StateValue>
|
|
164
|
-
) => void;
|
|
165
|
-
setInitialStates: (initialState: StateValue) => void;
|
|
166
|
-
setCreatedState: (initialState: StateValue) => void;
|
|
217
|
+
|
|
167
218
|
updateInitialStateGlobal: (key: string, newState: StateValue) => void;
|
|
168
|
-
|
|
169
|
-
setIsLoadingGlobal: (key: string, value: boolean) => void;
|
|
170
|
-
setServerState: <StateKey extends StateKeys>(
|
|
171
|
-
key: StateKey,
|
|
172
|
-
value: StateValue
|
|
173
|
-
) => void;
|
|
219
|
+
|
|
174
220
|
getInitialOptions: (key: string) => OptionsType | undefined;
|
|
175
221
|
setInitialStateOptions: (key: string, value: OptionsType) => void;
|
|
176
222
|
|
|
@@ -181,275 +227,441 @@ export type CogsGlobalState = {
|
|
|
181
227
|
removeValidationError: (path: string) => void;
|
|
182
228
|
|
|
183
229
|
// --- Server Sync and Logging ---
|
|
184
|
-
|
|
185
|
-
|
|
230
|
+
|
|
231
|
+
serverStateUpdates: Map<
|
|
232
|
+
string,
|
|
233
|
+
{
|
|
234
|
+
data: any;
|
|
235
|
+
status: 'loading' | 'success' | 'error';
|
|
236
|
+
timestamp: number;
|
|
237
|
+
}
|
|
238
|
+
>;
|
|
239
|
+
|
|
240
|
+
setServerStateUpdate: (key: string, serverState: any) => void;
|
|
241
|
+
|
|
186
242
|
stateLog: { [key: string]: UpdateTypeDetail[] };
|
|
187
243
|
syncInfoStore: Map<string, SyncInfo>;
|
|
188
|
-
|
|
189
|
-
setServerSyncLog: (key: string, newValue: SyncLogType) => void;
|
|
190
|
-
setServerSideOrNot: (key: string, value: boolean) => void;
|
|
191
|
-
getServerSideOrNot: (key: string) => boolean | undefined;
|
|
192
|
-
getThisLocalUpdate: (key: string) => UpdateTypeDetail[] | undefined;
|
|
193
|
-
setServerSyncActions: (key: string, value: SyncActionsType<any>) => void;
|
|
244
|
+
|
|
194
245
|
setStateLog: (
|
|
195
246
|
key: string,
|
|
196
247
|
updater: (prevUpdates: UpdateTypeDetail[]) => UpdateTypeDetail[]
|
|
197
248
|
) => void;
|
|
198
249
|
setSyncInfo: (key: string, syncInfo: SyncInfo) => void;
|
|
199
250
|
getSyncInfo: (key: string) => SyncInfo | null;
|
|
251
|
+
};
|
|
252
|
+
const isSimpleObject = (value: any): boolean => {
|
|
253
|
+
if (value === null || typeof value !== 'object') return false;
|
|
254
|
+
|
|
255
|
+
// Handle special cases that should be treated as primitives
|
|
256
|
+
if (
|
|
257
|
+
value instanceof Uint8Array ||
|
|
258
|
+
value instanceof Int8Array ||
|
|
259
|
+
value instanceof Uint16Array ||
|
|
260
|
+
value instanceof Int16Array ||
|
|
261
|
+
value instanceof Uint32Array ||
|
|
262
|
+
value instanceof Int32Array ||
|
|
263
|
+
value instanceof Float32Array ||
|
|
264
|
+
value instanceof Float64Array ||
|
|
265
|
+
value instanceof ArrayBuffer ||
|
|
266
|
+
value instanceof Date ||
|
|
267
|
+
value instanceof RegExp ||
|
|
268
|
+
value instanceof Map ||
|
|
269
|
+
value instanceof Set
|
|
270
|
+
) {
|
|
271
|
+
return false; // Treat as primitive
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Arrays and plain objects are complex
|
|
275
|
+
return Array.isArray(value) || value.constructor === Object;
|
|
276
|
+
};
|
|
277
|
+
export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
278
|
+
markAsDirty: (key: string, path: string[], options = { bubble: true }) => {
|
|
279
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
280
|
+
let changed = false;
|
|
281
|
+
|
|
282
|
+
const setDirty = (currentPath: string[]) => {
|
|
283
|
+
const fullKey = [key, ...currentPath].join('.');
|
|
284
|
+
const meta = newShadowStore.get(fullKey);
|
|
285
|
+
|
|
286
|
+
// We mark something as dirty if it isn't already.
|
|
287
|
+
// The original data source doesn't matter.
|
|
288
|
+
if (meta && meta.isDirty !== true) {
|
|
289
|
+
newShadowStore.set(fullKey, { ...meta, isDirty: true });
|
|
290
|
+
changed = true;
|
|
291
|
+
} else if (!meta) {
|
|
292
|
+
// If there's no metadata, create it and mark it as dirty.
|
|
293
|
+
// This handles newly created fields within an object.
|
|
294
|
+
newShadowStore.set(fullKey, { isDirty: true });
|
|
295
|
+
changed = true;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
200
298
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
string,
|
|
204
|
-
Set<{
|
|
205
|
-
instanceId: string;
|
|
206
|
-
parentId: string;
|
|
207
|
-
position: number;
|
|
208
|
-
effect?: string;
|
|
209
|
-
map?: string;
|
|
210
|
-
}>
|
|
211
|
-
>;
|
|
212
|
-
addSignalElement: (
|
|
213
|
-
signalId: string,
|
|
214
|
-
elementInfo: {
|
|
215
|
-
instanceId: string;
|
|
216
|
-
parentId: string;
|
|
217
|
-
position: number;
|
|
218
|
-
effect?: string;
|
|
219
|
-
map?: string;
|
|
220
|
-
}
|
|
221
|
-
) => void;
|
|
222
|
-
removeSignalElement: (signalId: string, instanceId: string) => void;
|
|
223
|
-
stateComponents: Map<string, ComponentsType>;
|
|
299
|
+
// 1. Mark the target path itself as dirty.
|
|
300
|
+
setDirty(path);
|
|
224
301
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
depsFunction: ((state: any) => any[] | true) | null;
|
|
302
|
+
// 2. If `bubble` is true, walk up the path and mark all parents as dirty.
|
|
303
|
+
if (options.bubble) {
|
|
304
|
+
let parentPath = [...path];
|
|
305
|
+
while (parentPath.length > 0) {
|
|
306
|
+
parentPath.pop();
|
|
307
|
+
setDirty(parentPath);
|
|
308
|
+
}
|
|
233
309
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
record: {
|
|
238
|
-
deps: any[];
|
|
239
|
-
updaters: Set<() => void>;
|
|
240
|
-
depsFunction: ((state: any) => any[] | true) | null;
|
|
310
|
+
|
|
311
|
+
if (changed) {
|
|
312
|
+
set({ shadowStateStore: newShadowStore });
|
|
241
313
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
314
|
+
},
|
|
315
|
+
serverStateUpdates: new Map(),
|
|
316
|
+
setServerStateUpdate: (key, serverState) => {
|
|
317
|
+
set((state) => {
|
|
318
|
+
const newMap = new Map(state.serverStateUpdates);
|
|
319
|
+
newMap.set(key, serverState);
|
|
320
|
+
return { serverStateUpdates: newMap };
|
|
321
|
+
});
|
|
246
322
|
|
|
247
|
-
|
|
323
|
+
// Notify all subscribers for this key
|
|
324
|
+
get().notifyPathSubscribers(key, {
|
|
325
|
+
type: 'SERVER_STATE_UPDATE',
|
|
326
|
+
serverState,
|
|
327
|
+
});
|
|
328
|
+
},
|
|
248
329
|
shadowStateStore: new Map(),
|
|
249
|
-
|
|
330
|
+
pathSubscribers: new Map<string, Set<(newValue: any) => void>>(),
|
|
250
331
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
newSubs.set(key, subsForKey);
|
|
257
|
-
return { shadowStateSubscribers: newSubs };
|
|
258
|
-
});
|
|
332
|
+
subscribeToPath: (path, callback) => {
|
|
333
|
+
const subscribers = get().pathSubscribers;
|
|
334
|
+
const subsForPath = subscribers.get(path) || new Set();
|
|
335
|
+
subsForPath.add(callback);
|
|
336
|
+
subscribers.set(path, subsForPath);
|
|
259
337
|
|
|
260
|
-
// Return unsubscribe function
|
|
261
338
|
return () => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
if (subsForKey.size === 0) {
|
|
268
|
-
newSubs.delete(key);
|
|
269
|
-
}
|
|
339
|
+
const currentSubs = get().pathSubscribers.get(path);
|
|
340
|
+
if (currentSubs) {
|
|
341
|
+
currentSubs.delete(callback);
|
|
342
|
+
if (currentSubs.size === 0) {
|
|
343
|
+
get().pathSubscribers.delete(path);
|
|
270
344
|
}
|
|
271
|
-
|
|
272
|
-
});
|
|
345
|
+
}
|
|
273
346
|
};
|
|
274
347
|
},
|
|
348
|
+
|
|
349
|
+
notifyPathSubscribers: (updatedPath, newValue) => {
|
|
350
|
+
// <-- Now accepts newValue
|
|
351
|
+
const subscribers = get().pathSubscribers;
|
|
352
|
+
const subs = subscribers.get(updatedPath);
|
|
353
|
+
|
|
354
|
+
if (subs) {
|
|
355
|
+
// Pass the newValue to every callback
|
|
356
|
+
subs.forEach((callback) => callback(newValue));
|
|
357
|
+
}
|
|
358
|
+
},
|
|
275
359
|
initializeShadowState: (key: string, initialState: any) => {
|
|
276
|
-
const
|
|
360
|
+
const existingShadowStore = new Map(get().shadowStateStore);
|
|
277
361
|
|
|
278
362
|
const processValue = (value: any, path: string[]) => {
|
|
279
|
-
const nodeKey = [key, ...path].join(
|
|
363
|
+
const nodeKey = [key, ...path].join('.');
|
|
280
364
|
|
|
281
365
|
if (Array.isArray(value)) {
|
|
366
|
+
// Handle arrays as before
|
|
282
367
|
const childIds: string[] = [];
|
|
283
368
|
|
|
284
369
|
value.forEach((item) => {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
370
|
+
const itemId = `id:${ulid()}`;
|
|
371
|
+
childIds.push(nodeKey + '.' + itemId);
|
|
372
|
+
});
|
|
288
373
|
|
|
289
|
-
|
|
290
|
-
childIds.push(itemId);
|
|
374
|
+
existingShadowStore.set(nodeKey, { arrayKeys: childIds });
|
|
291
375
|
|
|
292
|
-
|
|
293
|
-
|
|
376
|
+
value.forEach((item, index) => {
|
|
377
|
+
const itemId = childIds[index]!.split('.').pop();
|
|
378
|
+
processValue(item, [...path!, itemId!]);
|
|
294
379
|
});
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
} else if (typeof value === "object" && value !== null) {
|
|
302
|
-
newShadowStore.set(nodeKey, { id: ulid() });
|
|
380
|
+
} else if (isSimpleObject(value)) {
|
|
381
|
+
// Only create field mappings for simple objects
|
|
382
|
+
const fields = Object.fromEntries(
|
|
383
|
+
Object.keys(value).map((k) => [k, nodeKey + '.' + k])
|
|
384
|
+
);
|
|
385
|
+
existingShadowStore.set(nodeKey, { fields });
|
|
303
386
|
|
|
304
387
|
Object.keys(value).forEach((k) => {
|
|
305
388
|
processValue(value[k], [...path, k]);
|
|
306
389
|
});
|
|
307
390
|
} else {
|
|
308
|
-
|
|
391
|
+
// Treat everything else (including Uint8Array) as primitive values
|
|
392
|
+
existingShadowStore.set(nodeKey, { value });
|
|
309
393
|
}
|
|
310
394
|
};
|
|
311
395
|
|
|
312
396
|
processValue(initialState, []);
|
|
397
|
+
set({ shadowStateStore: existingShadowStore });
|
|
398
|
+
},
|
|
313
399
|
|
|
314
|
-
|
|
400
|
+
getShadowValue: (fullKey: string, validArrayIds?: string[]) => {
|
|
401
|
+
const shadowMeta = get().shadowStateStore.get(fullKey);
|
|
402
|
+
|
|
403
|
+
// If no metadata found, return undefined
|
|
404
|
+
if (!shadowMeta) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// For primitive values, return the value
|
|
409
|
+
if (shadowMeta.value !== undefined) {
|
|
410
|
+
return shadowMeta.value;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// For arrays, reconstruct with possible validArrayIds
|
|
414
|
+
if (shadowMeta.arrayKeys) {
|
|
415
|
+
const arrayKeys = validArrayIds ?? shadowMeta.arrayKeys;
|
|
416
|
+
const items = arrayKeys.map((itemKey) => {
|
|
417
|
+
// RECURSIVELY call getShadowValue for each item
|
|
418
|
+
return get().getShadowValue(itemKey);
|
|
419
|
+
});
|
|
420
|
+
return items;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// For objects with fields, reconstruct object
|
|
424
|
+
if (shadowMeta.fields) {
|
|
425
|
+
const reconstructedObject: any = {};
|
|
426
|
+
Object.entries(shadowMeta.fields).forEach(([key, fieldPath]) => {
|
|
427
|
+
// RECURSIVELY call getShadowValue for each field
|
|
428
|
+
reconstructedObject[key] = get().getShadowValue(fieldPath as string);
|
|
429
|
+
});
|
|
430
|
+
return reconstructedObject;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return undefined;
|
|
315
434
|
},
|
|
316
|
-
getShadowMetadata: (
|
|
317
|
-
|
|
435
|
+
getShadowMetadata: (
|
|
436
|
+
key: string,
|
|
437
|
+
path: string[],
|
|
438
|
+
validArrayIds?: string[]
|
|
439
|
+
) => {
|
|
440
|
+
const fullKey = [key, ...path].join('.');
|
|
441
|
+
let data = get().shadowStateStore.get(fullKey);
|
|
442
|
+
|
|
318
443
|
return get().shadowStateStore.get(fullKey);
|
|
319
444
|
},
|
|
320
445
|
|
|
321
446
|
setShadowMetadata: (key: string, path: string[], metadata: any) => {
|
|
322
|
-
const fullKey = [key, ...path].join(
|
|
447
|
+
const fullKey = [key, ...path].join('.');
|
|
323
448
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
324
449
|
const existing = newShadowStore.get(fullKey) || { id: ulid() };
|
|
325
450
|
newShadowStore.set(fullKey, { ...existing, ...metadata });
|
|
326
451
|
set({ shadowStateStore: newShadowStore });
|
|
452
|
+
},
|
|
453
|
+
setTransformCache: (
|
|
454
|
+
key: string,
|
|
455
|
+
path: string[],
|
|
456
|
+
cacheKey: string,
|
|
457
|
+
cacheData: any
|
|
458
|
+
) => {
|
|
459
|
+
const fullKey = [key, ...path].join('.');
|
|
460
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
461
|
+
const existing = newShadowStore.get(fullKey) || {};
|
|
327
462
|
|
|
328
|
-
if
|
|
329
|
-
|
|
330
|
-
|
|
463
|
+
// Initialize transformCaches if it doesn't exist
|
|
464
|
+
if (!existing.transformCaches) {
|
|
465
|
+
existing.transformCaches = new Map();
|
|
331
466
|
}
|
|
332
|
-
},
|
|
333
467
|
|
|
468
|
+
// Update just the specific cache entry
|
|
469
|
+
existing.transformCaches.set(cacheKey, cacheData);
|
|
470
|
+
|
|
471
|
+
// Update shadow store WITHOUT notifying path subscribers
|
|
472
|
+
newShadowStore.set(fullKey, existing);
|
|
473
|
+
set({ shadowStateStore: newShadowStore });
|
|
474
|
+
|
|
475
|
+
// Don't call notifyPathSubscribers here - cache updates shouldn't trigger renders
|
|
476
|
+
},
|
|
334
477
|
insertShadowArrayElement: (
|
|
335
478
|
key: string,
|
|
336
479
|
arrayPath: string[],
|
|
337
480
|
newItem: any
|
|
338
481
|
) => {
|
|
339
482
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
340
|
-
const arrayKey = [key, ...arrayPath].join(
|
|
483
|
+
const arrayKey = [key, ...arrayPath].join('.');
|
|
341
484
|
const parentMeta = newShadowStore.get(arrayKey);
|
|
342
|
-
const newArrayState = get().getNestedState(key, arrayPath) as any[];
|
|
343
485
|
|
|
344
486
|
if (!parentMeta || !parentMeta.arrayKeys) return;
|
|
345
487
|
|
|
346
|
-
const newItemId = `id:${
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
if (newIndex === -1) return;
|
|
488
|
+
const newItemId = `id:${ulid()}`;
|
|
489
|
+
const fullItemKey = arrayKey + '.' + newItemId;
|
|
350
490
|
|
|
491
|
+
// Just add to the end (or at a specific index if provided)
|
|
351
492
|
const newArrayKeys = [...parentMeta.arrayKeys];
|
|
352
|
-
newArrayKeys.
|
|
493
|
+
newArrayKeys.push(fullItemKey); // Or use splice if you have an index
|
|
353
494
|
newShadowStore.set(arrayKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
354
495
|
|
|
496
|
+
// Process the new item - but use the correct logic
|
|
355
497
|
const processNewItem = (value: any, path: string[]) => {
|
|
356
|
-
const nodeKey = [key, ...path].join(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
498
|
+
const nodeKey = [key, ...path].join('.');
|
|
499
|
+
|
|
500
|
+
if (Array.isArray(value)) {
|
|
501
|
+
// Handle arrays...
|
|
502
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
503
|
+
// Create fields mapping
|
|
504
|
+
const fields = Object.fromEntries(
|
|
505
|
+
Object.keys(value).map((k) => [k, nodeKey + '.' + k])
|
|
506
|
+
);
|
|
507
|
+
newShadowStore.set(nodeKey, { fields });
|
|
508
|
+
|
|
509
|
+
// Process each field
|
|
510
|
+
Object.entries(value).forEach(([k, v]) => {
|
|
511
|
+
processNewItem(v, [...path, k]);
|
|
361
512
|
});
|
|
362
513
|
} else {
|
|
363
|
-
|
|
514
|
+
// Primitive value
|
|
515
|
+
newShadowStore.set(nodeKey, { value });
|
|
364
516
|
}
|
|
365
517
|
};
|
|
366
518
|
|
|
367
519
|
processNewItem(newItem, [...arrayPath, newItemId]);
|
|
368
|
-
|
|
369
520
|
set({ shadowStateStore: newShadowStore });
|
|
370
|
-
},
|
|
371
521
|
|
|
522
|
+
get().notifyPathSubscribers(arrayKey, {
|
|
523
|
+
type: 'INSERT',
|
|
524
|
+
path: arrayKey,
|
|
525
|
+
itemKey: fullItemKey,
|
|
526
|
+
});
|
|
527
|
+
},
|
|
372
528
|
removeShadowArrayElement: (key: string, itemPath: string[]) => {
|
|
373
529
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
374
|
-
const itemKey = [key, ...itemPath].join(".");
|
|
375
|
-
const itemIdToRemove = itemPath[itemPath.length - 1];
|
|
376
530
|
|
|
531
|
+
// Get the full item key (e.g., "stateKey.products.id:xxx")
|
|
532
|
+
const itemKey = [key, ...itemPath].join('.');
|
|
533
|
+
|
|
534
|
+
// Extract parent path and item ID
|
|
377
535
|
const parentPath = itemPath.slice(0, -1);
|
|
378
|
-
const parentKey = [key, ...parentPath].join(
|
|
536
|
+
const parentKey = [key, ...parentPath].join('.');
|
|
537
|
+
|
|
538
|
+
// Get parent metadata
|
|
379
539
|
const parentMeta = newShadowStore.get(parentKey);
|
|
380
540
|
|
|
381
541
|
if (parentMeta && parentMeta.arrayKeys) {
|
|
382
|
-
|
|
383
|
-
|
|
542
|
+
// Find the index of the item to remove
|
|
543
|
+
const indexToRemove = parentMeta.arrayKeys.findIndex(
|
|
544
|
+
(arrayItemKey) => arrayItemKey === itemKey
|
|
384
545
|
);
|
|
385
|
-
newShadowStore.set(parentKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
386
|
-
}
|
|
387
546
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
547
|
+
if (indexToRemove !== -1) {
|
|
548
|
+
// Create new array keys with the item removed
|
|
549
|
+
const newArrayKeys = parentMeta.arrayKeys.filter(
|
|
550
|
+
(arrayItemKey) => arrayItemKey !== itemKey
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
// Update parent with new array keys
|
|
554
|
+
newShadowStore.set(parentKey, {
|
|
555
|
+
...parentMeta,
|
|
556
|
+
arrayKeys: newArrayKeys,
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// Delete all data associated with the removed item
|
|
560
|
+
const prefixToDelete = itemKey + '.';
|
|
561
|
+
for (const k of Array.from(newShadowStore.keys())) {
|
|
562
|
+
if (k === itemKey || k.startsWith(prefixToDelete)) {
|
|
563
|
+
newShadowStore.delete(k);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
392
566
|
}
|
|
393
567
|
}
|
|
394
568
|
|
|
395
569
|
set({ shadowStateStore: newShadowStore });
|
|
570
|
+
|
|
571
|
+
get().notifyPathSubscribers(parentKey, {
|
|
572
|
+
type: 'REMOVE',
|
|
573
|
+
path: parentKey,
|
|
574
|
+
itemKey: itemKey, // The exact ID of the removed item
|
|
575
|
+
});
|
|
396
576
|
},
|
|
397
|
-
updateShadowAtPath: (key
|
|
398
|
-
const fullKey = [key, ...path].join(".");
|
|
577
|
+
updateShadowAtPath: (key, path, newValue) => {
|
|
399
578
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
400
|
-
const
|
|
401
|
-
|
|
579
|
+
const fullKey = [key, ...path].join('.');
|
|
580
|
+
|
|
581
|
+
const updateValue = (currentKey: string, valueToSet: any) => {
|
|
582
|
+
const meta = newShadowStore.get(currentKey);
|
|
583
|
+
|
|
584
|
+
// If it's a simple object with fields, update recursively
|
|
585
|
+
if (isSimpleObject(valueToSet) && meta && meta.fields) {
|
|
586
|
+
for (const fieldKey in valueToSet) {
|
|
587
|
+
if (Object.prototype.hasOwnProperty.call(valueToSet, fieldKey)) {
|
|
588
|
+
const childPath = meta.fields[fieldKey];
|
|
589
|
+
const childValue = valueToSet[fieldKey];
|
|
590
|
+
|
|
591
|
+
if (childPath) {
|
|
592
|
+
updateValue(childPath as string, childValue);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
} else {
|
|
597
|
+
// For primitives (including Uint8Array), just replace the value
|
|
598
|
+
// This gives you useState-like behavior
|
|
599
|
+
const existing = newShadowStore.get(currentKey) || {};
|
|
600
|
+
newShadowStore.set(currentKey, { ...existing, value: valueToSet });
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
updateValue(fullKey, newValue);
|
|
605
|
+
get().notifyPathSubscribers(fullKey, { type: 'UPDATE', newValue });
|
|
402
606
|
set({ shadowStateStore: newShadowStore });
|
|
403
607
|
},
|
|
608
|
+
selectedIndicesMap: new Map<string, string>(),
|
|
609
|
+
getSelectedIndex: (arrayKey: string, validIds?: string[]): number => {
|
|
610
|
+
const itemKey = get().selectedIndicesMap.get(arrayKey);
|
|
611
|
+
|
|
612
|
+
if (!itemKey) return -1;
|
|
613
|
+
|
|
614
|
+
// Use validIds if provided (for filtered views), otherwise use all arrayKeys
|
|
615
|
+
const arrayKeys =
|
|
616
|
+
validIds ||
|
|
617
|
+
getGlobalStore.getState().getShadowMetadata(arrayKey, [])?.arrayKeys;
|
|
404
618
|
|
|
405
|
-
|
|
619
|
+
if (!arrayKeys) return -1;
|
|
406
620
|
|
|
407
|
-
|
|
408
|
-
getSelectedIndex: (stateKey: string, parentPath: string) => {
|
|
409
|
-
const stateMap = get().selectedIndicesMap.get(stateKey);
|
|
410
|
-
if (!stateMap) return undefined;
|
|
411
|
-
return stateMap.get(parentPath);
|
|
621
|
+
return arrayKeys.indexOf(itemKey);
|
|
412
622
|
},
|
|
413
623
|
|
|
414
|
-
setSelectedIndex: (
|
|
415
|
-
stateKey: string,
|
|
416
|
-
parentPath: string,
|
|
417
|
-
index: number | undefined
|
|
418
|
-
) => {
|
|
624
|
+
setSelectedIndex: (arrayKey: string, itemKey: string | undefined) => {
|
|
419
625
|
set((state) => {
|
|
420
|
-
const newMap =
|
|
421
|
-
let stateMap = newMap.get(stateKey);
|
|
422
|
-
|
|
423
|
-
if (!stateMap) {
|
|
424
|
-
stateMap = new Map<string, number>();
|
|
425
|
-
newMap.set(stateKey, stateMap);
|
|
426
|
-
}
|
|
626
|
+
const newMap = state.selectedIndicesMap;
|
|
427
627
|
|
|
428
|
-
if (
|
|
429
|
-
|
|
628
|
+
if (itemKey === undefined) {
|
|
629
|
+
newMap.delete(arrayKey);
|
|
430
630
|
} else {
|
|
431
|
-
|
|
432
|
-
|
|
631
|
+
if (newMap.has(arrayKey)) {
|
|
632
|
+
get().notifyPathSubscribers(newMap.get(arrayKey)!, {
|
|
633
|
+
type: 'THIS_UNSELECTED',
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
newMap.set(arrayKey, itemKey);
|
|
433
637
|
|
|
638
|
+
get().notifyPathSubscribers(itemKey, {
|
|
639
|
+
type: 'THIS_SELECTED',
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
get().notifyPathSubscribers(arrayKey, {
|
|
643
|
+
type: 'GET_SELECTED',
|
|
644
|
+
});
|
|
434
645
|
return {
|
|
435
646
|
...state,
|
|
436
647
|
selectedIndicesMap: newMap,
|
|
437
648
|
};
|
|
438
649
|
});
|
|
439
650
|
},
|
|
440
|
-
clearSelectedIndex: ({
|
|
441
|
-
stateKey,
|
|
442
|
-
path,
|
|
443
|
-
}: {
|
|
444
|
-
stateKey: string;
|
|
445
|
-
path: string[];
|
|
446
|
-
}) => {
|
|
651
|
+
clearSelectedIndex: ({ arrayKey }: { arrayKey: string }): void => {
|
|
447
652
|
set((state) => {
|
|
448
|
-
const newMap =
|
|
449
|
-
const
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
|
|
653
|
+
const newMap = state.selectedIndicesMap;
|
|
654
|
+
const acutalKey = newMap.get(arrayKey);
|
|
655
|
+
if (acutalKey) {
|
|
656
|
+
get().notifyPathSubscribers(acutalKey, {
|
|
657
|
+
type: 'CLEAR_SELECTION',
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
newMap.delete(arrayKey);
|
|
662
|
+
get().notifyPathSubscribers(arrayKey, {
|
|
663
|
+
type: 'CLEAR_SELECTION',
|
|
664
|
+
});
|
|
453
665
|
return {
|
|
454
666
|
...state,
|
|
455
667
|
selectedIndicesMap: newMap,
|
|
@@ -461,126 +673,23 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
461
673
|
const newOuterMap = new Map(state.selectedIndicesMap);
|
|
462
674
|
const changed = newOuterMap.delete(stateKey);
|
|
463
675
|
if (changed) {
|
|
464
|
-
console.log(
|
|
465
|
-
`Cleared selected indices map entry for stateKey: ${stateKey}`
|
|
466
|
-
);
|
|
467
676
|
return { selectedIndicesMap: newOuterMap };
|
|
468
677
|
} else {
|
|
469
678
|
return {};
|
|
470
679
|
}
|
|
471
680
|
});
|
|
472
681
|
},
|
|
473
|
-
stateComponents: new Map(),
|
|
474
|
-
subscribe: (listener: () => void) => {
|
|
475
|
-
// zustand's subscribe returns an unsubscribe function
|
|
476
|
-
return get().subscribe(listener);
|
|
477
|
-
},
|
|
478
|
-
|
|
479
|
-
reactiveDeps: {},
|
|
480
|
-
setReactiveDeps: (key, record) =>
|
|
481
|
-
set((state) => ({
|
|
482
|
-
...state,
|
|
483
|
-
reactiveDeps: {
|
|
484
|
-
...state.reactiveDeps,
|
|
485
|
-
[key]: record,
|
|
486
|
-
},
|
|
487
|
-
})),
|
|
488
|
-
deleteReactiveDeps: (key) =>
|
|
489
|
-
set((state) => {
|
|
490
|
-
const { [key]: _, ...rest } = state.reactiveDeps;
|
|
491
|
-
return {
|
|
492
|
-
...state,
|
|
493
|
-
reactiveDeps: rest,
|
|
494
|
-
};
|
|
495
|
-
}),
|
|
496
|
-
|
|
497
|
-
reRenderTriggerPrevValue: {},
|
|
498
|
-
signalDomElements: new Map(),
|
|
499
|
-
addSignalElement: (
|
|
500
|
-
signalId: string,
|
|
501
|
-
elementInfo: { instanceId: string; parentId: string; position: number }
|
|
502
|
-
) => {
|
|
503
|
-
const current = get().signalDomElements;
|
|
504
|
-
if (!current.has(signalId)) {
|
|
505
|
-
current.set(signalId, new Set());
|
|
506
|
-
}
|
|
507
|
-
current.get(signalId)!.add(elementInfo);
|
|
508
682
|
|
|
509
|
-
set({ signalDomElements: new Map(current) }); // Create new reference to trigger update
|
|
510
|
-
},
|
|
511
|
-
removeSignalElement: (signalId: string, instanceId: string) => {
|
|
512
|
-
const current = get().signalDomElements;
|
|
513
|
-
const elements = current.get(signalId);
|
|
514
|
-
if (elements) {
|
|
515
|
-
elements.forEach((el) => {
|
|
516
|
-
if (el.instanceId === instanceId) {
|
|
517
|
-
elements.delete(el);
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
set({ signalDomElements: new Map(current) });
|
|
522
|
-
},
|
|
523
683
|
initialStateOptions: {},
|
|
524
|
-
|
|
684
|
+
|
|
525
685
|
stateTimeline: {},
|
|
526
686
|
cogsStateStore: {},
|
|
527
687
|
stateLog: {},
|
|
528
|
-
isLoadingGlobal: {},
|
|
529
688
|
|
|
530
689
|
initialStateGlobal: {},
|
|
531
|
-
iniitialCreatedState: {},
|
|
532
|
-
updateInitialCreatedState: (key, newState) => {
|
|
533
|
-
set((prev) => ({
|
|
534
|
-
iniitialCreatedState: {
|
|
535
|
-
...prev.iniitialCreatedState,
|
|
536
|
-
[key]: newState,
|
|
537
|
-
},
|
|
538
|
-
}));
|
|
539
|
-
},
|
|
540
690
|
|
|
541
691
|
validationErrors: new Map(),
|
|
542
692
|
|
|
543
|
-
serverState: {},
|
|
544
|
-
|
|
545
|
-
serverSyncActions: {},
|
|
546
|
-
|
|
547
|
-
serverSyncLog: {},
|
|
548
|
-
serverSideOrNot: {},
|
|
549
|
-
setServerSyncLog: (key, newValue) => {
|
|
550
|
-
set((state) => ({
|
|
551
|
-
serverSyncLog: {
|
|
552
|
-
...state.serverSyncLog,
|
|
553
|
-
[key]: [...(state.serverSyncLog[key] ?? []), newValue],
|
|
554
|
-
},
|
|
555
|
-
}));
|
|
556
|
-
},
|
|
557
|
-
setServerSideOrNot: (key, value) => {
|
|
558
|
-
set((state) => ({
|
|
559
|
-
serverSideOrNot: {
|
|
560
|
-
...state.serverSideOrNot,
|
|
561
|
-
[key]: value,
|
|
562
|
-
},
|
|
563
|
-
}));
|
|
564
|
-
},
|
|
565
|
-
getServerSideOrNot: (key) => {
|
|
566
|
-
return get().serverSideOrNot[key];
|
|
567
|
-
},
|
|
568
|
-
|
|
569
|
-
getThisLocalUpdate: (key: string) => {
|
|
570
|
-
return get().stateLog[key];
|
|
571
|
-
},
|
|
572
|
-
setServerState: <StateKey extends StateKeys>(
|
|
573
|
-
key: StateKey,
|
|
574
|
-
value: StateValue
|
|
575
|
-
) => {
|
|
576
|
-
set((prev) => ({
|
|
577
|
-
serverState: {
|
|
578
|
-
...prev.serverState,
|
|
579
|
-
[key]: value,
|
|
580
|
-
},
|
|
581
|
-
}));
|
|
582
|
-
},
|
|
583
|
-
|
|
584
693
|
setStateLog: (
|
|
585
694
|
key: string,
|
|
586
695
|
updater: (prevUpdates: UpdateTypeDetail[]) => UpdateTypeDetail[]
|
|
@@ -596,28 +705,12 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
596
705
|
};
|
|
597
706
|
});
|
|
598
707
|
},
|
|
599
|
-
|
|
600
|
-
set((prev) => ({
|
|
601
|
-
isLoadingGlobal: {
|
|
602
|
-
...prev.isLoadingGlobal,
|
|
603
|
-
[key]: value,
|
|
604
|
-
},
|
|
605
|
-
}));
|
|
606
|
-
},
|
|
607
|
-
setServerSyncActions: (key: string, value: SyncActionsType<any>) => {
|
|
608
|
-
set((prev) => ({
|
|
609
|
-
serverSyncActions: {
|
|
610
|
-
...prev.serverSyncActions,
|
|
611
|
-
[key]: value,
|
|
612
|
-
},
|
|
613
|
-
}));
|
|
614
|
-
},
|
|
708
|
+
|
|
615
709
|
addValidationError: (path, message) => {
|
|
616
|
-
console.log("addValidationError---");
|
|
617
710
|
set((prev) => {
|
|
618
711
|
const updatedErrors = new Map(prev.validationErrors);
|
|
619
712
|
const existingMessages = updatedErrors.get(path) || [];
|
|
620
|
-
console.log(
|
|
713
|
+
console.log('addValidationError', path, message, existingMessages);
|
|
621
714
|
// Append the new message instead of replacing
|
|
622
715
|
updatedErrors.set(path, [...existingMessages, message]);
|
|
623
716
|
return { validationErrors: updatedErrors };
|
|
@@ -628,9 +721,9 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
628
721
|
const updatedErrors = new Map(prev.validationErrors);
|
|
629
722
|
|
|
630
723
|
let doSomething = false;
|
|
631
|
-
const pathArray = path.split(
|
|
724
|
+
const pathArray = path.split('.');
|
|
632
725
|
Array.from(updatedErrors.keys()).forEach((key) => {
|
|
633
|
-
const keyArray = key.split(
|
|
726
|
+
const keyArray = key.split('.');
|
|
634
727
|
if (keyArray.length >= pathArray.length) {
|
|
635
728
|
let match = true;
|
|
636
729
|
for (let i = 0; i < pathArray.length; i++) {
|
|
@@ -653,11 +746,11 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
653
746
|
getValidationErrors: (path: string) => {
|
|
654
747
|
const errors: string[] = [];
|
|
655
748
|
const valErrors = get().validationErrors;
|
|
656
|
-
const pathArray = path.split(
|
|
749
|
+
const pathArray = path.split('.');
|
|
657
750
|
|
|
658
751
|
// Helper to check if an index matches either a wildcard or is in an array of indices
|
|
659
752
|
const isIndexMatch = (pathSegment: string, keySegment: string) => {
|
|
660
|
-
if (pathSegment ===
|
|
753
|
+
if (pathSegment === '[*]') return true;
|
|
661
754
|
if (Array.isArray(pathSegment)) {
|
|
662
755
|
return pathSegment.includes(parseInt(keySegment));
|
|
663
756
|
}
|
|
@@ -665,7 +758,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
665
758
|
};
|
|
666
759
|
|
|
667
760
|
Array.from(valErrors.keys()).forEach((key) => {
|
|
668
|
-
const keyArray = key.split(
|
|
761
|
+
const keyArray = key.split('.');
|
|
669
762
|
if (keyArray.length >= pathArray.length) {
|
|
670
763
|
let match = true;
|
|
671
764
|
for (let i = 0; i < pathArray.length; i++) {
|
|
@@ -673,7 +766,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
673
766
|
const keySegment = keyArray[i]!;
|
|
674
767
|
|
|
675
768
|
// If current path segment is a number or [*], we need special handling
|
|
676
|
-
if (pathSegment ===
|
|
769
|
+
if (pathSegment === '[*]' || Array.isArray(pathSegment)) {
|
|
677
770
|
// Key segment should be a number if we're using [*] or array indices
|
|
678
771
|
const keyIndex = parseInt(keySegment);
|
|
679
772
|
if (isNaN(keyIndex)) {
|
|
@@ -705,48 +798,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
705
798
|
getInitialOptions: (key) => {
|
|
706
799
|
return get().initialStateOptions[key];
|
|
707
800
|
},
|
|
708
|
-
getNestedState: (key: string, path: string[]) => {
|
|
709
|
-
const rootState = get().cogsStateStore[key];
|
|
710
|
-
|
|
711
|
-
const resolvePath = (obj: any, pathArray: string[]): any => {
|
|
712
|
-
if (pathArray.length === 0 || obj === undefined) {
|
|
713
|
-
return obj;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
const currentSegment = pathArray[0];
|
|
717
|
-
const remainingPath = pathArray.slice(1);
|
|
718
|
-
|
|
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 === "[*]") {
|
|
734
|
-
if (!Array.isArray(obj)) {
|
|
735
|
-
console.warn("Asterisk notation used on non-array value");
|
|
736
|
-
return undefined;
|
|
737
|
-
}
|
|
738
|
-
if (remainingPath.length === 0) return obj;
|
|
739
|
-
const results = obj.map((item) => resolvePath(item, remainingPath));
|
|
740
|
-
return Array.isArray(results[0]) ? results.flat() : results;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Handle standard object property access and numeric array indices
|
|
744
|
-
const nextObj = obj[currentSegment as keyof typeof obj];
|
|
745
|
-
return resolvePath(nextObj, remainingPath);
|
|
746
|
-
};
|
|
747
801
|
|
|
748
|
-
return resolvePath(rootState, path);
|
|
749
|
-
},
|
|
750
802
|
setInitialStateOptions: (key, value) => {
|
|
751
803
|
set((prev) => ({
|
|
752
804
|
initialStateOptions: {
|
|
@@ -763,49 +815,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
763
815
|
},
|
|
764
816
|
}));
|
|
765
817
|
},
|
|
766
|
-
getUpdaterState: (key) => {
|
|
767
|
-
return get().updaterState[key];
|
|
768
|
-
},
|
|
769
|
-
setUpdaterState: (key, newUpdater) => {
|
|
770
|
-
const current = get().updaterState;
|
|
771
|
-
|
|
772
|
-
if (!key || !newUpdater) return;
|
|
773
|
-
|
|
774
|
-
set({ updaterState: { ...(current ?? {}), [key]: newUpdater } });
|
|
775
|
-
},
|
|
776
|
-
getKeyState: <StateKey extends StateKeys>(key: StateKey) => {
|
|
777
|
-
return get().cogsStateStore[key];
|
|
778
|
-
},
|
|
779
|
-
|
|
780
|
-
setState: <StateKey extends StateKeys>(key: StateKey, value: StateValue) => {
|
|
781
|
-
set((prev) => {
|
|
782
|
-
return {
|
|
783
|
-
cogsStateStore: {
|
|
784
|
-
...prev.cogsStateStore,
|
|
785
|
-
[key]:
|
|
786
|
-
typeof value === "function"
|
|
787
|
-
? value(prev.cogsStateStore[key])
|
|
788
|
-
: value,
|
|
789
|
-
},
|
|
790
|
-
};
|
|
791
|
-
});
|
|
792
|
-
},
|
|
793
|
-
setInitialStates: <StateKey extends StateKeys>(initialState: StateValue) => {
|
|
794
|
-
set((prev) => ({
|
|
795
|
-
cogsStateStore: {
|
|
796
|
-
...prev.cogsStateStore,
|
|
797
|
-
...initialState,
|
|
798
|
-
},
|
|
799
|
-
}));
|
|
800
|
-
},
|
|
801
|
-
setCreatedState: (initialState: StateValue) => {
|
|
802
|
-
set((prev) => ({
|
|
803
|
-
iniitialCreatedState: {
|
|
804
|
-
...prev.cogsStateStore,
|
|
805
|
-
...initialState,
|
|
806
|
-
},
|
|
807
|
-
}));
|
|
808
|
-
},
|
|
809
818
|
|
|
810
819
|
syncInfoStore: new Map<string, SyncInfo>(),
|
|
811
820
|
setSyncInfo: (key: string, syncInfo: SyncInfo) =>
|