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/CogsState.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import {
|
|
4
4
|
createElement,
|
|
5
5
|
startTransition,
|
|
@@ -34,15 +34,11 @@ import superjson from "superjson";
|
|
|
34
34
|
import { v4 as uuidv4 } from "uuid";
|
|
35
35
|
import { z } from "zod";
|
|
36
36
|
|
|
37
|
-
import {
|
|
38
|
-
formRefStore,
|
|
39
|
-
getGlobalStore,
|
|
40
|
-
type ComponentsType,
|
|
41
|
-
type ItemMeta,
|
|
42
|
-
} from "./store.js";
|
|
37
|
+
import { formRefStore, getGlobalStore, type ComponentsType } from "./store.js";
|
|
43
38
|
import { useCogsConfig } from "./CogsStateClient.js";
|
|
44
39
|
import { applyPatch } from "fast-json-patch";
|
|
45
40
|
import useMeasure from "react-use-measure";
|
|
41
|
+
import { ulid } from "ulid";
|
|
46
42
|
|
|
47
43
|
type Prettify<T> = { [K in keyof T]: T[K] } & {};
|
|
48
44
|
|
|
@@ -568,7 +564,7 @@ export function addStateOptions<T extends unknown>(
|
|
|
568
564
|
return { initialState: initialState, formElements, validation } as T;
|
|
569
565
|
}
|
|
570
566
|
|
|
571
|
-
export const createCogsState = <State extends Record<
|
|
567
|
+
export const createCogsState = <State extends Record<StateKeys, unknown>>(
|
|
572
568
|
initialState: State,
|
|
573
569
|
opt?: { formElements?: FormsElementsType; validation?: ValidationOptionsType }
|
|
574
570
|
) => {
|
|
@@ -1096,7 +1092,6 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1096
1092
|
}
|
|
1097
1093
|
}
|
|
1098
1094
|
|
|
1099
|
-
console.log("shadowState", store.shadowStateStore);
|
|
1100
1095
|
if (
|
|
1101
1096
|
updateObj.updateType === "update" &&
|
|
1102
1097
|
(validationKey || latestInitialOptionsRef.current?.validation?.key) &&
|
|
@@ -1123,22 +1118,20 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1123
1118
|
updateObj.updateType === "insert" &&
|
|
1124
1119
|
latestInitialOptionsRef.current?.validation?.key
|
|
1125
1120
|
) {
|
|
1126
|
-
|
|
1121
|
+
const getValidation = getValidationErrors(
|
|
1127
1122
|
latestInitialOptionsRef.current?.validation?.key +
|
|
1128
1123
|
"." +
|
|
1129
1124
|
arrayWithoutIndex.join(".")
|
|
1130
1125
|
);
|
|
1131
1126
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
getValidation.filter(([k, v]) => {
|
|
1127
|
+
getValidation.filter((k) => {
|
|
1135
1128
|
let length = k?.split(".").length;
|
|
1129
|
+
const v = ""; // Placeholder as `v` is not used from getValidationErrors
|
|
1136
1130
|
|
|
1137
1131
|
if (
|
|
1138
1132
|
k == arrayWithoutIndex.join(".") &&
|
|
1139
1133
|
length == arrayWithoutIndex.length - 1
|
|
1140
1134
|
) {
|
|
1141
|
-
// console.log(length, pathWithoutIndex.length);
|
|
1142
1135
|
let newKey = k + "." + arrayWithoutIndex;
|
|
1143
1136
|
removeValidationError(k!);
|
|
1144
1137
|
addValidationError(newKey, v!);
|
|
@@ -1147,7 +1140,6 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1147
1140
|
}
|
|
1148
1141
|
|
|
1149
1142
|
const stateEntry = store.stateComponents.get(thisKey);
|
|
1150
|
-
console.log("stateEntry >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", stateEntry);
|
|
1151
1143
|
if (stateEntry) {
|
|
1152
1144
|
const changedPaths = getDifferences(prevValue, payload);
|
|
1153
1145
|
const changedPathsSet = new Set(changedPaths);
|
|
@@ -1164,7 +1156,7 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1164
1156
|
const reactiveTypes = Array.isArray(component.reactiveType)
|
|
1165
1157
|
? component.reactiveType
|
|
1166
1158
|
: [component.reactiveType || "component"];
|
|
1167
|
-
|
|
1159
|
+
|
|
1168
1160
|
if (reactiveTypes.includes("none")) continue;
|
|
1169
1161
|
if (reactiveTypes.includes("all")) {
|
|
1170
1162
|
component.forceUpdate();
|
|
@@ -1238,17 +1230,7 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1238
1230
|
}
|
|
1239
1231
|
const timeStamp = Date.now();
|
|
1240
1232
|
|
|
1241
|
-
|
|
1242
|
-
const arrayPath = path.slice(0, -1);
|
|
1243
|
-
const arrayValue = getNestedValue(payload, arrayPath);
|
|
1244
|
-
|
|
1245
|
-
return i === path.length - 1 &&
|
|
1246
|
-
["insert", "cut"].includes(updateObj.updateType)
|
|
1247
|
-
? (arrayValue.length - 1).toString()
|
|
1248
|
-
: p;
|
|
1249
|
-
});
|
|
1250
|
-
|
|
1251
|
-
const { oldValue, newValue } = getUpdateValues(
|
|
1233
|
+
let { oldValue, newValue } = getUpdateValues(
|
|
1252
1234
|
updateObj.updateType,
|
|
1253
1235
|
prevValue,
|
|
1254
1236
|
payload,
|
|
@@ -1265,56 +1247,59 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1265
1247
|
} satisfies UpdateTypeDetail;
|
|
1266
1248
|
|
|
1267
1249
|
switch (updateObj.updateType) {
|
|
1268
|
-
case "
|
|
1269
|
-
// For updates, just mirror the structure at the path
|
|
1270
|
-
store.updateShadowAtPath(thisKey, path, payload);
|
|
1271
|
-
break;
|
|
1272
|
-
|
|
1273
|
-
case "insert":
|
|
1274
|
-
// For array insert, create a new metadata object with a ULID.
|
|
1250
|
+
case "insert": {
|
|
1275
1251
|
const parentPath = path.slice(0, -1);
|
|
1252
|
+
const idSegment = path[path.length - 1]!; // e.g., 'id:xyz'
|
|
1253
|
+
const targetId = idSegment.split(":")[1];
|
|
1254
|
+
const newArray = getNestedValue(payload, parentPath);
|
|
1276
1255
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
const newId = newValue.id ?? newValue.key ?? ulid();
|
|
1280
|
-
const newMeta = { _cogsId: newId };
|
|
1256
|
+
newValue = newArray.find((item: any) => item.id == targetId);
|
|
1257
|
+
oldValue = null;
|
|
1281
1258
|
|
|
1282
|
-
store.insertShadowArrayElement(thisKey, parentPath,
|
|
1259
|
+
store.insertShadowArrayElement(thisKey, parentPath, newValue);
|
|
1283
1260
|
break;
|
|
1261
|
+
}
|
|
1284
1262
|
|
|
1285
|
-
case "cut":
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1263
|
+
case "cut": {
|
|
1264
|
+
oldValue = getNestedValue(prevValue, path);
|
|
1265
|
+
newValue = null;
|
|
1266
|
+
|
|
1267
|
+
store.removeShadowArrayElement(thisKey, path);
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
case "update": {
|
|
1272
|
+
oldValue = getNestedValue(prevValue, path);
|
|
1273
|
+
newValue = getNestedValue(payload, path);
|
|
1274
|
+
|
|
1275
|
+
const shadowPath = path.map((p, i) => {
|
|
1276
|
+
const currentSubPath = path.slice(0, i + 1);
|
|
1277
|
+
const subValue = getNestedValue(payload, currentSubPath);
|
|
1278
|
+
return subValue?.id ? `id:${subValue.id}` : p;
|
|
1279
|
+
});
|
|
1280
|
+
store.updateShadowAtPath(thisKey, shadowPath, newValue);
|
|
1292
1281
|
break;
|
|
1282
|
+
}
|
|
1293
1283
|
}
|
|
1294
1284
|
|
|
1295
1285
|
setStateLog(thisKey, (prevLogs) => {
|
|
1296
1286
|
const logs = [...(prevLogs ?? []), newUpdate];
|
|
1287
|
+
const aggregatedLogs = new Map<string, typeof newUpdate>();
|
|
1297
1288
|
|
|
1298
|
-
|
|
1299
|
-
const aggregatedLogs = logs.reduce((acc, log) => {
|
|
1289
|
+
logs.forEach((log) => {
|
|
1300
1290
|
const uniqueKey = `${log.stateKey}:${JSON.stringify(log.path)}`;
|
|
1301
|
-
const existing =
|
|
1291
|
+
const existing = aggregatedLogs.get(uniqueKey);
|
|
1302
1292
|
|
|
1303
1293
|
if (existing) {
|
|
1304
|
-
// Update the existing entry with the most recent details
|
|
1305
1294
|
existing.timeStamp = Math.max(existing.timeStamp, log.timeStamp);
|
|
1306
|
-
existing.newValue = log.newValue;
|
|
1307
|
-
existing.oldValue = existing.oldValue ?? log.oldValue;
|
|
1308
|
-
existing.updateType = log.updateType;
|
|
1295
|
+
existing.newValue = log.newValue;
|
|
1296
|
+
existing.oldValue = existing.oldValue ?? log.oldValue;
|
|
1297
|
+
existing.updateType = log.updateType;
|
|
1309
1298
|
} else {
|
|
1310
|
-
|
|
1311
|
-
acc.set(uniqueKey, { ...(log as any) });
|
|
1299
|
+
aggregatedLogs.set(uniqueKey, { ...(log as any) });
|
|
1312
1300
|
}
|
|
1301
|
+
});
|
|
1313
1302
|
|
|
1314
|
-
return acc;
|
|
1315
|
-
}, new Map<string, typeof newUpdate>());
|
|
1316
|
-
|
|
1317
|
-
// Convert the aggregated map back to an array
|
|
1318
1303
|
return Array.from(aggregatedLogs.values());
|
|
1319
1304
|
});
|
|
1320
1305
|
|
|
@@ -1367,7 +1352,6 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1367
1352
|
}
|
|
1368
1353
|
|
|
1369
1354
|
const updaterFinal = useMemo(() => {
|
|
1370
|
-
// Create proxy with baseObject as target
|
|
1371
1355
|
return createProxyHandler<TStateObject>(
|
|
1372
1356
|
thisKey,
|
|
1373
1357
|
effectiveSetState,
|
|
@@ -1388,7 +1372,6 @@ function createProxyHandler<T>(
|
|
|
1388
1372
|
componentId: string,
|
|
1389
1373
|
sessionId?: string
|
|
1390
1374
|
): StateObject<T> {
|
|
1391
|
-
// ADDED: Enhanced cache with versioning
|
|
1392
1375
|
type CacheEntry = {
|
|
1393
1376
|
proxy: any;
|
|
1394
1377
|
stateVersion: number;
|
|
@@ -1396,7 +1379,6 @@ function createProxyHandler<T>(
|
|
|
1396
1379
|
const shapeCache = new Map<string, CacheEntry>();
|
|
1397
1380
|
let stateVersion = 0;
|
|
1398
1381
|
|
|
1399
|
-
// ADDED: Cache invalidation helper
|
|
1400
1382
|
const invalidateCachePath = (path: string[]) => {
|
|
1401
1383
|
const pathKey = path.join(".");
|
|
1402
1384
|
for (const [key] of shapeCache) {
|
|
@@ -1428,9 +1410,8 @@ function createProxyHandler<T>(
|
|
|
1428
1410
|
|
|
1429
1411
|
const initialState =
|
|
1430
1412
|
getGlobalStore.getState().initialStateGlobal[stateKey];
|
|
1431
|
-
|
|
1413
|
+
getGlobalStore.getState().initializeShadowState(stateKey, initialState);
|
|
1432
1414
|
getGlobalStore.getState().clearSelectedIndexesForState(stateKey);
|
|
1433
|
-
// ADDED: Clear cache on revert
|
|
1434
1415
|
shapeCache.clear();
|
|
1435
1416
|
stateVersion++;
|
|
1436
1417
|
|
|
@@ -1460,7 +1441,6 @@ function createProxyHandler<T>(
|
|
|
1460
1441
|
return initialState;
|
|
1461
1442
|
},
|
|
1462
1443
|
updateInitialState: (newState: T) => {
|
|
1463
|
-
// ADDED: Clear cache on initial state update
|
|
1464
1444
|
shapeCache.clear();
|
|
1465
1445
|
stateVersion++;
|
|
1466
1446
|
|
|
@@ -1513,14 +1493,20 @@ function createProxyHandler<T>(
|
|
|
1513
1493
|
},
|
|
1514
1494
|
};
|
|
1515
1495
|
|
|
1496
|
+
function getOrderedIds(arrayPath: string[]): string[] | null {
|
|
1497
|
+
const arrayKey = [stateKey, ...arrayPath].join(".");
|
|
1498
|
+
const arrayMeta = getGlobalStore.getState().shadowStateStore.get(arrayKey);
|
|
1499
|
+
return arrayMeta?.arrayKeys || null;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1516
1502
|
function rebuildStateShape(
|
|
1517
1503
|
currentState: T,
|
|
1518
1504
|
path: string[] = [],
|
|
1519
|
-
meta?: {
|
|
1505
|
+
meta?: {
|
|
1506
|
+
validIds?: string[];
|
|
1507
|
+
}
|
|
1520
1508
|
): any {
|
|
1521
1509
|
const cacheKey = path.map(String).join(".");
|
|
1522
|
-
|
|
1523
|
-
// MODIFIED: Cache check with version
|
|
1524
1510
|
const cachedEntry = shapeCache.get(cacheKey);
|
|
1525
1511
|
|
|
1526
1512
|
type CallableStateObject<T> = {
|
|
@@ -1533,24 +1519,16 @@ function createProxyHandler<T>(
|
|
|
1533
1519
|
return getGlobalStore().getNestedState(stateKey, path);
|
|
1534
1520
|
} as unknown as CallableStateObject<T>;
|
|
1535
1521
|
|
|
1536
|
-
// Copy properties from baseObj to the function with type assertion
|
|
1537
1522
|
Object.keys(baseObj).forEach((key) => {
|
|
1538
1523
|
(baseFunction as any)[key] = (baseObj as any)[key];
|
|
1539
1524
|
});
|
|
1540
1525
|
|
|
1541
1526
|
const handler = {
|
|
1542
1527
|
apply(target: any, thisArg: any, args: any[]) {
|
|
1543
|
-
console.log(
|
|
1544
|
-
`PROXY APPLY TRAP HIT: stateKey=${stateKey}, path=${path.join(".")}`
|
|
1545
|
-
); // <--- ADD LOGGING
|
|
1546
|
-
console.trace("Apply trap stack trace");
|
|
1547
1528
|
return getGlobalStore().getNestedState(stateKey, path);
|
|
1548
1529
|
},
|
|
1549
1530
|
|
|
1550
1531
|
get(target: any, prop: string) {
|
|
1551
|
-
if (meta?.validIndices && !Array.isArray(currentState)) {
|
|
1552
|
-
meta = { ...meta, validIndices: undefined };
|
|
1553
|
-
}
|
|
1554
1532
|
const mutationMethods = new Set([
|
|
1555
1533
|
"insert",
|
|
1556
1534
|
"cut",
|
|
@@ -1582,51 +1560,40 @@ function createProxyHandler<T>(
|
|
|
1582
1560
|
!mutationMethods.has(prop)
|
|
1583
1561
|
) {
|
|
1584
1562
|
const fullComponentId = `${stateKey}////${componentId}`;
|
|
1585
|
-
// console.log("adding path", fullComponentId, path, prop);
|
|
1586
1563
|
const stateEntry = getGlobalStore
|
|
1587
1564
|
.getState()
|
|
1588
1565
|
.stateComponents.get(stateKey);
|
|
1589
1566
|
|
|
1590
1567
|
if (stateEntry) {
|
|
1591
1568
|
const component = stateEntry.components.get(fullComponentId);
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
(currentPath === existingPath ||
|
|
1604
|
-
currentPath[existingPath.length] === ".")
|
|
1605
|
-
) {
|
|
1606
|
-
needsAdd = false;
|
|
1607
|
-
break;
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
if (needsAdd) {
|
|
1612
|
-
component.paths.add(currentPath);
|
|
1569
|
+
if (component && !component.paths.has("")) {
|
|
1570
|
+
const currentPath = path.join(".");
|
|
1571
|
+
let needsAdd = true;
|
|
1572
|
+
for (const existingPath of component.paths) {
|
|
1573
|
+
if (
|
|
1574
|
+
currentPath.startsWith(existingPath) &&
|
|
1575
|
+
(currentPath === existingPath ||
|
|
1576
|
+
currentPath[existingPath.length] === ".")
|
|
1577
|
+
) {
|
|
1578
|
+
needsAdd = false;
|
|
1579
|
+
break;
|
|
1613
1580
|
}
|
|
1614
1581
|
}
|
|
1582
|
+
if (needsAdd) {
|
|
1583
|
+
component.paths.add(currentPath);
|
|
1584
|
+
}
|
|
1615
1585
|
}
|
|
1616
1586
|
}
|
|
1617
1587
|
}
|
|
1618
1588
|
if (prop === "getDifferences") {
|
|
1619
|
-
return () =>
|
|
1620
|
-
|
|
1589
|
+
return () =>
|
|
1590
|
+
getDifferences(
|
|
1621
1591
|
getGlobalStore.getState().cogsStateStore[stateKey],
|
|
1622
1592
|
getGlobalStore.getState().initialStateGlobal[stateKey]
|
|
1623
1593
|
);
|
|
1624
|
-
return differences;
|
|
1625
|
-
};
|
|
1626
1594
|
}
|
|
1627
1595
|
if (prop === "sync" && path.length === 0) {
|
|
1628
1596
|
return async function () {
|
|
1629
|
-
// Get the options for this state key
|
|
1630
1597
|
const options = getGlobalStore
|
|
1631
1598
|
.getState()
|
|
1632
1599
|
.getInitialOptions(stateKey);
|
|
@@ -1637,100 +1604,64 @@ function createProxyHandler<T>(
|
|
|
1637
1604
|
return { success: false, error: `No mutation defined` };
|
|
1638
1605
|
}
|
|
1639
1606
|
|
|
1640
|
-
// Get the root state
|
|
1641
1607
|
const state = getGlobalStore
|
|
1642
1608
|
.getState()
|
|
1643
1609
|
.getNestedState(stateKey, []);
|
|
1644
|
-
|
|
1645
|
-
// Get validation key
|
|
1646
1610
|
const validationKey = options?.validation?.key;
|
|
1647
1611
|
|
|
1648
1612
|
try {
|
|
1649
|
-
// Execute the mutation action
|
|
1650
1613
|
const response = await sync.action(state);
|
|
1651
|
-
|
|
1652
|
-
// Handle validation errors
|
|
1653
1614
|
if (
|
|
1654
1615
|
response &&
|
|
1655
1616
|
!response.success &&
|
|
1656
1617
|
response.errors &&
|
|
1657
1618
|
validationKey
|
|
1658
1619
|
) {
|
|
1659
|
-
// Clear existing errors
|
|
1660
1620
|
getGlobalStore.getState().removeValidationError(validationKey);
|
|
1661
|
-
|
|
1662
|
-
// Add new validation errors
|
|
1663
1621
|
response.errors.forEach((error) => {
|
|
1664
1622
|
const errorPath = [validationKey, ...error.path].join(".");
|
|
1665
|
-
|
|
1666
1623
|
getGlobalStore
|
|
1667
1624
|
.getState()
|
|
1668
1625
|
.addValidationError(errorPath, error.message);
|
|
1669
1626
|
});
|
|
1670
|
-
|
|
1671
|
-
// Notify components to update
|
|
1672
|
-
const stateEntry = getGlobalStore
|
|
1673
|
-
.getState()
|
|
1674
|
-
.stateComponents.get(stateKey);
|
|
1675
|
-
if (stateEntry) {
|
|
1676
|
-
stateEntry.components.forEach((component) => {
|
|
1677
|
-
component.forceUpdate();
|
|
1678
|
-
});
|
|
1679
|
-
}
|
|
1627
|
+
notifyComponents(stateKey);
|
|
1680
1628
|
}
|
|
1681
1629
|
|
|
1682
|
-
|
|
1683
|
-
if (response?.success && sync.onSuccess) {
|
|
1630
|
+
if (response?.success && sync.onSuccess)
|
|
1684
1631
|
sync.onSuccess(response.data);
|
|
1685
|
-
|
|
1632
|
+
else if (!response?.success && sync.onError)
|
|
1686
1633
|
sync.onError(response.error);
|
|
1687
|
-
}
|
|
1688
1634
|
|
|
1689
1635
|
return response;
|
|
1690
1636
|
} catch (error) {
|
|
1691
|
-
if (sync.onError)
|
|
1692
|
-
sync.onError(error);
|
|
1693
|
-
}
|
|
1637
|
+
if (sync.onError) sync.onError(error);
|
|
1694
1638
|
return { success: false, error };
|
|
1695
1639
|
}
|
|
1696
1640
|
};
|
|
1697
1641
|
}
|
|
1698
1642
|
if (prop === "_status") {
|
|
1699
|
-
// Get current state at this path (non-reactive version)
|
|
1700
1643
|
const thisReactiveState = getGlobalStore
|
|
1701
1644
|
.getState()
|
|
1702
1645
|
.getNestedState(stateKey, path);
|
|
1703
|
-
|
|
1704
|
-
// Get initial state at this path
|
|
1705
1646
|
const initialState =
|
|
1706
1647
|
getGlobalStore.getState().initialStateGlobal[stateKey];
|
|
1707
1648
|
const initialStateAtPath = getNestedValue(initialState, path);
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
return "fresh"; // Matches initial state
|
|
1712
|
-
} else {
|
|
1713
|
-
return "stale"; // Different from initial state
|
|
1714
|
-
}
|
|
1649
|
+
return isDeepEqual(thisReactiveState, initialStateAtPath)
|
|
1650
|
+
? "fresh"
|
|
1651
|
+
: "stale";
|
|
1715
1652
|
}
|
|
1716
1653
|
if (prop === "getStatus") {
|
|
1717
1654
|
return function () {
|
|
1718
|
-
// Get current state at this path (reactive version)
|
|
1719
1655
|
const thisReactiveState = getGlobalStore().getNestedState(
|
|
1720
1656
|
stateKey,
|
|
1721
1657
|
path
|
|
1722
1658
|
);
|
|
1723
|
-
|
|
1724
|
-
// Get initial state at this path
|
|
1725
1659
|
const initialState =
|
|
1726
1660
|
getGlobalStore.getState().initialStateGlobal[stateKey];
|
|
1727
1661
|
const initialStateAtPath = getNestedValue(initialState, path);
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
} else {
|
|
1732
|
-
return "stale"; // Different from initial state
|
|
1733
|
-
}
|
|
1662
|
+
return isDeepEqual(thisReactiveState, initialStateAtPath)
|
|
1663
|
+
? "fresh"
|
|
1664
|
+
: "stale";
|
|
1734
1665
|
};
|
|
1735
1666
|
}
|
|
1736
1667
|
if (prop === "removeStorage") {
|
|
@@ -1739,14 +1670,10 @@ function createProxyHandler<T>(
|
|
|
1739
1670
|
getGlobalStore.getState().initialStateGlobal[stateKey];
|
|
1740
1671
|
const initalOptionsGet = getInitialOptions(stateKey as string);
|
|
1741
1672
|
const localKey = isFunction(initalOptionsGet?.localStorage?.key)
|
|
1742
|
-
? initalOptionsGet
|
|
1673
|
+
? initalOptionsGet.localStorage.key(initialState)
|
|
1743
1674
|
: initalOptionsGet?.localStorage?.key;
|
|
1744
|
-
|
|
1745
1675
|
const storageKey = `${sessionId}-${stateKey}-${localKey}`;
|
|
1746
|
-
|
|
1747
|
-
if (storageKey) {
|
|
1748
|
-
localStorage.removeItem(storageKey);
|
|
1749
|
-
}
|
|
1676
|
+
if (storageKey) localStorage.removeItem(storageKey);
|
|
1750
1677
|
};
|
|
1751
1678
|
}
|
|
1752
1679
|
if (prop === "showValidationErrors") {
|
|
@@ -1754,49 +1681,30 @@ function createProxyHandler<T>(
|
|
|
1754
1681
|
const init = getGlobalStore
|
|
1755
1682
|
.getState()
|
|
1756
1683
|
.getInitialOptions(stateKey)?.validation;
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
throw new Error("Validation key not found");
|
|
1760
|
-
}
|
|
1761
|
-
const errors = getGlobalStore
|
|
1684
|
+
if (!init?.key) throw new Error("Validation key not found");
|
|
1685
|
+
return getGlobalStore
|
|
1762
1686
|
.getState()
|
|
1763
1687
|
.getValidationErrors(init.key + "." + path.join("."));
|
|
1764
|
-
|
|
1765
|
-
return errors;
|
|
1766
1688
|
};
|
|
1767
1689
|
}
|
|
1768
1690
|
if (Array.isArray(currentState)) {
|
|
1769
|
-
const getSourceArrayAndIndices = (): {
|
|
1770
|
-
item: any;
|
|
1771
|
-
originalIndex: number;
|
|
1772
|
-
}[] => {
|
|
1773
|
-
// If meta exists, we're in a chain. Use the currentState and map it to its original index.
|
|
1774
|
-
if (meta?.validIndices) {
|
|
1775
|
-
return (currentState as any[]).map((item, index) => ({
|
|
1776
|
-
item,
|
|
1777
|
-
originalIndex: meta!.validIndices![index]!,
|
|
1778
|
-
}));
|
|
1779
|
-
}
|
|
1780
|
-
// Otherwise, this is the first operation. Use the full array from the global store.
|
|
1781
|
-
const sourceArray = getGlobalStore
|
|
1782
|
-
.getState()
|
|
1783
|
-
.getNestedState(stateKey, path) as any[];
|
|
1784
|
-
return sourceArray.map((item, index) => ({
|
|
1785
|
-
item,
|
|
1786
|
-
originalIndex: index,
|
|
1787
|
-
}));
|
|
1788
|
-
};
|
|
1789
1691
|
if (prop === "getSelected") {
|
|
1790
1692
|
return () => {
|
|
1791
1693
|
const selectedIndex = getGlobalStore
|
|
1792
1694
|
.getState()
|
|
1793
1695
|
.getSelectedIndex(stateKey, path.join("."));
|
|
1794
1696
|
if (selectedIndex === undefined) return undefined;
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
)
|
|
1697
|
+
|
|
1698
|
+
const sourceArray = getGlobalStore
|
|
1699
|
+
.getState()
|
|
1700
|
+
.getNestedState(stateKey, path) as any[];
|
|
1701
|
+
if (!sourceArray || selectedIndex >= sourceArray.length)
|
|
1702
|
+
return undefined;
|
|
1703
|
+
|
|
1704
|
+
const selectedItem = sourceArray[selectedIndex];
|
|
1705
|
+
const itemId = `id:${selectedItem.id}`;
|
|
1706
|
+
|
|
1707
|
+
return rebuildStateShape(selectedItem, [...path, itemId], meta);
|
|
1800
1708
|
};
|
|
1801
1709
|
}
|
|
1802
1710
|
if (prop === "clearSelected") {
|
|
@@ -1806,14 +1714,23 @@ function createProxyHandler<T>(
|
|
|
1806
1714
|
}
|
|
1807
1715
|
if (prop === "getSelectedIndex") {
|
|
1808
1716
|
return () => {
|
|
1809
|
-
const
|
|
1717
|
+
const globallySelectedIndex = getGlobalStore
|
|
1810
1718
|
.getState()
|
|
1811
1719
|
.getSelectedIndex(stateKey, path.join("."));
|
|
1720
|
+
if (globallySelectedIndex === undefined) return -1;
|
|
1721
|
+
|
|
1722
|
+
if (meta?.validIds) {
|
|
1723
|
+
const sourceIds = getOrderedIds(path) || [];
|
|
1724
|
+
const selectedItemId = sourceIds[globallySelectedIndex];
|
|
1725
|
+
if (!selectedItemId) return -1;
|
|
1726
|
+
const localIndex = meta.validIds.indexOf(selectedItemId);
|
|
1727
|
+
return localIndex;
|
|
1728
|
+
}
|
|
1812
1729
|
|
|
1813
|
-
return
|
|
1730
|
+
return globallySelectedIndex;
|
|
1814
1731
|
};
|
|
1815
1732
|
}
|
|
1816
|
-
//
|
|
1733
|
+
// Replace the entire 'if (prop === "useVirtualView")' block with this
|
|
1817
1734
|
if (prop === "useVirtualView") {
|
|
1818
1735
|
return (
|
|
1819
1736
|
options: VirtualViewOptions
|
|
@@ -1835,7 +1752,9 @@ function createProxyHandler<T>(
|
|
|
1835
1752
|
const userHasScrolledAwayRef = useRef(false);
|
|
1836
1753
|
const previousCountRef = useRef(0);
|
|
1837
1754
|
const lastRangeRef = useRef(range);
|
|
1838
|
-
|
|
1755
|
+
const orderedIds = getOrderedIds(path);
|
|
1756
|
+
|
|
1757
|
+
// Subscribe to shadow state updates for dynamic height changes
|
|
1839
1758
|
useEffect(() => {
|
|
1840
1759
|
const unsubscribe = getGlobalStore
|
|
1841
1760
|
.getState()
|
|
@@ -1851,21 +1770,23 @@ function createProxyHandler<T>(
|
|
|
1851
1770
|
) as any[];
|
|
1852
1771
|
const totalCount = sourceArray.length;
|
|
1853
1772
|
|
|
1854
|
-
// Calculate
|
|
1773
|
+
// Calculate total height and individual item positions
|
|
1855
1774
|
const { totalHeight, positions } = useMemo(() => {
|
|
1856
|
-
const rawShadowData = getGlobalStore
|
|
1857
|
-
.getState()
|
|
1858
|
-
.getShadowMetadata(stateKey, path);
|
|
1859
|
-
const shadowArray: ItemMeta[] = Array.isArray(rawShadowData)
|
|
1860
|
-
? rawShadowData
|
|
1861
|
-
: [];
|
|
1862
1775
|
let height = 0;
|
|
1863
1776
|
const pos: number[] = [];
|
|
1864
1777
|
for (let i = 0; i < totalCount; i++) {
|
|
1865
1778
|
pos[i] = height;
|
|
1866
|
-
const
|
|
1867
|
-
|
|
1868
|
-
|
|
1779
|
+
const itemId = orderedIds?.[i];
|
|
1780
|
+
if (itemId) {
|
|
1781
|
+
const itemPath = [...path, itemId];
|
|
1782
|
+
const itemMeta = getGlobalStore
|
|
1783
|
+
.getState()
|
|
1784
|
+
.getShadowMetadata(stateKey, itemPath);
|
|
1785
|
+
const measuredHeight = itemMeta?.virtualizer?.itemHeight;
|
|
1786
|
+
height += measuredHeight || itemHeight;
|
|
1787
|
+
} else {
|
|
1788
|
+
height += itemHeight;
|
|
1789
|
+
}
|
|
1869
1790
|
}
|
|
1870
1791
|
return { totalHeight: height, positions: pos };
|
|
1871
1792
|
}, [
|
|
@@ -1874,87 +1795,65 @@ function createProxyHandler<T>(
|
|
|
1874
1795
|
path.join("."),
|
|
1875
1796
|
itemHeight,
|
|
1876
1797
|
shadowUpdateTrigger,
|
|
1798
|
+
orderedIds,
|
|
1877
1799
|
]);
|
|
1878
1800
|
|
|
1879
|
-
// Create virtual state
|
|
1801
|
+
// Create the virtual state object
|
|
1880
1802
|
const virtualState = useMemo(() => {
|
|
1881
1803
|
const start = Math.max(0, range.startIndex);
|
|
1882
1804
|
const end = Math.min(totalCount, range.endIndex);
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
);
|
|
1887
|
-
|
|
1805
|
+
// The sliced array is the `currentState` for the new proxy
|
|
1806
|
+
const slicedArray = sourceArray.slice(start, end);
|
|
1807
|
+
// The `validIds` for the new proxy are the sliced IDs
|
|
1808
|
+
const slicedIds = orderedIds?.slice(start, end);
|
|
1809
|
+
|
|
1888
1810
|
return rebuildStateShape(slicedArray as any, path, {
|
|
1889
1811
|
...meta,
|
|
1890
|
-
|
|
1812
|
+
validIds: slicedIds,
|
|
1891
1813
|
});
|
|
1892
|
-
}, [
|
|
1814
|
+
}, [
|
|
1815
|
+
range.startIndex,
|
|
1816
|
+
range.endIndex,
|
|
1817
|
+
sourceArray,
|
|
1818
|
+
totalCount,
|
|
1819
|
+
orderedIds,
|
|
1820
|
+
]);
|
|
1893
1821
|
|
|
1894
|
-
// Helper to scroll to last item using stored ref
|
|
1895
1822
|
const scrollToLastItem = useCallback(() => {
|
|
1896
|
-
const rawShadowData = getGlobalStore
|
|
1897
|
-
.getState()
|
|
1898
|
-
.getShadowMetadata(stateKey, path);
|
|
1899
|
-
const shadowArray: ItemMeta[] = Array.isArray(rawShadowData)
|
|
1900
|
-
? rawShadowData
|
|
1901
|
-
: [];
|
|
1902
1823
|
const lastIndex = totalCount - 1;
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
const
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1824
|
+
if (lastIndex >= 0 && orderedIds?.[lastIndex]) {
|
|
1825
|
+
const lastItemId = orderedIds[lastIndex];
|
|
1826
|
+
const lastItemPath = [...path, lastItemId];
|
|
1827
|
+
const lastItemMeta = getGlobalStore
|
|
1828
|
+
.getState()
|
|
1829
|
+
.getShadowMetadata(stateKey, lastItemPath);
|
|
1830
|
+
if (lastItemMeta?.virtualizer?.domRef) {
|
|
1831
|
+
const element = lastItemMeta.virtualizer.domRef;
|
|
1832
|
+
if (element?.scrollIntoView) {
|
|
1909
1833
|
element.scrollIntoView({
|
|
1910
1834
|
behavior: "auto",
|
|
1911
1835
|
block: "end",
|
|
1912
|
-
inline: "nearest",
|
|
1913
1836
|
});
|
|
1914
1837
|
return true;
|
|
1915
1838
|
}
|
|
1916
1839
|
}
|
|
1917
1840
|
}
|
|
1918
1841
|
return false;
|
|
1919
|
-
}, [stateKey, path, totalCount]);
|
|
1842
|
+
}, [stateKey, path, totalCount, orderedIds]);
|
|
1920
1843
|
|
|
1921
|
-
// Handle new items when at bottom
|
|
1922
1844
|
useEffect(() => {
|
|
1923
1845
|
if (!stickToBottom || totalCount === 0) return;
|
|
1924
|
-
|
|
1925
1846
|
const hasNewItems = totalCount > previousCountRef.current;
|
|
1926
|
-
const isInitialLoad =
|
|
1927
|
-
previousCountRef.current === 0 && totalCount > 0;
|
|
1928
|
-
|
|
1929
|
-
// Only auto-scroll if user hasn't scrolled away
|
|
1930
1847
|
if (
|
|
1931
|
-
|
|
1848
|
+
hasNewItems &&
|
|
1932
1849
|
wasAtBottomRef.current &&
|
|
1933
1850
|
!userHasScrolledAwayRef.current
|
|
1934
1851
|
) {
|
|
1935
|
-
|
|
1936
|
-
(containerRef.current?.clientHeight || 0) / itemHeight
|
|
1937
|
-
);
|
|
1938
|
-
const newRange = {
|
|
1939
|
-
startIndex: Math.max(
|
|
1940
|
-
0,
|
|
1941
|
-
totalCount - visibleCount - overscan
|
|
1942
|
-
),
|
|
1943
|
-
endIndex: totalCount,
|
|
1944
|
-
};
|
|
1945
|
-
|
|
1946
|
-
setRange(newRange);
|
|
1947
|
-
|
|
1948
|
-
const timeoutId = setTimeout(() => {
|
|
1949
|
-
scrollToIndex(totalCount - 1, "smooth");
|
|
1950
|
-
}, 50);
|
|
1951
|
-
return () => clearTimeout(timeoutId);
|
|
1852
|
+
setTimeout(() => scrollToIndex(totalCount - 1, "smooth"), 50);
|
|
1952
1853
|
}
|
|
1953
|
-
|
|
1954
1854
|
previousCountRef.current = totalCount;
|
|
1955
|
-
}, [totalCount,
|
|
1855
|
+
}, [totalCount, stickToBottom]);
|
|
1956
1856
|
|
|
1957
|
-
// Handle scroll events
|
|
1958
1857
|
useEffect(() => {
|
|
1959
1858
|
const container = containerRef.current;
|
|
1960
1859
|
if (!container) return;
|
|
@@ -1963,21 +1862,12 @@ function createProxyHandler<T>(
|
|
|
1963
1862
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
1964
1863
|
const distanceFromBottom =
|
|
1965
1864
|
scrollHeight - scrollTop - clientHeight;
|
|
1966
|
-
|
|
1967
|
-
// Track if we're at bottom
|
|
1968
1865
|
wasAtBottomRef.current = distanceFromBottom < 5;
|
|
1969
|
-
|
|
1970
|
-
// If user scrolls away from bottom past threshold, set flag
|
|
1971
|
-
if (distanceFromBottom > 100) {
|
|
1866
|
+
if (distanceFromBottom > 100)
|
|
1972
1867
|
userHasScrolledAwayRef.current = true;
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
// If user scrolls back to bottom, cle
|
|
1976
|
-
if (distanceFromBottom < 5) {
|
|
1868
|
+
if (distanceFromBottom < 5)
|
|
1977
1869
|
userHasScrolledAwayRef.current = false;
|
|
1978
|
-
}
|
|
1979
1870
|
|
|
1980
|
-
// Update visible range based on scroll position
|
|
1981
1871
|
let startIndex = 0;
|
|
1982
1872
|
for (let i = 0; i < positions.length; i++) {
|
|
1983
1873
|
if (positions[i]! > scrollTop - itemHeight * overscan) {
|
|
@@ -1985,7 +1875,6 @@ function createProxyHandler<T>(
|
|
|
1985
1875
|
break;
|
|
1986
1876
|
}
|
|
1987
1877
|
}
|
|
1988
|
-
|
|
1989
1878
|
let endIndex = startIndex;
|
|
1990
1879
|
const viewportEnd = scrollTop + clientHeight;
|
|
1991
1880
|
for (let i = startIndex; i < positions.length; i++) {
|
|
@@ -1994,14 +1883,12 @@ function createProxyHandler<T>(
|
|
|
1994
1883
|
}
|
|
1995
1884
|
endIndex = i;
|
|
1996
1885
|
}
|
|
1997
|
-
|
|
1998
1886
|
const newStartIndex = Math.max(0, startIndex);
|
|
1999
1887
|
const newEndIndex = Math.min(
|
|
2000
1888
|
totalCount,
|
|
2001
1889
|
endIndex + 1 + overscan
|
|
2002
1890
|
);
|
|
2003
1891
|
|
|
2004
|
-
// THE FIX: Only update state if the visible range of items has changed.
|
|
2005
1892
|
if (
|
|
2006
1893
|
newStartIndex !== lastRangeRef.current.startIndex ||
|
|
2007
1894
|
newEndIndex !== lastRangeRef.current.endIndex
|
|
@@ -2020,81 +1907,30 @@ function createProxyHandler<T>(
|
|
|
2020
1907
|
container.addEventListener("scroll", handleScroll, {
|
|
2021
1908
|
passive: true,
|
|
2022
1909
|
});
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
if (
|
|
2026
|
-
stickToBottom &&
|
|
2027
|
-
totalCount > 0 &&
|
|
2028
|
-
!userHasScrolledAwayRef.current
|
|
2029
|
-
) {
|
|
2030
|
-
const { scrollTop } = container;
|
|
2031
|
-
// Only if we're at the very top (initial load)
|
|
2032
|
-
if (scrollTop === 0) {
|
|
2033
|
-
container.scrollTop = container.scrollHeight;
|
|
2034
|
-
wasAtBottomRef.current = true;
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
handleScroll();
|
|
2039
|
-
|
|
2040
|
-
return () => {
|
|
1910
|
+
handleScroll(); // Initial check
|
|
1911
|
+
return () =>
|
|
2041
1912
|
container.removeEventListener("scroll", handleScroll);
|
|
2042
|
-
};
|
|
2043
1913
|
}, [positions, totalCount, itemHeight, overscan, stickToBottom]);
|
|
2044
1914
|
|
|
2045
1915
|
const scrollToBottom = useCallback(() => {
|
|
2046
1916
|
wasAtBottomRef.current = true;
|
|
2047
1917
|
userHasScrolledAwayRef.current = false;
|
|
2048
|
-
|
|
2049
|
-
if (!scrolled && containerRef.current) {
|
|
1918
|
+
if (!scrollToLastItem() && containerRef.current) {
|
|
2050
1919
|
containerRef.current.scrollTop =
|
|
2051
1920
|
containerRef.current.scrollHeight;
|
|
2052
1921
|
}
|
|
2053
1922
|
}, [scrollToLastItem]);
|
|
1923
|
+
|
|
2054
1924
|
const scrollToIndex = useCallback(
|
|
2055
1925
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
2056
1926
|
const container = containerRef.current;
|
|
2057
1927
|
if (!container) return;
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
// --- Special Case: The Last Item ---
|
|
2062
|
-
if (isLastItem) {
|
|
2063
|
-
// For the last item, scrollIntoView can fail. The most reliable method
|
|
2064
|
-
// is to scroll the parent container to its maximum scroll height.
|
|
2065
|
-
container.scrollTo({
|
|
2066
|
-
top: container.scrollHeight,
|
|
2067
|
-
behavior: behavior,
|
|
2068
|
-
});
|
|
2069
|
-
return; // We're done.
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
|
-
// --- Standard Case: All Other Items ---
|
|
2073
|
-
// For all other items, we find the ref and use scrollIntoView.
|
|
2074
|
-
const rawShadowData = getGlobalStore
|
|
2075
|
-
.getState()
|
|
2076
|
-
.getShadowMetadata(stateKey, path);
|
|
2077
|
-
const shadowArray: ItemMeta[] = Array.isArray(rawShadowData)
|
|
2078
|
-
? rawShadowData
|
|
2079
|
-
: [];
|
|
2080
|
-
const itemData = shadowArray[index];
|
|
2081
|
-
const element = itemData?.virtualizer?.domRef;
|
|
2082
|
-
|
|
2083
|
-
if (element) {
|
|
2084
|
-
// 'center' gives a better user experience for items in the middle of the list.
|
|
2085
|
-
element.scrollIntoView({
|
|
2086
|
-
behavior: behavior,
|
|
2087
|
-
block: "center",
|
|
2088
|
-
});
|
|
2089
|
-
} else if (positions[index] !== undefined) {
|
|
2090
|
-
// Fallback if the ref isn't available for some reason.
|
|
2091
|
-
container.scrollTo({
|
|
2092
|
-
top: positions[index],
|
|
2093
|
-
behavior,
|
|
2094
|
-
});
|
|
1928
|
+
const top = positions[index];
|
|
1929
|
+
if (top !== undefined) {
|
|
1930
|
+
container.scrollTo({ top, behavior });
|
|
2095
1931
|
}
|
|
2096
1932
|
},
|
|
2097
|
-
[positions
|
|
1933
|
+
[positions]
|
|
2098
1934
|
);
|
|
2099
1935
|
|
|
2100
1936
|
const virtualizerProps = {
|
|
@@ -2123,106 +1959,112 @@ function createProxyHandler<T>(
|
|
|
2123
1959
|
};
|
|
2124
1960
|
};
|
|
2125
1961
|
}
|
|
2126
|
-
if (prop === "
|
|
1962
|
+
if (prop === "stateMap") {
|
|
2127
1963
|
return (
|
|
2128
1964
|
callbackfn: (
|
|
2129
|
-
value:
|
|
2130
|
-
setter:
|
|
1965
|
+
value: any,
|
|
1966
|
+
setter: any,
|
|
2131
1967
|
index: number,
|
|
2132
|
-
array:
|
|
2133
|
-
arraySetter:
|
|
2134
|
-
) =>
|
|
1968
|
+
array: any,
|
|
1969
|
+
arraySetter: any
|
|
1970
|
+
) => void
|
|
2135
1971
|
) => {
|
|
2136
1972
|
const arrayToMap = currentState as any[];
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
if (
|
|
2141
|
-
meta?.validIndices &&
|
|
2142
|
-
meta.validIndices[index] !== undefined
|
|
2143
|
-
) {
|
|
2144
|
-
originalIndex = meta.validIndices[index]!;
|
|
2145
|
-
} else {
|
|
2146
|
-
originalIndex = index;
|
|
2147
|
-
}
|
|
2148
|
-
const finalPath = [...path, originalIndex.toString()];
|
|
1973
|
+
const itemIdsForCurrentArray =
|
|
1974
|
+
meta?.validIds || getOrderedIds(path) || [];
|
|
1975
|
+
const arraySetter = rebuildStateShape(currentState, path, meta);
|
|
2149
1976
|
|
|
2150
|
-
|
|
1977
|
+
return arrayToMap.map((item, index) => {
|
|
1978
|
+
const itemId = itemIdsForCurrentArray[index] || `id:${item.id}`;
|
|
1979
|
+
const itemPath = [...path, itemId];
|
|
1980
|
+
const itemSetter = rebuildStateShape(item, itemPath, meta);
|
|
2151
1981
|
return callbackfn(
|
|
2152
1982
|
item,
|
|
2153
|
-
|
|
1983
|
+
itemSetter,
|
|
2154
1984
|
index,
|
|
2155
|
-
currentState
|
|
2156
|
-
|
|
1985
|
+
currentState,
|
|
1986
|
+
arraySetter
|
|
2157
1987
|
);
|
|
2158
1988
|
});
|
|
2159
1989
|
};
|
|
2160
1990
|
}
|
|
2161
|
-
if (prop === "
|
|
1991
|
+
if (prop === "stateMapNoRender") {
|
|
2162
1992
|
return (
|
|
2163
1993
|
callbackfn: (
|
|
2164
|
-
value:
|
|
2165
|
-
setter:
|
|
1994
|
+
value: any,
|
|
1995
|
+
setter: any,
|
|
2166
1996
|
index: number,
|
|
2167
|
-
array:
|
|
2168
|
-
arraySetter:
|
|
1997
|
+
array: any,
|
|
1998
|
+
arraySetter: any
|
|
2169
1999
|
) => void
|
|
2170
2000
|
) => {
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
_mapFn: callbackfn as any, // Pass the actual function, not string
|
|
2176
|
-
},
|
|
2001
|
+
const arrayToMap = currentState as any[];
|
|
2002
|
+
const itemIdsForCurrentArray =
|
|
2003
|
+
meta?.validIds || getOrderedIds(path) || [];
|
|
2004
|
+
const arraySetter = rebuildStateShape(currentState, path, meta);
|
|
2177
2005
|
|
|
2178
|
-
|
|
2006
|
+
return arrayToMap.map((item, index) => {
|
|
2007
|
+
const itemId = itemIdsForCurrentArray[index] || `id:${item.id}`;
|
|
2008
|
+
const finalPath = [...path, itemId];
|
|
2009
|
+
const setter = rebuildStateShape(item, finalPath, meta);
|
|
2010
|
+
return callbackfn(
|
|
2011
|
+
item,
|
|
2012
|
+
setter,
|
|
2013
|
+
index,
|
|
2014
|
+
currentState,
|
|
2015
|
+
arraySetter
|
|
2016
|
+
);
|
|
2179
2017
|
});
|
|
2180
2018
|
};
|
|
2181
2019
|
}
|
|
2020
|
+
if (prop === "$stateMap") {
|
|
2021
|
+
return (callbackfn: any) =>
|
|
2022
|
+
createElement(SignalMapRenderer, {
|
|
2023
|
+
proxy: { _stateKey: stateKey, _path: path, _mapFn: callbackfn },
|
|
2024
|
+
rebuildStateShape,
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2182
2027
|
if (prop === "stateList") {
|
|
2183
2028
|
return (
|
|
2184
2029
|
callbackfn: (
|
|
2185
|
-
value:
|
|
2186
|
-
setter:
|
|
2030
|
+
value: any,
|
|
2031
|
+
setter: any,
|
|
2187
2032
|
index: { localIndex: number; originalIndex: number },
|
|
2188
|
-
array:
|
|
2189
|
-
arraySetter:
|
|
2190
|
-
) =>
|
|
2191
|
-
formOpts?: FormOptsType
|
|
2033
|
+
array: any,
|
|
2034
|
+
arraySetter: any
|
|
2035
|
+
) => ReactNode
|
|
2192
2036
|
) => {
|
|
2193
|
-
const arrayToMap =
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
const indicesToMap =
|
|
2205
|
-
meta?.validIndices ||
|
|
2206
|
-
Array.from({ length: arrayToMap.length }, (_, i) => i);
|
|
2037
|
+
const arrayToMap = currentState as any[];
|
|
2038
|
+
if (!Array.isArray(arrayToMap)) return null;
|
|
2039
|
+
|
|
2040
|
+
const itemIdsForCurrentArray =
|
|
2041
|
+
meta?.validIds || getOrderedIds(path) || [];
|
|
2042
|
+
const sourceIds = getOrderedIds(path) || [];
|
|
2043
|
+
const arraySetter = rebuildStateShape(
|
|
2044
|
+
arrayToMap as any,
|
|
2045
|
+
path,
|
|
2046
|
+
meta
|
|
2047
|
+
);
|
|
2207
2048
|
|
|
2208
|
-
return
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2049
|
+
return arrayToMap.map((item, localIndex) => {
|
|
2050
|
+
const itemId =
|
|
2051
|
+
itemIdsForCurrentArray[localIndex] || `id:${item.id}`;
|
|
2052
|
+
const originalIndex = sourceIds.indexOf(itemId);
|
|
2053
|
+
const finalPath = [...path, itemId];
|
|
2211
2054
|
const setter = rebuildStateShape(item, finalPath, meta);
|
|
2212
|
-
const itemComponentId = `${componentId}-${path.join(".")}-${
|
|
2055
|
+
const itemComponentId = `${componentId}-${path.join(".")}-${itemId}`;
|
|
2213
2056
|
|
|
2214
2057
|
return createElement(CogsItemWrapper, {
|
|
2215
|
-
key:
|
|
2058
|
+
key: itemId,
|
|
2216
2059
|
stateKey,
|
|
2217
2060
|
itemComponentId,
|
|
2218
|
-
formOpts,
|
|
2219
2061
|
itemPath: finalPath,
|
|
2220
2062
|
children: callbackfn(
|
|
2221
2063
|
item,
|
|
2222
2064
|
setter,
|
|
2223
2065
|
{ localIndex, originalIndex },
|
|
2224
2066
|
arrayToMap as any,
|
|
2225
|
-
|
|
2067
|
+
arraySetter
|
|
2226
2068
|
),
|
|
2227
2069
|
});
|
|
2228
2070
|
});
|
|
@@ -2243,15 +2085,30 @@ function createProxyHandler<T>(
|
|
|
2243
2085
|
);
|
|
2244
2086
|
};
|
|
2245
2087
|
}
|
|
2246
|
-
|
|
2247
2088
|
if (prop === "index") {
|
|
2248
2089
|
return (index: number) => {
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2090
|
+
const idList = meta?.validIds || getOrderedIds(path);
|
|
2091
|
+
const itemId = idList?.[index];
|
|
2092
|
+
|
|
2093
|
+
if (!itemId) {
|
|
2094
|
+
return rebuildStateShape(undefined as T, [
|
|
2095
|
+
...path,
|
|
2096
|
+
index.toString(),
|
|
2097
|
+
]);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
const sourceArray = getGlobalStore
|
|
2101
|
+
.getState()
|
|
2102
|
+
.getNestedState(stateKey, path) as any[];
|
|
2103
|
+
const itemData = sourceArray.find(
|
|
2104
|
+
(item) => `id:${item.id}` === itemId
|
|
2105
|
+
);
|
|
2106
|
+
|
|
2107
|
+
const itemPath = [...path, itemId];
|
|
2108
|
+
return rebuildStateShape(itemData, itemPath, meta);
|
|
2251
2109
|
};
|
|
2252
2110
|
}
|
|
2253
2111
|
if (prop === "last") {
|
|
2254
|
-
// Added handler for 'last'
|
|
2255
2112
|
return () => {
|
|
2256
2113
|
const currentArray = getGlobalStore
|
|
2257
2114
|
.getState()
|
|
@@ -2260,14 +2117,11 @@ function createProxyHandler<T>(
|
|
|
2260
2117
|
const lastIndex = currentArray.length - 1;
|
|
2261
2118
|
const lastValue = currentArray[lastIndex];
|
|
2262
2119
|
const newPath = [...path, lastIndex.toString()];
|
|
2263
|
-
// shapeCache.clear(); // Decide if you need cache invalidation for 'last' access
|
|
2264
|
-
// stateVersion++;
|
|
2265
2120
|
return rebuildStateShape(lastValue, newPath);
|
|
2266
2121
|
};
|
|
2267
2122
|
}
|
|
2268
2123
|
if (prop === "insert") {
|
|
2269
2124
|
return (payload: UpdateArg<T>) => {
|
|
2270
|
-
// ADDED: Invalidate cache on insert
|
|
2271
2125
|
invalidateCachePath(path);
|
|
2272
2126
|
pushFunc(effectiveSetState, payload, path, stateKey);
|
|
2273
2127
|
return rebuildStateShape(
|
|
@@ -2276,7 +2130,6 @@ function createProxyHandler<T>(
|
|
|
2276
2130
|
);
|
|
2277
2131
|
};
|
|
2278
2132
|
}
|
|
2279
|
-
|
|
2280
2133
|
if (prop === "uniqueInsert") {
|
|
2281
2134
|
return (
|
|
2282
2135
|
payload: UpdateArg<T>,
|
|
@@ -2292,19 +2145,12 @@ function createProxyHandler<T>(
|
|
|
2292
2145
|
|
|
2293
2146
|
let matchedItem: any = null;
|
|
2294
2147
|
const isUnique = !currentArray.some((item) => {
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
}
|
|
2302
|
-
return isMatch;
|
|
2303
|
-
}
|
|
2304
|
-
const isMatch = isDeepEqual(item, newValue);
|
|
2305
|
-
if (isMatch) {
|
|
2306
|
-
matchedItem = item;
|
|
2307
|
-
}
|
|
2148
|
+
const isMatch = fields
|
|
2149
|
+
? fields.every((field) =>
|
|
2150
|
+
isDeepEqual(item[field], newValue[field])
|
|
2151
|
+
)
|
|
2152
|
+
: isDeepEqual(item, newValue);
|
|
2153
|
+
if (isMatch) matchedItem = item;
|
|
2308
2154
|
return isMatch;
|
|
2309
2155
|
});
|
|
2310
2156
|
|
|
@@ -2321,11 +2167,9 @@ function createProxyHandler<T>(
|
|
|
2321
2167
|
}
|
|
2322
2168
|
};
|
|
2323
2169
|
}
|
|
2324
|
-
|
|
2325
2170
|
if (prop === "cut") {
|
|
2326
2171
|
return (index: number, options?: { waitForSync?: boolean }) => {
|
|
2327
2172
|
if (options?.waitForSync) return;
|
|
2328
|
-
// ADDED: Invalidate cache on cut
|
|
2329
2173
|
invalidateCachePath(path);
|
|
2330
2174
|
cutFunc(effectiveSetState, path, stateKey, index);
|
|
2331
2175
|
return rebuildStateShape(
|
|
@@ -2336,51 +2180,70 @@ function createProxyHandler<T>(
|
|
|
2336
2180
|
}
|
|
2337
2181
|
if (prop === "cutByValue") {
|
|
2338
2182
|
return (value: string | number | boolean) => {
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
cutFunc(effectiveSetState, path, stateKey, index);
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2183
|
+
const index = currentState.findIndex((item) => item === value);
|
|
2184
|
+
if (index > -1) cutFunc(effectiveSetState, path, stateKey, index);
|
|
2344
2185
|
};
|
|
2345
2186
|
}
|
|
2346
2187
|
if (prop === "toggleByValue") {
|
|
2347
2188
|
return (value: string | number | boolean) => {
|
|
2348
2189
|
const index = currentState.findIndex((item) => item === value);
|
|
2349
2190
|
if (index > -1) {
|
|
2350
|
-
// Value exists, so cut it
|
|
2351
2191
|
cutFunc(effectiveSetState, path, stateKey, index);
|
|
2352
2192
|
} else {
|
|
2353
|
-
// Value doesn't exist, so insert it
|
|
2354
2193
|
pushFunc(effectiveSetState, value as any, path, stateKey);
|
|
2355
2194
|
}
|
|
2356
2195
|
};
|
|
2357
2196
|
}
|
|
2358
|
-
if (prop === "
|
|
2359
|
-
return (
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
const found = sourceWithIndices.find(({ item }, index) =>
|
|
2367
|
-
callbackfn(item, index)
|
|
2197
|
+
if (prop === "stateFilter") {
|
|
2198
|
+
return (callbackfn: (value: any, index: number) => boolean) => {
|
|
2199
|
+
const sourceIds = meta?.validIds || getOrderedIds(path) || [];
|
|
2200
|
+
const sourceArray = getGlobalStore
|
|
2201
|
+
.getState()
|
|
2202
|
+
.getNestedState(stateKey, path) as any[];
|
|
2203
|
+
const sourceMap = new Map(
|
|
2204
|
+
sourceArray.map((item) => [`id:${item.id}`, item])
|
|
2368
2205
|
);
|
|
2369
|
-
|
|
2370
|
-
const
|
|
2371
|
-
|
|
2206
|
+
|
|
2207
|
+
const newValidIds: string[] = [];
|
|
2208
|
+
const newFilteredArray: any[] = [];
|
|
2209
|
+
|
|
2210
|
+
sourceIds.forEach((id, index) => {
|
|
2211
|
+
const item = sourceMap.get(id);
|
|
2212
|
+
if (item && callbackfn(item, index)) {
|
|
2213
|
+
newValidIds.push(id);
|
|
2214
|
+
newFilteredArray.push(item);
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
|
|
2218
|
+
return rebuildStateShape(newFilteredArray as any, path, {
|
|
2219
|
+
validIds: newValidIds,
|
|
2220
|
+
});
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
if (prop === "stateSort") {
|
|
2224
|
+
return (compareFn: (a: any, b: any) => number) => {
|
|
2225
|
+
const sourceArray = currentState as any[];
|
|
2226
|
+
const itemsWithIds = sourceArray.map((item) => ({
|
|
2227
|
+
item,
|
|
2228
|
+
id: `id:${item.id}`,
|
|
2229
|
+
}));
|
|
2230
|
+
itemsWithIds.sort((a, b) => compareFn(a.item, b.item));
|
|
2231
|
+
const sortedArray = itemsWithIds.map((d) => d.item);
|
|
2232
|
+
const newValidIds = itemsWithIds.map((d) => d.id);
|
|
2233
|
+
return rebuildStateShape(sortedArray as any, path, {
|
|
2234
|
+
validIds: newValidIds,
|
|
2235
|
+
});
|
|
2372
2236
|
};
|
|
2373
2237
|
}
|
|
2374
|
-
|
|
2375
2238
|
if (prop === "findWith") {
|
|
2376
2239
|
return (thisKey: keyof InferArrayElement<T>, thisValue: any) => {
|
|
2377
|
-
const
|
|
2378
|
-
|
|
2379
|
-
({ item }) => item[thisKey] === thisValue
|
|
2240
|
+
const foundItem = (currentState as any[]).find(
|
|
2241
|
+
(item) => item[thisKey] === thisValue
|
|
2380
2242
|
);
|
|
2381
|
-
if (!
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2243
|
+
if (!foundItem) return undefined;
|
|
2244
|
+
const itemId = `id:${foundItem.id}`;
|
|
2245
|
+
const finalPath = [...path, itemId];
|
|
2246
|
+
return rebuildStateShape(foundItem, finalPath, meta);
|
|
2384
2247
|
};
|
|
2385
2248
|
}
|
|
2386
2249
|
}
|
|
@@ -2390,7 +2253,6 @@ function createProxyHandler<T>(
|
|
|
2390
2253
|
const parentValue = getGlobalStore
|
|
2391
2254
|
.getState()
|
|
2392
2255
|
.getNestedState(stateKey, parentPath);
|
|
2393
|
-
|
|
2394
2256
|
if (Array.isArray(parentValue) && prop === "cut") {
|
|
2395
2257
|
return () =>
|
|
2396
2258
|
cutFunc(
|
|
@@ -2401,16 +2263,28 @@ function createProxyHandler<T>(
|
|
|
2401
2263
|
);
|
|
2402
2264
|
}
|
|
2403
2265
|
}
|
|
2404
|
-
|
|
2405
2266
|
if (prop === "get") {
|
|
2406
2267
|
return () => {
|
|
2407
|
-
if
|
|
2408
|
-
|
|
2409
|
-
|
|
2268
|
+
// Check if this proxy represents a derived array.
|
|
2269
|
+
// A derived array proxy has `meta.validIds` AND its `currentState` is an array.
|
|
2270
|
+
if (meta?.validIds && Array.isArray(currentState)) {
|
|
2271
|
+
// It IS a derived array proxy. Reconstruct it to ensure freshness.
|
|
2272
|
+
const sourceArray = getGlobalStore
|
|
2410
2273
|
.getState()
|
|
2411
2274
|
.getNestedState(stateKey, path) as any[];
|
|
2412
|
-
|
|
2275
|
+
if (!Array.isArray(sourceArray)) return [];
|
|
2276
|
+
|
|
2277
|
+
const sourceMap = new Map(
|
|
2278
|
+
sourceArray.map((item: any) => [`id:${item.id}`, item])
|
|
2279
|
+
);
|
|
2280
|
+
|
|
2281
|
+
return meta.validIds
|
|
2282
|
+
.map((id) => sourceMap.get(id))
|
|
2283
|
+
.filter(Boolean);
|
|
2413
2284
|
}
|
|
2285
|
+
|
|
2286
|
+
// For all other cases (non-derived arrays, single items, properties),
|
|
2287
|
+
// the standard lookup is correct.
|
|
2414
2288
|
return getGlobalStore.getState().getNestedState(stateKey, path);
|
|
2415
2289
|
};
|
|
2416
2290
|
}
|
|
@@ -2422,19 +2296,13 @@ function createProxyHandler<T>(
|
|
|
2422
2296
|
_effect: fn.toString(),
|
|
2423
2297
|
});
|
|
2424
2298
|
}
|
|
2425
|
-
|
|
2426
2299
|
if (prop === "$get") {
|
|
2427
|
-
return () =>
|
|
2428
|
-
$cogsSignal({
|
|
2429
|
-
_stateKey: stateKey,
|
|
2430
|
-
_path: path,
|
|
2431
|
-
});
|
|
2300
|
+
return () => $cogsSignal({ _stateKey: stateKey, _path: path });
|
|
2432
2301
|
}
|
|
2433
2302
|
if (prop === "lastSynced") {
|
|
2434
2303
|
const syncKey = `${stateKey}:${path.join(".")}`;
|
|
2435
2304
|
return getGlobalStore.getState().getSyncInfo(syncKey);
|
|
2436
2305
|
}
|
|
2437
|
-
|
|
2438
2306
|
if (prop == "getLocalStorage") {
|
|
2439
2307
|
return (key: string) =>
|
|
2440
2308
|
loadFromLocalStorage(sessionId + "-" + stateKey + "-" + key);
|
|
@@ -2442,13 +2310,16 @@ function createProxyHandler<T>(
|
|
|
2442
2310
|
if (prop === "_selected") {
|
|
2443
2311
|
const parentPath = path.slice(0, -1);
|
|
2444
2312
|
const parentKey = parentPath.join(".");
|
|
2445
|
-
|
|
2446
|
-
.
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2313
|
+
if (
|
|
2314
|
+
Array.isArray(
|
|
2315
|
+
getGlobalStore.getState().getNestedState(stateKey, parentPath)
|
|
2316
|
+
)
|
|
2317
|
+
) {
|
|
2318
|
+
const itemId = path[path.length - 1];
|
|
2319
|
+
const orderedIds = getOrderedIds(parentPath);
|
|
2320
|
+
const thisIndex = orderedIds?.indexOf(itemId!);
|
|
2450
2321
|
return (
|
|
2451
|
-
|
|
2322
|
+
thisIndex ===
|
|
2452
2323
|
getGlobalStore.getState().getSelectedIndex(stateKey, parentKey)
|
|
2453
2324
|
);
|
|
2454
2325
|
}
|
|
@@ -2457,37 +2328,39 @@ function createProxyHandler<T>(
|
|
|
2457
2328
|
if (prop === "setSelected") {
|
|
2458
2329
|
return (value: boolean) => {
|
|
2459
2330
|
const parentPath = path.slice(0, -1);
|
|
2460
|
-
const
|
|
2461
|
-
const
|
|
2331
|
+
const itemId = path[path.length - 1];
|
|
2332
|
+
const orderedIds = getOrderedIds(parentPath);
|
|
2333
|
+
const thisIndex = orderedIds?.indexOf(itemId!);
|
|
2462
2334
|
|
|
2463
|
-
if (
|
|
2464
|
-
getGlobalStore
|
|
2465
|
-
.getState()
|
|
2466
|
-
.setSelectedIndex(stateKey, parentKey, thisIndex);
|
|
2467
|
-
} else {
|
|
2468
|
-
getGlobalStore
|
|
2469
|
-
.getState()
|
|
2470
|
-
.setSelectedIndex(stateKey, parentKey, undefined);
|
|
2471
|
-
}
|
|
2335
|
+
if (thisIndex === undefined || thisIndex === -1) return;
|
|
2472
2336
|
|
|
2337
|
+
const parentKey = parentPath.join(".");
|
|
2338
|
+
getGlobalStore
|
|
2339
|
+
.getState()
|
|
2340
|
+
.setSelectedIndex(
|
|
2341
|
+
stateKey,
|
|
2342
|
+
parentKey,
|
|
2343
|
+
value ? thisIndex : undefined
|
|
2344
|
+
);
|
|
2473
2345
|
const nested = getGlobalStore
|
|
2474
2346
|
.getState()
|
|
2475
2347
|
.getNestedState(stateKey, [...parentPath]);
|
|
2476
2348
|
updateFn(effectiveSetState, nested, parentPath);
|
|
2477
|
-
|
|
2478
|
-
// Invalidate cache for this path
|
|
2479
2349
|
invalidateCachePath(parentPath);
|
|
2480
2350
|
};
|
|
2481
2351
|
}
|
|
2482
2352
|
if (prop === "toggleSelected") {
|
|
2483
2353
|
return () => {
|
|
2484
2354
|
const parentPath = path.slice(0, -1);
|
|
2485
|
-
const
|
|
2355
|
+
const itemId = path[path.length - 1];
|
|
2356
|
+
const orderedIds = getOrderedIds(parentPath);
|
|
2357
|
+
const thisIndex = orderedIds?.indexOf(itemId!);
|
|
2358
|
+
if (thisIndex === undefined || thisIndex === -1) return;
|
|
2359
|
+
|
|
2486
2360
|
const parentKey = parentPath.join(".");
|
|
2487
2361
|
const selectedIndex = getGlobalStore
|
|
2488
2362
|
.getState()
|
|
2489
2363
|
.getSelectedIndex(stateKey, parentKey);
|
|
2490
|
-
|
|
2491
2364
|
getGlobalStore
|
|
2492
2365
|
.getState()
|
|
2493
2366
|
.setSelectedIndex(
|
|
@@ -2495,11 +2368,11 @@ function createProxyHandler<T>(
|
|
|
2495
2368
|
parentKey,
|
|
2496
2369
|
selectedIndex === thisIndex ? undefined : thisIndex
|
|
2497
2370
|
);
|
|
2371
|
+
|
|
2498
2372
|
const nested = getGlobalStore
|
|
2499
2373
|
.getState()
|
|
2500
2374
|
.getNestedState(stateKey, [...parentPath]);
|
|
2501
2375
|
updateFn(effectiveSetState, nested, parentPath);
|
|
2502
|
-
|
|
2503
2376
|
invalidateCachePath(parentPath);
|
|
2504
2377
|
};
|
|
2505
2378
|
}
|
|
@@ -2509,34 +2382,20 @@ function createProxyHandler<T>(
|
|
|
2509
2382
|
const init = getGlobalStore
|
|
2510
2383
|
.getState()
|
|
2511
2384
|
.getInitialOptions(stateKey)?.validation;
|
|
2512
|
-
|
|
2513
|
-
if (!init?.key) {
|
|
2514
|
-
throw new Error("Validation key not found");
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
// Clear existing errors for this validation key
|
|
2385
|
+
if (!init?.key) throw new Error("Validation key not found");
|
|
2518
2386
|
removeValidationError(init.key);
|
|
2519
|
-
console.log("addValidationError", errors);
|
|
2520
|
-
// Add each new error
|
|
2521
2387
|
errors.forEach((error) => {
|
|
2522
2388
|
const fullErrorPath = [init.key, ...error.path].join(".");
|
|
2523
|
-
console.log("fullErrorPath", fullErrorPath);
|
|
2524
2389
|
addValidationError(fullErrorPath, error.message);
|
|
2525
2390
|
});
|
|
2526
|
-
|
|
2527
|
-
// Notify components to update
|
|
2528
2391
|
notifyComponents(stateKey);
|
|
2529
2392
|
};
|
|
2530
2393
|
}
|
|
2531
2394
|
if (prop === "applyJsonPatch") {
|
|
2532
2395
|
return (patches: any[]) => {
|
|
2533
|
-
// This part is correct.
|
|
2534
2396
|
const currentState =
|
|
2535
2397
|
getGlobalStore.getState().cogsStateStore[stateKey];
|
|
2536
|
-
const
|
|
2537
|
-
const newState = patchResult.newDocument;
|
|
2538
|
-
|
|
2539
|
-
// This is also correct.
|
|
2398
|
+
const newState = applyPatch(currentState, patches).newDocument;
|
|
2540
2399
|
updateGlobalState(
|
|
2541
2400
|
stateKey,
|
|
2542
2401
|
getGlobalStore.getState().initialStateGlobal[stateKey],
|
|
@@ -2545,105 +2404,7 @@ function createProxyHandler<T>(
|
|
|
2545
2404
|
componentId,
|
|
2546
2405
|
sessionId
|
|
2547
2406
|
);
|
|
2548
|
-
|
|
2549
|
-
const stateEntry = getGlobalStore
|
|
2550
|
-
.getState()
|
|
2551
|
-
.stateComponents.get(stateKey); // Use stateKey here
|
|
2552
|
-
|
|
2553
|
-
if (stateEntry) {
|
|
2554
|
-
// Use `getDifferences` between the state before and after the patch.
|
|
2555
|
-
const changedPaths = getDifferences(currentState, newState);
|
|
2556
|
-
const changedPathsSet = new Set(changedPaths);
|
|
2557
|
-
|
|
2558
|
-
// There is no single `primaryPathToCheck` for a patch, so we just check against the full set.
|
|
2559
|
-
|
|
2560
|
-
for (const [
|
|
2561
|
-
componentKey,
|
|
2562
|
-
component,
|
|
2563
|
-
] of stateEntry.components.entries()) {
|
|
2564
|
-
let shouldUpdate = false;
|
|
2565
|
-
const reactiveTypes = Array.isArray(component.reactiveType)
|
|
2566
|
-
? component.reactiveType
|
|
2567
|
-
: [component.reactiveType || "component"];
|
|
2568
|
-
|
|
2569
|
-
if (reactiveTypes.includes("none")) continue;
|
|
2570
|
-
if (reactiveTypes.includes("all")) {
|
|
2571
|
-
component.forceUpdate();
|
|
2572
|
-
continue;
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
|
-
if (reactiveTypes.includes("component")) {
|
|
2576
|
-
// This is the core logic that needs to be copied.
|
|
2577
|
-
// Check if any of the component's watched paths are in the set of changed paths.
|
|
2578
|
-
if (component.paths.has("")) {
|
|
2579
|
-
// Always update for root listeners
|
|
2580
|
-
shouldUpdate = true;
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
if (!shouldUpdate) {
|
|
2584
|
-
for (const changedPath of changedPathsSet) {
|
|
2585
|
-
// Direct match first (fastest)
|
|
2586
|
-
if (component.paths.has(changedPath)) {
|
|
2587
|
-
shouldUpdate = true;
|
|
2588
|
-
break;
|
|
2589
|
-
}
|
|
2590
|
-
|
|
2591
|
-
// Check parent paths more efficiently
|
|
2592
|
-
let dotIndex = changedPath.lastIndexOf(".");
|
|
2593
|
-
while (dotIndex !== -1) {
|
|
2594
|
-
const parentPath = changedPath.substring(0, dotIndex);
|
|
2595
|
-
if (component.paths.has(parentPath)) {
|
|
2596
|
-
shouldUpdate = true;
|
|
2597
|
-
break;
|
|
2598
|
-
}
|
|
2599
|
-
// Skip numeric segments more efficiently
|
|
2600
|
-
const lastSegment = changedPath.substring(
|
|
2601
|
-
dotIndex + 1
|
|
2602
|
-
);
|
|
2603
|
-
if (!isNaN(Number(lastSegment))) {
|
|
2604
|
-
// For array indices, check the parent collection path
|
|
2605
|
-
const parentDotIndex = parentPath.lastIndexOf(".");
|
|
2606
|
-
if (parentDotIndex !== -1) {
|
|
2607
|
-
const grandParentPath = parentPath.substring(
|
|
2608
|
-
0,
|
|
2609
|
-
parentDotIndex
|
|
2610
|
-
);
|
|
2611
|
-
if (component.paths.has(grandParentPath)) {
|
|
2612
|
-
shouldUpdate = true;
|
|
2613
|
-
break;
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
}
|
|
2617
|
-
dotIndex = parentPath.lastIndexOf(".");
|
|
2618
|
-
}
|
|
2619
|
-
|
|
2620
|
-
if (shouldUpdate) break;
|
|
2621
|
-
}
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
|
|
2625
|
-
if (!shouldUpdate && reactiveTypes.includes("deps")) {
|
|
2626
|
-
// Use `newState` (the result of the patch) for dependency checks.
|
|
2627
|
-
if (component.depsFunction) {
|
|
2628
|
-
const depsResult = component.depsFunction(newState);
|
|
2629
|
-
let depsChanged = false;
|
|
2630
|
-
if (typeof depsResult === "boolean") {
|
|
2631
|
-
if (depsResult) depsChanged = true;
|
|
2632
|
-
} else if (!isDeepEqual(component.deps, depsResult)) {
|
|
2633
|
-
component.deps = depsResult;
|
|
2634
|
-
depsChanged = true;
|
|
2635
|
-
}
|
|
2636
|
-
if (depsChanged) {
|
|
2637
|
-
shouldUpdate = true;
|
|
2638
|
-
}
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
if (shouldUpdate) {
|
|
2643
|
-
component.forceUpdate();
|
|
2644
|
-
}
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2407
|
+
notifyComponents(stateKey); // Simplified notification
|
|
2647
2408
|
};
|
|
2648
2409
|
}
|
|
2649
2410
|
if (prop === "validateZodSchema") {
|
|
@@ -2651,75 +2412,31 @@ function createProxyHandler<T>(
|
|
|
2651
2412
|
const init = getGlobalStore
|
|
2652
2413
|
.getState()
|
|
2653
2414
|
.getInitialOptions(stateKey)?.validation;
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
if (!init?.zodSchema) {
|
|
2658
|
-
throw new Error("Zod schema not found");
|
|
2659
|
-
}
|
|
2415
|
+
if (!init?.zodSchema || !init?.key)
|
|
2416
|
+
throw new Error("Zod schema or validation key not found");
|
|
2660
2417
|
|
|
2661
|
-
if (!init?.key) {
|
|
2662
|
-
throw new Error("Validation key not found");
|
|
2663
|
-
}
|
|
2664
2418
|
removeValidationError(init.key);
|
|
2665
2419
|
const thisObject =
|
|
2666
2420
|
getGlobalStore.getState().cogsStateStore[stateKey];
|
|
2421
|
+
const result = init.zodSchema.safeParse(thisObject);
|
|
2667
2422
|
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
if (existingErrors && existingErrors.length > 0) {
|
|
2675
|
-
existingErrors.forEach(([errorPath]) => {
|
|
2676
|
-
if (errorPath && errorPath.startsWith(init.key!)) {
|
|
2677
|
-
removeValidationError(errorPath);
|
|
2678
|
-
}
|
|
2679
|
-
});
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
// Attempt to validate with Zod
|
|
2683
|
-
const result = init.zodSchema.safeParse(thisObject);
|
|
2684
|
-
|
|
2685
|
-
if (!result.success) {
|
|
2686
|
-
// Process Zod errors and add them to the validation store
|
|
2687
|
-
const zodErrors = result.error.errors;
|
|
2688
|
-
|
|
2689
|
-
zodErrors.forEach((error) => {
|
|
2690
|
-
const errorPath = error.path;
|
|
2691
|
-
const errorMessage = error.message;
|
|
2692
|
-
|
|
2693
|
-
// Build the full path for the validation error
|
|
2694
|
-
// Format: validationKey.path.to.field
|
|
2695
|
-
const fullErrorPath = [init.key, ...errorPath].join(".");
|
|
2696
|
-
|
|
2697
|
-
// Add the error to the store
|
|
2698
|
-
addValidationError(fullErrorPath, errorMessage);
|
|
2699
|
-
});
|
|
2700
|
-
|
|
2701
|
-
notifyComponents(stateKey);
|
|
2702
|
-
|
|
2703
|
-
return false;
|
|
2704
|
-
}
|
|
2705
|
-
|
|
2706
|
-
return true;
|
|
2707
|
-
} catch (error) {
|
|
2708
|
-
console.error("Zod schema validation failed", error);
|
|
2423
|
+
if (!result.success) {
|
|
2424
|
+
result.error.errors.forEach((error) => {
|
|
2425
|
+
const fullErrorPath = [init.key, ...error.path].join(".");
|
|
2426
|
+
addValidationError(fullErrorPath, error.message);
|
|
2427
|
+
});
|
|
2428
|
+
notifyComponents(stateKey);
|
|
2709
2429
|
return false;
|
|
2710
2430
|
}
|
|
2431
|
+
return true;
|
|
2711
2432
|
};
|
|
2712
2433
|
}
|
|
2713
2434
|
if (prop === "_componentId") return componentId;
|
|
2714
|
-
if (prop === "getComponents")
|
|
2435
|
+
if (prop === "getComponents")
|
|
2715
2436
|
return () => getGlobalStore().stateComponents.get(stateKey);
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
return formRefStore.getState().getFormRefsByStateKey(stateKey);
|
|
2720
|
-
};
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2437
|
+
if (prop === "getAllFormRefs")
|
|
2438
|
+
return () =>
|
|
2439
|
+
formRefStore.getState().getFormRefsByStateKey(stateKey);
|
|
2723
2440
|
if (prop === "_initialState")
|
|
2724
2441
|
return getGlobalStore.getState().initialStateGlobal[stateKey];
|
|
2725
2442
|
if (prop === "_serverState")
|
|
@@ -2732,13 +2449,9 @@ function createProxyHandler<T>(
|
|
|
2732
2449
|
if (prop === "removeValidation") return baseObj.removeValidation;
|
|
2733
2450
|
}
|
|
2734
2451
|
if (prop === "getFormRef") {
|
|
2735
|
-
return () =>
|
|
2736
|
-
|
|
2737
|
-
.getState()
|
|
2738
|
-
.getFormRef(stateKey + "." + path.join("."));
|
|
2739
|
-
};
|
|
2452
|
+
return () =>
|
|
2453
|
+
formRefStore.getState().getFormRef(stateKey + "." + path.join("."));
|
|
2740
2454
|
}
|
|
2741
|
-
|
|
2742
2455
|
if (prop === "validationWrapper") {
|
|
2743
2456
|
return ({
|
|
2744
2457
|
children,
|
|
@@ -2753,20 +2466,16 @@ function createProxyHandler<T>(
|
|
|
2753
2466
|
}
|
|
2754
2467
|
path={path}
|
|
2755
2468
|
stateKey={stateKey}
|
|
2756
|
-
validIndices={meta?.validIndices}
|
|
2757
2469
|
>
|
|
2758
2470
|
{children}
|
|
2759
2471
|
</ValidationWrapper>
|
|
2760
2472
|
);
|
|
2761
2473
|
}
|
|
2762
|
-
|
|
2763
2474
|
if (prop === "_stateKey") return stateKey;
|
|
2764
2475
|
if (prop === "_path") return path;
|
|
2765
2476
|
if (prop === "_isServerSynced") return baseObj._isServerSynced;
|
|
2766
|
-
|
|
2767
2477
|
if (prop === "update") {
|
|
2768
2478
|
return (payload: UpdateArg<T>, opts?: UpdateOpts<T>) => {
|
|
2769
|
-
// ADDED: Invalidate cache on update
|
|
2770
2479
|
if (opts?.debounce) {
|
|
2771
2480
|
debounce(() => {
|
|
2772
2481
|
updateFn(effectiveSetState, payload, path, "");
|
|
@@ -2785,19 +2494,16 @@ function createProxyHandler<T>(
|
|
|
2785
2494
|
invalidateCachePath(path);
|
|
2786
2495
|
};
|
|
2787
2496
|
}
|
|
2788
|
-
|
|
2789
2497
|
if (prop === "formElement") {
|
|
2790
|
-
return (child: FormControl<T>, formOpts?: FormOptsType) =>
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
);
|
|
2800
|
-
};
|
|
2498
|
+
return (child: FormControl<T>, formOpts?: FormOptsType) => (
|
|
2499
|
+
<FormControlComponent<T>
|
|
2500
|
+
setState={effectiveSetState}
|
|
2501
|
+
stateKey={stateKey}
|
|
2502
|
+
path={path}
|
|
2503
|
+
child={child}
|
|
2504
|
+
formOpts={formOpts}
|
|
2505
|
+
/>
|
|
2506
|
+
);
|
|
2801
2507
|
}
|
|
2802
2508
|
|
|
2803
2509
|
const nextPath = [...path, prop];
|
|
@@ -2809,12 +2515,10 @@ function createProxyHandler<T>(
|
|
|
2809
2515
|
};
|
|
2810
2516
|
|
|
2811
2517
|
const proxyInstance = new Proxy(baseFunction, handler);
|
|
2812
|
-
|
|
2813
2518
|
shapeCache.set(cacheKey, {
|
|
2814
2519
|
proxy: proxyInstance,
|
|
2815
2520
|
stateVersion: stateVersion,
|
|
2816
2521
|
});
|
|
2817
|
-
|
|
2818
2522
|
return proxyInstance;
|
|
2819
2523
|
}
|
|
2820
2524
|
|
|
@@ -2833,7 +2537,6 @@ export function $cogsSignal(proxy: {
|
|
|
2833
2537
|
|
|
2834
2538
|
function SignalMapRenderer({
|
|
2835
2539
|
proxy,
|
|
2836
|
-
|
|
2837
2540
|
rebuildStateShape,
|
|
2838
2541
|
}: {
|
|
2839
2542
|
proxy: {
|
|
@@ -2847,30 +2550,26 @@ function SignalMapRenderer({
|
|
|
2847
2550
|
arraySetter: any
|
|
2848
2551
|
) => ReactNode;
|
|
2849
2552
|
};
|
|
2850
|
-
|
|
2851
2553
|
rebuildStateShape: (
|
|
2852
2554
|
currentState: any,
|
|
2853
2555
|
path: string[],
|
|
2854
|
-
meta?: {
|
|
2556
|
+
meta?: { validIds?: string[] }
|
|
2855
2557
|
) => any;
|
|
2856
2558
|
}) {
|
|
2857
2559
|
const value = getGlobalStore().getNestedState(proxy._stateKey, proxy._path);
|
|
2560
|
+
if (!Array.isArray(value)) return null;
|
|
2858
2561
|
|
|
2859
|
-
if (!Array.isArray(value)) {
|
|
2860
|
-
return null;
|
|
2861
|
-
}
|
|
2862
2562
|
const arraySetter = rebuildStateShape(
|
|
2863
2563
|
value,
|
|
2864
2564
|
proxy._path
|
|
2865
2565
|
) as ArrayEndType<any>;
|
|
2866
|
-
// Use existing global state management
|
|
2867
2566
|
return arraySetter.stateMapNoRender(
|
|
2868
|
-
(item, setter, index,
|
|
2869
|
-
|
|
2870
|
-
return proxy._mapFn(item, setter, index, value, arraysetter);
|
|
2567
|
+
(item, setter, index, array, arraySetter) => {
|
|
2568
|
+
return proxy._mapFn(item, setter, index, array, arraySetter);
|
|
2871
2569
|
}
|
|
2872
2570
|
);
|
|
2873
2571
|
}
|
|
2572
|
+
|
|
2874
2573
|
function SignalRenderer({
|
|
2875
2574
|
proxy,
|
|
2876
2575
|
}: {
|
|
@@ -2907,12 +2606,10 @@ function SignalRenderer({
|
|
|
2907
2606
|
|
|
2908
2607
|
getGlobalStore.getState().addSignalElement(signalId, elementInfo);
|
|
2909
2608
|
|
|
2910
|
-
// Get the raw value from the store
|
|
2911
2609
|
const value = getGlobalStore
|
|
2912
2610
|
.getState()
|
|
2913
2611
|
.getNestedState(proxy._stateKey, proxy._path);
|
|
2914
|
-
|
|
2915
|
-
let displayValue;
|
|
2612
|
+
let displayValue = value;
|
|
2916
2613
|
if (proxy._effect) {
|
|
2917
2614
|
try {
|
|
2918
2615
|
displayValue = new Function(
|
|
@@ -2920,11 +2617,8 @@ function SignalRenderer({
|
|
|
2920
2617
|
`return (${proxy._effect})(state)`
|
|
2921
2618
|
)(value);
|
|
2922
2619
|
} catch (err) {
|
|
2923
|
-
console.error("Error evaluating effect function
|
|
2924
|
-
displayValue = value; // Fallback to raw value
|
|
2620
|
+
console.error("Error evaluating effect function:", err);
|
|
2925
2621
|
}
|
|
2926
|
-
} else {
|
|
2927
|
-
displayValue = value;
|
|
2928
2622
|
}
|
|
2929
2623
|
|
|
2930
2624
|
if (displayValue !== null && typeof displayValue === "object") {
|
|
@@ -2941,6 +2635,7 @@ function SignalRenderer({
|
|
|
2941
2635
|
"data-signal-id": signalId,
|
|
2942
2636
|
});
|
|
2943
2637
|
}
|
|
2638
|
+
|
|
2944
2639
|
export function $cogsSignalStore(proxy: {
|
|
2945
2640
|
_path: string[];
|
|
2946
2641
|
_stateKey: string;
|
|
@@ -2949,13 +2644,14 @@ export function $cogsSignalStore(proxy: {
|
|
|
2949
2644
|
(notify) => {
|
|
2950
2645
|
const stateEntry = getGlobalStore
|
|
2951
2646
|
.getState()
|
|
2952
|
-
.stateComponents.get(proxy._stateKey) || {
|
|
2953
|
-
components: new Map(),
|
|
2954
|
-
};
|
|
2647
|
+
.stateComponents.get(proxy._stateKey) || { components: new Map() };
|
|
2955
2648
|
stateEntry.components.set(proxy._stateKey, {
|
|
2956
2649
|
forceUpdate: notify,
|
|
2957
2650
|
paths: new Set([proxy._path.join(".")]),
|
|
2958
2651
|
});
|
|
2652
|
+
getGlobalStore
|
|
2653
|
+
.getState()
|
|
2654
|
+
.stateComponents.set(proxy._stateKey, stateEntry);
|
|
2959
2655
|
return () => stateEntry.components.delete(proxy._stateKey);
|
|
2960
2656
|
},
|
|
2961
2657
|
() => getGlobalStore.getState().getNestedState(proxy._stateKey, proxy._path)
|
|
@@ -2963,12 +2659,10 @@ export function $cogsSignalStore(proxy: {
|
|
|
2963
2659
|
return createElement("text", {}, String(value));
|
|
2964
2660
|
}
|
|
2965
2661
|
|
|
2966
|
-
// Modified CogsItemWrapper that stores the DOM ref
|
|
2967
2662
|
function CogsItemWrapper({
|
|
2968
2663
|
stateKey,
|
|
2969
2664
|
itemComponentId,
|
|
2970
2665
|
itemPath,
|
|
2971
|
-
formOpts,
|
|
2972
2666
|
children,
|
|
2973
2667
|
}: {
|
|
2974
2668
|
stateKey: string;
|
|
@@ -2977,16 +2671,11 @@ function CogsItemWrapper({
|
|
|
2977
2671
|
formOpts?: FormOptsType;
|
|
2978
2672
|
children: React.ReactNode;
|
|
2979
2673
|
}) {
|
|
2980
|
-
// This hook handles the re-rendering when the item's own data changes.
|
|
2981
2674
|
const [, forceUpdate] = useState({});
|
|
2982
|
-
// This hook measures the element.
|
|
2983
2675
|
const [measureRef, bounds] = useMeasure();
|
|
2984
|
-
// Store the actual DOM element
|
|
2985
2676
|
const elementRef = useRef<HTMLDivElement | null>(null);
|
|
2986
|
-
// This ref prevents sending the same height update repeatedly.
|
|
2987
2677
|
const lastReportedHeight = useRef<number | null>(null);
|
|
2988
2678
|
|
|
2989
|
-
// Combine both refs
|
|
2990
2679
|
const setRefs = useCallback(
|
|
2991
2680
|
(element: HTMLDivElement | null) => {
|
|
2992
2681
|
measureRef(element);
|
|
@@ -2995,50 +2684,32 @@ function CogsItemWrapper({
|
|
|
2995
2684
|
[measureRef]
|
|
2996
2685
|
);
|
|
2997
2686
|
|
|
2998
|
-
// This is the primary effect for this component.
|
|
2999
2687
|
useEffect(() => {
|
|
3000
|
-
// We only report a height if it's a valid number AND it's different
|
|
3001
|
-
// from the last height we reported. This prevents infinite loops.
|
|
3002
2688
|
if (bounds.height > 0 && bounds.height !== lastReportedHeight.current) {
|
|
3003
|
-
// Store the new height so we don't report it again.
|
|
3004
2689
|
lastReportedHeight.current = bounds.height;
|
|
3005
|
-
|
|
3006
|
-
// Call the store function to save the height AND the ref
|
|
3007
2690
|
getGlobalStore.getState().setShadowMetadata(stateKey, itemPath, {
|
|
3008
|
-
virtualizer: {
|
|
3009
|
-
itemHeight: bounds.height,
|
|
3010
|
-
domRef: elementRef.current, // Store the actual DOM element reference
|
|
3011
|
-
},
|
|
2691
|
+
virtualizer: { itemHeight: bounds.height, domRef: elementRef.current },
|
|
3012
2692
|
});
|
|
3013
2693
|
}
|
|
3014
|
-
}, [bounds.height, stateKey, itemPath]);
|
|
2694
|
+
}, [bounds.height, stateKey, itemPath]);
|
|
3015
2695
|
|
|
3016
|
-
// This effect handles subscribing the item to its own data path for updates.
|
|
3017
2696
|
useLayoutEffect(() => {
|
|
3018
2697
|
const fullComponentId = `${stateKey}////${itemComponentId}`;
|
|
3019
2698
|
const stateEntry = getGlobalStore
|
|
3020
2699
|
.getState()
|
|
3021
|
-
.stateComponents.get(stateKey) || {
|
|
3022
|
-
components: new Map(),
|
|
3023
|
-
};
|
|
3024
|
-
|
|
2700
|
+
.stateComponents.get(stateKey) || { components: new Map() };
|
|
3025
2701
|
stateEntry.components.set(fullComponentId, {
|
|
3026
2702
|
forceUpdate: () => forceUpdate({}),
|
|
3027
2703
|
paths: new Set([itemPath.join(".")]),
|
|
3028
2704
|
});
|
|
3029
|
-
|
|
3030
2705
|
getGlobalStore.getState().stateComponents.set(stateKey, stateEntry);
|
|
3031
|
-
|
|
3032
2706
|
return () => {
|
|
3033
2707
|
const currentEntry = getGlobalStore
|
|
3034
2708
|
.getState()
|
|
3035
2709
|
.stateComponents.get(stateKey);
|
|
3036
|
-
if (currentEntry)
|
|
3037
|
-
currentEntry.components.delete(fullComponentId);
|
|
3038
|
-
}
|
|
2710
|
+
if (currentEntry) currentEntry.components.delete(fullComponentId);
|
|
3039
2711
|
};
|
|
3040
2712
|
}, [stateKey, itemComponentId, itemPath.join(".")]);
|
|
3041
2713
|
|
|
3042
|
-
// The rendered output is a simple div that gets measured.
|
|
3043
2714
|
return <div ref={setRefs}>{children}</div>;
|
|
3044
2715
|
}
|