cogsbox-state 0.5.427 → 0.5.429
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 +762 -830
- 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 +423 -729
- 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,18 +1714,28 @@ 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
|
|
1820
1737
|
): VirtualStateObjectResult<any[]> => {
|
|
1738
|
+
// --- All this setup is from your original, working code ---
|
|
1821
1739
|
const {
|
|
1822
1740
|
itemHeight = 50,
|
|
1823
1741
|
overscan = 6,
|
|
@@ -1835,7 +1753,8 @@ function createProxyHandler<T>(
|
|
|
1835
1753
|
const userHasScrolledAwayRef = useRef(false);
|
|
1836
1754
|
const previousCountRef = useRef(0);
|
|
1837
1755
|
const lastRangeRef = useRef(range);
|
|
1838
|
-
|
|
1756
|
+
|
|
1757
|
+
// This is still the correct way to trigger re-calculations when item heights change.
|
|
1839
1758
|
useEffect(() => {
|
|
1840
1759
|
const unsubscribe = getGlobalStore
|
|
1841
1760
|
.getState()
|
|
@@ -1851,21 +1770,37 @@ function createProxyHandler<T>(
|
|
|
1851
1770
|
) as any[];
|
|
1852
1771
|
const totalCount = sourceArray.length;
|
|
1853
1772
|
|
|
1773
|
+
// --- START OF IMPROVEMENT ---
|
|
1774
|
+
// Get the canonical order of item IDs for this array. This is the most
|
|
1775
|
+
// reliable way to link an item's index to its metadata.
|
|
1776
|
+
const orderedIds = getOrderedIds(path);
|
|
1777
|
+
// --- END OF IMPROVEMENT ---
|
|
1778
|
+
|
|
1854
1779
|
// Calculate heights and positions
|
|
1855
1780
|
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
1781
|
let height = 0;
|
|
1863
1782
|
const pos: number[] = [];
|
|
1864
1783
|
for (let i = 0; i < totalCount; i++) {
|
|
1865
1784
|
pos[i] = height;
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1785
|
+
|
|
1786
|
+
// --- START OF IMPROVEMENT ---
|
|
1787
|
+
// Use the ordered ID to look up the correct metadata for the item at this index.
|
|
1788
|
+
// This is much more reliable than numeric indexing into a metadata store.
|
|
1789
|
+
const itemId = orderedIds?.[i];
|
|
1790
|
+
let measuredHeight = itemHeight; // Default height
|
|
1791
|
+
|
|
1792
|
+
if (itemId) {
|
|
1793
|
+
const itemPathWithId = [...path, itemId];
|
|
1794
|
+
const itemMeta = getGlobalStore
|
|
1795
|
+
.getState()
|
|
1796
|
+
.getShadowMetadata(stateKey, itemPathWithId);
|
|
1797
|
+
// Get the measured height from the shadow state if it exists.
|
|
1798
|
+
measuredHeight =
|
|
1799
|
+
itemMeta?.virtualizer?.itemHeight || itemHeight;
|
|
1800
|
+
}
|
|
1801
|
+
// --- END OF IMPROVEMENT ---
|
|
1802
|
+
|
|
1803
|
+
height += measuredHeight;
|
|
1869
1804
|
}
|
|
1870
1805
|
return { totalHeight: height, positions: pos };
|
|
1871
1806
|
}, [
|
|
@@ -1874,87 +1809,57 @@ function createProxyHandler<T>(
|
|
|
1874
1809
|
path.join("."),
|
|
1875
1810
|
itemHeight,
|
|
1876
1811
|
shadowUpdateTrigger,
|
|
1812
|
+
orderedIds, // Add `orderedIds` to the dependency array
|
|
1877
1813
|
]);
|
|
1878
1814
|
|
|
1879
|
-
// Create virtual state
|
|
1815
|
+
// Create virtual state (This part of your original code looks fine)
|
|
1880
1816
|
const virtualState = useMemo(() => {
|
|
1881
1817
|
const start = Math.max(0, range.startIndex);
|
|
1882
1818
|
const end = Math.min(totalCount, range.endIndex);
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1819
|
+
|
|
1820
|
+
// --- START OF IMPROVEMENT ---
|
|
1821
|
+
// We use `orderedIds` here as well to create a `validIds` list for the
|
|
1822
|
+
// virtualized proxy. This ensures that any subsequent operations on `virtualState`
|
|
1823
|
+
// (like `.index()` or `.getSelected()`) will have the correct context.
|
|
1824
|
+
const slicedIds = orderedIds?.slice(start, end);
|
|
1825
|
+
const sourceMap = new Map(
|
|
1826
|
+
sourceArray.map((item: any) => [`id:${item.id}`, item])
|
|
1886
1827
|
);
|
|
1887
|
-
const slicedArray =
|
|
1828
|
+
const slicedArray =
|
|
1829
|
+
slicedIds?.map((id) => sourceMap.get(id)).filter(Boolean) ||
|
|
1830
|
+
[];
|
|
1831
|
+
|
|
1888
1832
|
return rebuildStateShape(slicedArray as any, path, {
|
|
1889
1833
|
...meta,
|
|
1890
|
-
|
|
1834
|
+
validIds: slicedIds, // Pass the sliced IDs as the new `validIds`
|
|
1891
1835
|
});
|
|
1892
|
-
|
|
1836
|
+
// --- END OF IMPROVEMENT ---
|
|
1837
|
+
}, [
|
|
1838
|
+
range.startIndex,
|
|
1839
|
+
range.endIndex,
|
|
1840
|
+
sourceArray,
|
|
1841
|
+
totalCount,
|
|
1842
|
+
orderedIds,
|
|
1843
|
+
]);
|
|
1893
1844
|
|
|
1894
|
-
//
|
|
1895
|
-
|
|
1896
|
-
const rawShadowData = getGlobalStore
|
|
1897
|
-
.getState()
|
|
1898
|
-
.getShadowMetadata(stateKey, path);
|
|
1899
|
-
const shadowArray: ItemMeta[] = Array.isArray(rawShadowData)
|
|
1900
|
-
? rawShadowData
|
|
1901
|
-
: [];
|
|
1902
|
-
const lastIndex = totalCount - 1;
|
|
1903
|
-
|
|
1904
|
-
if (lastIndex >= 0) {
|
|
1905
|
-
const lastItemData = shadowArray[lastIndex];
|
|
1906
|
-
if (lastItemData?.virtualizer?.domRef) {
|
|
1907
|
-
const element = lastItemData.virtualizer.domRef;
|
|
1908
|
-
if (element && element.scrollIntoView) {
|
|
1909
|
-
element.scrollIntoView({
|
|
1910
|
-
behavior: "auto",
|
|
1911
|
-
block: "end",
|
|
1912
|
-
inline: "nearest",
|
|
1913
|
-
});
|
|
1914
|
-
return true;
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
return false;
|
|
1919
|
-
}, [stateKey, path, totalCount]);
|
|
1845
|
+
// --- All the following logic for scrolling and event handling is from your original code. ---
|
|
1846
|
+
// It is preserved, but we will improve `scrollToIndex` to use the shadow refs.
|
|
1920
1847
|
|
|
1921
|
-
// Handle new items when at bottom
|
|
1848
|
+
// Handle new items when at bottom (original logic)
|
|
1922
1849
|
useEffect(() => {
|
|
1923
1850
|
if (!stickToBottom || totalCount === 0) return;
|
|
1924
|
-
|
|
1925
1851
|
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
1852
|
if (
|
|
1931
|
-
|
|
1853
|
+
hasNewItems &&
|
|
1932
1854
|
wasAtBottomRef.current &&
|
|
1933
1855
|
!userHasScrolledAwayRef.current
|
|
1934
1856
|
) {
|
|
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);
|
|
1857
|
+
setTimeout(() => scrollToIndex(totalCount - 1, "smooth"), 50);
|
|
1952
1858
|
}
|
|
1953
|
-
|
|
1954
1859
|
previousCountRef.current = totalCount;
|
|
1955
|
-
}, [totalCount,
|
|
1860
|
+
}, [totalCount, stickToBottom]);
|
|
1956
1861
|
|
|
1957
|
-
// Handle scroll events
|
|
1862
|
+
// Handle scroll events (original logic)
|
|
1958
1863
|
useEffect(() => {
|
|
1959
1864
|
const container = containerRef.current;
|
|
1960
1865
|
if (!container) return;
|
|
@@ -1963,21 +1868,12 @@ function createProxyHandler<T>(
|
|
|
1963
1868
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
1964
1869
|
const distanceFromBottom =
|
|
1965
1870
|
scrollHeight - scrollTop - clientHeight;
|
|
1966
|
-
|
|
1967
|
-
// Track if we're at bottom
|
|
1968
1871
|
wasAtBottomRef.current = distanceFromBottom < 5;
|
|
1969
|
-
|
|
1970
|
-
// If user scrolls away from bottom past threshold, set flag
|
|
1971
|
-
if (distanceFromBottom > 100) {
|
|
1872
|
+
if (distanceFromBottom > 100)
|
|
1972
1873
|
userHasScrolledAwayRef.current = true;
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
// If user scrolls back to bottom, cle
|
|
1976
|
-
if (distanceFromBottom < 5) {
|
|
1874
|
+
if (distanceFromBottom < 5)
|
|
1977
1875
|
userHasScrolledAwayRef.current = false;
|
|
1978
|
-
}
|
|
1979
1876
|
|
|
1980
|
-
// Update visible range based on scroll position
|
|
1981
1877
|
let startIndex = 0;
|
|
1982
1878
|
for (let i = 0; i < positions.length; i++) {
|
|
1983
1879
|
if (positions[i]! > scrollTop - itemHeight * overscan) {
|
|
@@ -1985,7 +1881,6 @@ function createProxyHandler<T>(
|
|
|
1985
1881
|
break;
|
|
1986
1882
|
}
|
|
1987
1883
|
}
|
|
1988
|
-
|
|
1989
1884
|
let endIndex = startIndex;
|
|
1990
1885
|
const viewportEnd = scrollTop + clientHeight;
|
|
1991
1886
|
for (let i = startIndex; i < positions.length; i++) {
|
|
@@ -1994,14 +1889,12 @@ function createProxyHandler<T>(
|
|
|
1994
1889
|
}
|
|
1995
1890
|
endIndex = i;
|
|
1996
1891
|
}
|
|
1997
|
-
|
|
1998
1892
|
const newStartIndex = Math.max(0, startIndex);
|
|
1999
1893
|
const newEndIndex = Math.min(
|
|
2000
1894
|
totalCount,
|
|
2001
1895
|
endIndex + 1 + overscan
|
|
2002
1896
|
);
|
|
2003
1897
|
|
|
2004
|
-
// THE FIX: Only update state if the visible range of items has changed.
|
|
2005
1898
|
if (
|
|
2006
1899
|
newStartIndex !== lastRangeRef.current.startIndex ||
|
|
2007
1900
|
newEndIndex !== lastRangeRef.current.endIndex
|
|
@@ -2020,93 +1913,59 @@ function createProxyHandler<T>(
|
|
|
2020
1913
|
container.addEventListener("scroll", handleScroll, {
|
|
2021
1914
|
passive: true,
|
|
2022
1915
|
});
|
|
2023
|
-
|
|
2024
|
-
// Only auto-scroll on initial load when user hasn't scrolled away
|
|
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
1916
|
handleScroll();
|
|
2039
|
-
|
|
2040
|
-
return () => {
|
|
1917
|
+
return () =>
|
|
2041
1918
|
container.removeEventListener("scroll", handleScroll);
|
|
2042
|
-
|
|
2043
|
-
}, [positions, totalCount, itemHeight, overscan, stickToBottom]);
|
|
1919
|
+
}, [positions, totalCount, itemHeight, overscan]);
|
|
2044
1920
|
|
|
2045
1921
|
const scrollToBottom = useCallback(() => {
|
|
2046
1922
|
wasAtBottomRef.current = true;
|
|
2047
1923
|
userHasScrolledAwayRef.current = false;
|
|
2048
|
-
|
|
2049
|
-
if (!scrolled && containerRef.current) {
|
|
1924
|
+
if (containerRef.current) {
|
|
2050
1925
|
containerRef.current.scrollTop =
|
|
2051
1926
|
containerRef.current.scrollHeight;
|
|
2052
1927
|
}
|
|
2053
|
-
}, [
|
|
1928
|
+
}, []);
|
|
1929
|
+
|
|
2054
1930
|
const scrollToIndex = useCallback(
|
|
2055
1931
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
2056
1932
|
const container = containerRef.current;
|
|
2057
1933
|
if (!container) return;
|
|
2058
1934
|
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
if (
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
1935
|
+
// --- START OF IMPROVEMENT ---
|
|
1936
|
+
// Use the orderedId to reliably get the item's metadata and DOM ref.
|
|
1937
|
+
const itemId = orderedIds?.[index];
|
|
1938
|
+
if (itemId) {
|
|
1939
|
+
const itemPathWithId = [...path, itemId];
|
|
1940
|
+
const itemMeta = getGlobalStore
|
|
1941
|
+
.getState()
|
|
1942
|
+
.getShadowMetadata(stateKey, itemPathWithId);
|
|
1943
|
+
const element = itemMeta?.virtualizer?.domRef;
|
|
1944
|
+
|
|
1945
|
+
// If we have a direct ref to the DOM element from CogsItemWrapper, use it! It's the most reliable.
|
|
1946
|
+
if (element?.scrollIntoView) {
|
|
1947
|
+
element.scrollIntoView({ behavior, block: "nearest" });
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
2070
1950
|
}
|
|
1951
|
+
// --- END OF IMPROVEMENT ---
|
|
2071
1952
|
|
|
2072
|
-
//
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
.
|
|
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
|
-
});
|
|
1953
|
+
// Fallback to position-based scrolling if the ref isn't available for any reason.
|
|
1954
|
+
const top = positions[index];
|
|
1955
|
+
if (top !== undefined) {
|
|
1956
|
+
container.scrollTo({ top, behavior });
|
|
2095
1957
|
}
|
|
2096
1958
|
},
|
|
2097
|
-
[positions, stateKey, path,
|
|
1959
|
+
[positions, stateKey, path, orderedIds] // Add `orderedIds` to dependency array
|
|
2098
1960
|
);
|
|
2099
1961
|
|
|
2100
1962
|
const virtualizerProps = {
|
|
2101
1963
|
outer: {
|
|
2102
1964
|
ref: containerRef,
|
|
2103
|
-
style: { overflowY: "auto"
|
|
1965
|
+
style: { overflowY: "auto", height: "100%" },
|
|
2104
1966
|
},
|
|
2105
1967
|
inner: {
|
|
2106
|
-
style: {
|
|
2107
|
-
height: `${totalHeight}px`,
|
|
2108
|
-
position: "relative" as const,
|
|
2109
|
-
},
|
|
1968
|
+
style: { height: `${totalHeight}px`, position: "relative" },
|
|
2110
1969
|
},
|
|
2111
1970
|
list: {
|
|
2112
1971
|
style: {
|
|
@@ -2117,112 +1976,118 @@ function createProxyHandler<T>(
|
|
|
2117
1976
|
|
|
2118
1977
|
return {
|
|
2119
1978
|
virtualState,
|
|
2120
|
-
virtualizerProps,
|
|
1979
|
+
virtualizerProps: virtualizerProps as any,
|
|
2121
1980
|
scrollToBottom,
|
|
2122
1981
|
scrollToIndex,
|
|
2123
1982
|
};
|
|
2124
1983
|
};
|
|
2125
1984
|
}
|
|
2126
|
-
if (prop === "
|
|
1985
|
+
if (prop === "stateMap") {
|
|
2127
1986
|
return (
|
|
2128
1987
|
callbackfn: (
|
|
2129
|
-
value:
|
|
2130
|
-
setter:
|
|
1988
|
+
value: any,
|
|
1989
|
+
setter: any,
|
|
2131
1990
|
index: number,
|
|
2132
|
-
array:
|
|
2133
|
-
arraySetter:
|
|
2134
|
-
) =>
|
|
1991
|
+
array: any,
|
|
1992
|
+
arraySetter: any
|
|
1993
|
+
) => void
|
|
2135
1994
|
) => {
|
|
2136
1995
|
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()];
|
|
1996
|
+
const itemIdsForCurrentArray =
|
|
1997
|
+
meta?.validIds || getOrderedIds(path) || [];
|
|
1998
|
+
const arraySetter = rebuildStateShape(currentState, path, meta);
|
|
2149
1999
|
|
|
2150
|
-
|
|
2000
|
+
return arrayToMap.map((item, index) => {
|
|
2001
|
+
const itemId = itemIdsForCurrentArray[index] || `id:${item.id}`;
|
|
2002
|
+
const itemPath = [...path, itemId];
|
|
2003
|
+
const itemSetter = rebuildStateShape(item, itemPath, meta);
|
|
2151
2004
|
return callbackfn(
|
|
2152
2005
|
item,
|
|
2153
|
-
|
|
2006
|
+
itemSetter,
|
|
2154
2007
|
index,
|
|
2155
|
-
currentState
|
|
2156
|
-
|
|
2008
|
+
currentState,
|
|
2009
|
+
arraySetter
|
|
2157
2010
|
);
|
|
2158
2011
|
});
|
|
2159
2012
|
};
|
|
2160
2013
|
}
|
|
2161
|
-
if (prop === "
|
|
2014
|
+
if (prop === "stateMapNoRender") {
|
|
2162
2015
|
return (
|
|
2163
2016
|
callbackfn: (
|
|
2164
|
-
value:
|
|
2165
|
-
setter:
|
|
2017
|
+
value: any,
|
|
2018
|
+
setter: any,
|
|
2166
2019
|
index: number,
|
|
2167
|
-
array:
|
|
2168
|
-
arraySetter:
|
|
2020
|
+
array: any,
|
|
2021
|
+
arraySetter: any
|
|
2169
2022
|
) => void
|
|
2170
2023
|
) => {
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
_mapFn: callbackfn as any, // Pass the actual function, not string
|
|
2176
|
-
},
|
|
2024
|
+
const arrayToMap = currentState as any[];
|
|
2025
|
+
const itemIdsForCurrentArray =
|
|
2026
|
+
meta?.validIds || getOrderedIds(path) || [];
|
|
2027
|
+
const arraySetter = rebuildStateShape(currentState, path, meta);
|
|
2177
2028
|
|
|
2178
|
-
|
|
2029
|
+
return arrayToMap.map((item, index) => {
|
|
2030
|
+
const itemId = itemIdsForCurrentArray[index] || `id:${item.id}`;
|
|
2031
|
+
const finalPath = [...path, itemId];
|
|
2032
|
+
const setter = rebuildStateShape(item, finalPath, meta);
|
|
2033
|
+
return callbackfn(
|
|
2034
|
+
item,
|
|
2035
|
+
setter,
|
|
2036
|
+
index,
|
|
2037
|
+
currentState,
|
|
2038
|
+
arraySetter
|
|
2039
|
+
);
|
|
2179
2040
|
});
|
|
2180
2041
|
};
|
|
2181
2042
|
}
|
|
2043
|
+
if (prop === "$stateMap") {
|
|
2044
|
+
return (callbackfn: any) =>
|
|
2045
|
+
createElement(SignalMapRenderer, {
|
|
2046
|
+
proxy: { _stateKey: stateKey, _path: path, _mapFn: callbackfn },
|
|
2047
|
+
rebuildStateShape,
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2182
2050
|
if (prop === "stateList") {
|
|
2183
2051
|
return (
|
|
2184
2052
|
callbackfn: (
|
|
2185
|
-
value:
|
|
2186
|
-
setter:
|
|
2053
|
+
value: any,
|
|
2054
|
+
setter: any,
|
|
2187
2055
|
index: { localIndex: number; originalIndex: number },
|
|
2188
|
-
array:
|
|
2189
|
-
arraySetter:
|
|
2190
|
-
) =>
|
|
2191
|
-
formOpts?: FormOptsType
|
|
2056
|
+
array: any,
|
|
2057
|
+
arraySetter: any
|
|
2058
|
+
) => ReactNode
|
|
2192
2059
|
) => {
|
|
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);
|
|
2060
|
+
const arrayToMap = currentState as any[];
|
|
2061
|
+
if (!Array.isArray(arrayToMap)) return null;
|
|
2062
|
+
|
|
2063
|
+
const itemIdsForCurrentArray =
|
|
2064
|
+
meta?.validIds || getOrderedIds(path) || [];
|
|
2065
|
+
const sourceIds = getOrderedIds(path) || [];
|
|
2066
|
+
const arraySetter = rebuildStateShape(
|
|
2067
|
+
arrayToMap as any,
|
|
2068
|
+
path,
|
|
2069
|
+
meta
|
|
2070
|
+
);
|
|
2207
2071
|
|
|
2208
|
-
return
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2072
|
+
return arrayToMap.map((item, localIndex) => {
|
|
2073
|
+
const itemId =
|
|
2074
|
+
itemIdsForCurrentArray[localIndex] || `id:${item.id}`;
|
|
2075
|
+
const originalIndex = sourceIds.indexOf(itemId);
|
|
2076
|
+
const finalPath = [...path, itemId];
|
|
2211
2077
|
const setter = rebuildStateShape(item, finalPath, meta);
|
|
2212
|
-
const itemComponentId = `${componentId}-${path.join(".")}-${
|
|
2078
|
+
const itemComponentId = `${componentId}-${path.join(".")}-${itemId}`;
|
|
2213
2079
|
|
|
2214
2080
|
return createElement(CogsItemWrapper, {
|
|
2215
|
-
key:
|
|
2081
|
+
key: itemId,
|
|
2216
2082
|
stateKey,
|
|
2217
2083
|
itemComponentId,
|
|
2218
|
-
formOpts,
|
|
2219
2084
|
itemPath: finalPath,
|
|
2220
2085
|
children: callbackfn(
|
|
2221
2086
|
item,
|
|
2222
2087
|
setter,
|
|
2223
2088
|
{ localIndex, originalIndex },
|
|
2224
2089
|
arrayToMap as any,
|
|
2225
|
-
|
|
2090
|
+
arraySetter
|
|
2226
2091
|
),
|
|
2227
2092
|
});
|
|
2228
2093
|
});
|
|
@@ -2243,15 +2108,30 @@ function createProxyHandler<T>(
|
|
|
2243
2108
|
);
|
|
2244
2109
|
};
|
|
2245
2110
|
}
|
|
2246
|
-
|
|
2247
2111
|
if (prop === "index") {
|
|
2248
2112
|
return (index: number) => {
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2113
|
+
const idList = meta?.validIds || getOrderedIds(path);
|
|
2114
|
+
const itemId = idList?.[index];
|
|
2115
|
+
|
|
2116
|
+
if (!itemId) {
|
|
2117
|
+
return rebuildStateShape(undefined as T, [
|
|
2118
|
+
...path,
|
|
2119
|
+
index.toString(),
|
|
2120
|
+
]);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
const sourceArray = getGlobalStore
|
|
2124
|
+
.getState()
|
|
2125
|
+
.getNestedState(stateKey, path) as any[];
|
|
2126
|
+
const itemData = sourceArray.find(
|
|
2127
|
+
(item) => `id:${item.id}` === itemId
|
|
2128
|
+
);
|
|
2129
|
+
|
|
2130
|
+
const itemPath = [...path, itemId];
|
|
2131
|
+
return rebuildStateShape(itemData, itemPath, meta);
|
|
2251
2132
|
};
|
|
2252
2133
|
}
|
|
2253
2134
|
if (prop === "last") {
|
|
2254
|
-
// Added handler for 'last'
|
|
2255
2135
|
return () => {
|
|
2256
2136
|
const currentArray = getGlobalStore
|
|
2257
2137
|
.getState()
|
|
@@ -2260,14 +2140,11 @@ function createProxyHandler<T>(
|
|
|
2260
2140
|
const lastIndex = currentArray.length - 1;
|
|
2261
2141
|
const lastValue = currentArray[lastIndex];
|
|
2262
2142
|
const newPath = [...path, lastIndex.toString()];
|
|
2263
|
-
// shapeCache.clear(); // Decide if you need cache invalidation for 'last' access
|
|
2264
|
-
// stateVersion++;
|
|
2265
2143
|
return rebuildStateShape(lastValue, newPath);
|
|
2266
2144
|
};
|
|
2267
2145
|
}
|
|
2268
2146
|
if (prop === "insert") {
|
|
2269
2147
|
return (payload: UpdateArg<T>) => {
|
|
2270
|
-
// ADDED: Invalidate cache on insert
|
|
2271
2148
|
invalidateCachePath(path);
|
|
2272
2149
|
pushFunc(effectiveSetState, payload, path, stateKey);
|
|
2273
2150
|
return rebuildStateShape(
|
|
@@ -2276,7 +2153,6 @@ function createProxyHandler<T>(
|
|
|
2276
2153
|
);
|
|
2277
2154
|
};
|
|
2278
2155
|
}
|
|
2279
|
-
|
|
2280
2156
|
if (prop === "uniqueInsert") {
|
|
2281
2157
|
return (
|
|
2282
2158
|
payload: UpdateArg<T>,
|
|
@@ -2292,19 +2168,12 @@ function createProxyHandler<T>(
|
|
|
2292
2168
|
|
|
2293
2169
|
let matchedItem: any = null;
|
|
2294
2170
|
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
|
-
}
|
|
2171
|
+
const isMatch = fields
|
|
2172
|
+
? fields.every((field) =>
|
|
2173
|
+
isDeepEqual(item[field], newValue[field])
|
|
2174
|
+
)
|
|
2175
|
+
: isDeepEqual(item, newValue);
|
|
2176
|
+
if (isMatch) matchedItem = item;
|
|
2308
2177
|
return isMatch;
|
|
2309
2178
|
});
|
|
2310
2179
|
|
|
@@ -2321,11 +2190,9 @@ function createProxyHandler<T>(
|
|
|
2321
2190
|
}
|
|
2322
2191
|
};
|
|
2323
2192
|
}
|
|
2324
|
-
|
|
2325
2193
|
if (prop === "cut") {
|
|
2326
2194
|
return (index: number, options?: { waitForSync?: boolean }) => {
|
|
2327
2195
|
if (options?.waitForSync) return;
|
|
2328
|
-
// ADDED: Invalidate cache on cut
|
|
2329
2196
|
invalidateCachePath(path);
|
|
2330
2197
|
cutFunc(effectiveSetState, path, stateKey, index);
|
|
2331
2198
|
return rebuildStateShape(
|
|
@@ -2336,51 +2203,70 @@ function createProxyHandler<T>(
|
|
|
2336
2203
|
}
|
|
2337
2204
|
if (prop === "cutByValue") {
|
|
2338
2205
|
return (value: string | number | boolean) => {
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
cutFunc(effectiveSetState, path, stateKey, index);
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2206
|
+
const index = currentState.findIndex((item) => item === value);
|
|
2207
|
+
if (index > -1) cutFunc(effectiveSetState, path, stateKey, index);
|
|
2344
2208
|
};
|
|
2345
2209
|
}
|
|
2346
2210
|
if (prop === "toggleByValue") {
|
|
2347
2211
|
return (value: string | number | boolean) => {
|
|
2348
2212
|
const index = currentState.findIndex((item) => item === value);
|
|
2349
2213
|
if (index > -1) {
|
|
2350
|
-
// Value exists, so cut it
|
|
2351
2214
|
cutFunc(effectiveSetState, path, stateKey, index);
|
|
2352
2215
|
} else {
|
|
2353
|
-
// Value doesn't exist, so insert it
|
|
2354
2216
|
pushFunc(effectiveSetState, value as any, path, stateKey);
|
|
2355
2217
|
}
|
|
2356
2218
|
};
|
|
2357
2219
|
}
|
|
2358
|
-
if (prop === "
|
|
2359
|
-
return (
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
const found = sourceWithIndices.find(({ item }, index) =>
|
|
2367
|
-
callbackfn(item, index)
|
|
2220
|
+
if (prop === "stateFilter") {
|
|
2221
|
+
return (callbackfn: (value: any, index: number) => boolean) => {
|
|
2222
|
+
const sourceIds = meta?.validIds || getOrderedIds(path) || [];
|
|
2223
|
+
const sourceArray = getGlobalStore
|
|
2224
|
+
.getState()
|
|
2225
|
+
.getNestedState(stateKey, path) as any[];
|
|
2226
|
+
const sourceMap = new Map(
|
|
2227
|
+
sourceArray.map((item) => [`id:${item.id}`, item])
|
|
2368
2228
|
);
|
|
2369
|
-
|
|
2370
|
-
const
|
|
2371
|
-
|
|
2229
|
+
|
|
2230
|
+
const newValidIds: string[] = [];
|
|
2231
|
+
const newFilteredArray: any[] = [];
|
|
2232
|
+
|
|
2233
|
+
sourceIds.forEach((id, index) => {
|
|
2234
|
+
const item = sourceMap.get(id);
|
|
2235
|
+
if (item && callbackfn(item, index)) {
|
|
2236
|
+
newValidIds.push(id);
|
|
2237
|
+
newFilteredArray.push(item);
|
|
2238
|
+
}
|
|
2239
|
+
});
|
|
2240
|
+
|
|
2241
|
+
return rebuildStateShape(newFilteredArray as any, path, {
|
|
2242
|
+
validIds: newValidIds,
|
|
2243
|
+
});
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
if (prop === "stateSort") {
|
|
2247
|
+
return (compareFn: (a: any, b: any) => number) => {
|
|
2248
|
+
const sourceArray = currentState as any[];
|
|
2249
|
+
const itemsWithIds = sourceArray.map((item) => ({
|
|
2250
|
+
item,
|
|
2251
|
+
id: `id:${item.id}`,
|
|
2252
|
+
}));
|
|
2253
|
+
itemsWithIds.sort((a, b) => compareFn(a.item, b.item));
|
|
2254
|
+
const sortedArray = itemsWithIds.map((d) => d.item);
|
|
2255
|
+
const newValidIds = itemsWithIds.map((d) => d.id);
|
|
2256
|
+
return rebuildStateShape(sortedArray as any, path, {
|
|
2257
|
+
validIds: newValidIds,
|
|
2258
|
+
});
|
|
2372
2259
|
};
|
|
2373
2260
|
}
|
|
2374
|
-
|
|
2375
2261
|
if (prop === "findWith") {
|
|
2376
2262
|
return (thisKey: keyof InferArrayElement<T>, thisValue: any) => {
|
|
2377
|
-
const
|
|
2378
|
-
|
|
2379
|
-
({ item }) => item[thisKey] === thisValue
|
|
2263
|
+
const foundItem = (currentState as any[]).find(
|
|
2264
|
+
(item) => item[thisKey] === thisValue
|
|
2380
2265
|
);
|
|
2381
|
-
if (!
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2266
|
+
if (!foundItem) return undefined;
|
|
2267
|
+
const itemId = `id:${foundItem.id}`;
|
|
2268
|
+
const finalPath = [...path, itemId];
|
|
2269
|
+
return rebuildStateShape(foundItem, finalPath, meta);
|
|
2384
2270
|
};
|
|
2385
2271
|
}
|
|
2386
2272
|
}
|
|
@@ -2390,7 +2276,6 @@ function createProxyHandler<T>(
|
|
|
2390
2276
|
const parentValue = getGlobalStore
|
|
2391
2277
|
.getState()
|
|
2392
2278
|
.getNestedState(stateKey, parentPath);
|
|
2393
|
-
|
|
2394
2279
|
if (Array.isArray(parentValue) && prop === "cut") {
|
|
2395
2280
|
return () =>
|
|
2396
2281
|
cutFunc(
|
|
@@ -2401,16 +2286,28 @@ function createProxyHandler<T>(
|
|
|
2401
2286
|
);
|
|
2402
2287
|
}
|
|
2403
2288
|
}
|
|
2404
|
-
|
|
2405
2289
|
if (prop === "get") {
|
|
2406
2290
|
return () => {
|
|
2407
|
-
if
|
|
2408
|
-
|
|
2409
|
-
|
|
2291
|
+
// Check if this proxy represents a derived array.
|
|
2292
|
+
// A derived array proxy has `meta.validIds` AND its `currentState` is an array.
|
|
2293
|
+
if (meta?.validIds && Array.isArray(currentState)) {
|
|
2294
|
+
// It IS a derived array proxy. Reconstruct it to ensure freshness.
|
|
2295
|
+
const sourceArray = getGlobalStore
|
|
2410
2296
|
.getState()
|
|
2411
2297
|
.getNestedState(stateKey, path) as any[];
|
|
2412
|
-
|
|
2298
|
+
if (!Array.isArray(sourceArray)) return [];
|
|
2299
|
+
|
|
2300
|
+
const sourceMap = new Map(
|
|
2301
|
+
sourceArray.map((item: any) => [`id:${item.id}`, item])
|
|
2302
|
+
);
|
|
2303
|
+
|
|
2304
|
+
return meta.validIds
|
|
2305
|
+
.map((id) => sourceMap.get(id))
|
|
2306
|
+
.filter(Boolean);
|
|
2413
2307
|
}
|
|
2308
|
+
|
|
2309
|
+
// For all other cases (non-derived arrays, single items, properties),
|
|
2310
|
+
// the standard lookup is correct.
|
|
2414
2311
|
return getGlobalStore.getState().getNestedState(stateKey, path);
|
|
2415
2312
|
};
|
|
2416
2313
|
}
|
|
@@ -2422,19 +2319,13 @@ function createProxyHandler<T>(
|
|
|
2422
2319
|
_effect: fn.toString(),
|
|
2423
2320
|
});
|
|
2424
2321
|
}
|
|
2425
|
-
|
|
2426
2322
|
if (prop === "$get") {
|
|
2427
|
-
return () =>
|
|
2428
|
-
$cogsSignal({
|
|
2429
|
-
_stateKey: stateKey,
|
|
2430
|
-
_path: path,
|
|
2431
|
-
});
|
|
2323
|
+
return () => $cogsSignal({ _stateKey: stateKey, _path: path });
|
|
2432
2324
|
}
|
|
2433
2325
|
if (prop === "lastSynced") {
|
|
2434
2326
|
const syncKey = `${stateKey}:${path.join(".")}`;
|
|
2435
2327
|
return getGlobalStore.getState().getSyncInfo(syncKey);
|
|
2436
2328
|
}
|
|
2437
|
-
|
|
2438
2329
|
if (prop == "getLocalStorage") {
|
|
2439
2330
|
return (key: string) =>
|
|
2440
2331
|
loadFromLocalStorage(sessionId + "-" + stateKey + "-" + key);
|
|
@@ -2442,13 +2333,16 @@ function createProxyHandler<T>(
|
|
|
2442
2333
|
if (prop === "_selected") {
|
|
2443
2334
|
const parentPath = path.slice(0, -1);
|
|
2444
2335
|
const parentKey = parentPath.join(".");
|
|
2445
|
-
|
|
2446
|
-
.
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2336
|
+
if (
|
|
2337
|
+
Array.isArray(
|
|
2338
|
+
getGlobalStore.getState().getNestedState(stateKey, parentPath)
|
|
2339
|
+
)
|
|
2340
|
+
) {
|
|
2341
|
+
const itemId = path[path.length - 1];
|
|
2342
|
+
const orderedIds = getOrderedIds(parentPath);
|
|
2343
|
+
const thisIndex = orderedIds?.indexOf(itemId!);
|
|
2450
2344
|
return (
|
|
2451
|
-
|
|
2345
|
+
thisIndex ===
|
|
2452
2346
|
getGlobalStore.getState().getSelectedIndex(stateKey, parentKey)
|
|
2453
2347
|
);
|
|
2454
2348
|
}
|
|
@@ -2457,37 +2351,39 @@ function createProxyHandler<T>(
|
|
|
2457
2351
|
if (prop === "setSelected") {
|
|
2458
2352
|
return (value: boolean) => {
|
|
2459
2353
|
const parentPath = path.slice(0, -1);
|
|
2460
|
-
const
|
|
2461
|
-
const
|
|
2354
|
+
const itemId = path[path.length - 1];
|
|
2355
|
+
const orderedIds = getOrderedIds(parentPath);
|
|
2356
|
+
const thisIndex = orderedIds?.indexOf(itemId!);
|
|
2462
2357
|
|
|
2463
|
-
if (
|
|
2464
|
-
getGlobalStore
|
|
2465
|
-
.getState()
|
|
2466
|
-
.setSelectedIndex(stateKey, parentKey, thisIndex);
|
|
2467
|
-
} else {
|
|
2468
|
-
getGlobalStore
|
|
2469
|
-
.getState()
|
|
2470
|
-
.setSelectedIndex(stateKey, parentKey, undefined);
|
|
2471
|
-
}
|
|
2358
|
+
if (thisIndex === undefined || thisIndex === -1) return;
|
|
2472
2359
|
|
|
2360
|
+
const parentKey = parentPath.join(".");
|
|
2361
|
+
getGlobalStore
|
|
2362
|
+
.getState()
|
|
2363
|
+
.setSelectedIndex(
|
|
2364
|
+
stateKey,
|
|
2365
|
+
parentKey,
|
|
2366
|
+
value ? thisIndex : undefined
|
|
2367
|
+
);
|
|
2473
2368
|
const nested = getGlobalStore
|
|
2474
2369
|
.getState()
|
|
2475
2370
|
.getNestedState(stateKey, [...parentPath]);
|
|
2476
2371
|
updateFn(effectiveSetState, nested, parentPath);
|
|
2477
|
-
|
|
2478
|
-
// Invalidate cache for this path
|
|
2479
2372
|
invalidateCachePath(parentPath);
|
|
2480
2373
|
};
|
|
2481
2374
|
}
|
|
2482
2375
|
if (prop === "toggleSelected") {
|
|
2483
2376
|
return () => {
|
|
2484
2377
|
const parentPath = path.slice(0, -1);
|
|
2485
|
-
const
|
|
2378
|
+
const itemId = path[path.length - 1];
|
|
2379
|
+
const orderedIds = getOrderedIds(parentPath);
|
|
2380
|
+
const thisIndex = orderedIds?.indexOf(itemId!);
|
|
2381
|
+
if (thisIndex === undefined || thisIndex === -1) return;
|
|
2382
|
+
|
|
2486
2383
|
const parentKey = parentPath.join(".");
|
|
2487
2384
|
const selectedIndex = getGlobalStore
|
|
2488
2385
|
.getState()
|
|
2489
2386
|
.getSelectedIndex(stateKey, parentKey);
|
|
2490
|
-
|
|
2491
2387
|
getGlobalStore
|
|
2492
2388
|
.getState()
|
|
2493
2389
|
.setSelectedIndex(
|
|
@@ -2495,11 +2391,11 @@ function createProxyHandler<T>(
|
|
|
2495
2391
|
parentKey,
|
|
2496
2392
|
selectedIndex === thisIndex ? undefined : thisIndex
|
|
2497
2393
|
);
|
|
2394
|
+
|
|
2498
2395
|
const nested = getGlobalStore
|
|
2499
2396
|
.getState()
|
|
2500
2397
|
.getNestedState(stateKey, [...parentPath]);
|
|
2501
2398
|
updateFn(effectiveSetState, nested, parentPath);
|
|
2502
|
-
|
|
2503
2399
|
invalidateCachePath(parentPath);
|
|
2504
2400
|
};
|
|
2505
2401
|
}
|
|
@@ -2509,34 +2405,20 @@ function createProxyHandler<T>(
|
|
|
2509
2405
|
const init = getGlobalStore
|
|
2510
2406
|
.getState()
|
|
2511
2407
|
.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
|
|
2408
|
+
if (!init?.key) throw new Error("Validation key not found");
|
|
2518
2409
|
removeValidationError(init.key);
|
|
2519
|
-
console.log("addValidationError", errors);
|
|
2520
|
-
// Add each new error
|
|
2521
2410
|
errors.forEach((error) => {
|
|
2522
2411
|
const fullErrorPath = [init.key, ...error.path].join(".");
|
|
2523
|
-
console.log("fullErrorPath", fullErrorPath);
|
|
2524
2412
|
addValidationError(fullErrorPath, error.message);
|
|
2525
2413
|
});
|
|
2526
|
-
|
|
2527
|
-
// Notify components to update
|
|
2528
2414
|
notifyComponents(stateKey);
|
|
2529
2415
|
};
|
|
2530
2416
|
}
|
|
2531
2417
|
if (prop === "applyJsonPatch") {
|
|
2532
2418
|
return (patches: any[]) => {
|
|
2533
|
-
// This part is correct.
|
|
2534
2419
|
const currentState =
|
|
2535
2420
|
getGlobalStore.getState().cogsStateStore[stateKey];
|
|
2536
|
-
const
|
|
2537
|
-
const newState = patchResult.newDocument;
|
|
2538
|
-
|
|
2539
|
-
// This is also correct.
|
|
2421
|
+
const newState = applyPatch(currentState, patches).newDocument;
|
|
2540
2422
|
updateGlobalState(
|
|
2541
2423
|
stateKey,
|
|
2542
2424
|
getGlobalStore.getState().initialStateGlobal[stateKey],
|
|
@@ -2545,105 +2427,7 @@ function createProxyHandler<T>(
|
|
|
2545
2427
|
componentId,
|
|
2546
2428
|
sessionId
|
|
2547
2429
|
);
|
|
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
|
-
}
|
|
2430
|
+
notifyComponents(stateKey); // Simplified notification
|
|
2647
2431
|
};
|
|
2648
2432
|
}
|
|
2649
2433
|
if (prop === "validateZodSchema") {
|
|
@@ -2651,75 +2435,31 @@ function createProxyHandler<T>(
|
|
|
2651
2435
|
const init = getGlobalStore
|
|
2652
2436
|
.getState()
|
|
2653
2437
|
.getInitialOptions(stateKey)?.validation;
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
if (!init?.zodSchema) {
|
|
2658
|
-
throw new Error("Zod schema not found");
|
|
2659
|
-
}
|
|
2438
|
+
if (!init?.zodSchema || !init?.key)
|
|
2439
|
+
throw new Error("Zod schema or validation key not found");
|
|
2660
2440
|
|
|
2661
|
-
if (!init?.key) {
|
|
2662
|
-
throw new Error("Validation key not found");
|
|
2663
|
-
}
|
|
2664
2441
|
removeValidationError(init.key);
|
|
2665
2442
|
const thisObject =
|
|
2666
2443
|
getGlobalStore.getState().cogsStateStore[stateKey];
|
|
2444
|
+
const result = init.zodSchema.safeParse(thisObject);
|
|
2667
2445
|
|
|
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);
|
|
2446
|
+
if (!result.success) {
|
|
2447
|
+
result.error.errors.forEach((error) => {
|
|
2448
|
+
const fullErrorPath = [init.key, ...error.path].join(".");
|
|
2449
|
+
addValidationError(fullErrorPath, error.message);
|
|
2450
|
+
});
|
|
2451
|
+
notifyComponents(stateKey);
|
|
2709
2452
|
return false;
|
|
2710
2453
|
}
|
|
2454
|
+
return true;
|
|
2711
2455
|
};
|
|
2712
2456
|
}
|
|
2713
2457
|
if (prop === "_componentId") return componentId;
|
|
2714
|
-
if (prop === "getComponents")
|
|
2458
|
+
if (prop === "getComponents")
|
|
2715
2459
|
return () => getGlobalStore().stateComponents.get(stateKey);
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
return formRefStore.getState().getFormRefsByStateKey(stateKey);
|
|
2720
|
-
};
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2460
|
+
if (prop === "getAllFormRefs")
|
|
2461
|
+
return () =>
|
|
2462
|
+
formRefStore.getState().getFormRefsByStateKey(stateKey);
|
|
2723
2463
|
if (prop === "_initialState")
|
|
2724
2464
|
return getGlobalStore.getState().initialStateGlobal[stateKey];
|
|
2725
2465
|
if (prop === "_serverState")
|
|
@@ -2732,13 +2472,9 @@ function createProxyHandler<T>(
|
|
|
2732
2472
|
if (prop === "removeValidation") return baseObj.removeValidation;
|
|
2733
2473
|
}
|
|
2734
2474
|
if (prop === "getFormRef") {
|
|
2735
|
-
return () =>
|
|
2736
|
-
|
|
2737
|
-
.getState()
|
|
2738
|
-
.getFormRef(stateKey + "." + path.join("."));
|
|
2739
|
-
};
|
|
2475
|
+
return () =>
|
|
2476
|
+
formRefStore.getState().getFormRef(stateKey + "." + path.join("."));
|
|
2740
2477
|
}
|
|
2741
|
-
|
|
2742
2478
|
if (prop === "validationWrapper") {
|
|
2743
2479
|
return ({
|
|
2744
2480
|
children,
|
|
@@ -2753,20 +2489,16 @@ function createProxyHandler<T>(
|
|
|
2753
2489
|
}
|
|
2754
2490
|
path={path}
|
|
2755
2491
|
stateKey={stateKey}
|
|
2756
|
-
validIndices={meta?.validIndices}
|
|
2757
2492
|
>
|
|
2758
2493
|
{children}
|
|
2759
2494
|
</ValidationWrapper>
|
|
2760
2495
|
);
|
|
2761
2496
|
}
|
|
2762
|
-
|
|
2763
2497
|
if (prop === "_stateKey") return stateKey;
|
|
2764
2498
|
if (prop === "_path") return path;
|
|
2765
2499
|
if (prop === "_isServerSynced") return baseObj._isServerSynced;
|
|
2766
|
-
|
|
2767
2500
|
if (prop === "update") {
|
|
2768
2501
|
return (payload: UpdateArg<T>, opts?: UpdateOpts<T>) => {
|
|
2769
|
-
// ADDED: Invalidate cache on update
|
|
2770
2502
|
if (opts?.debounce) {
|
|
2771
2503
|
debounce(() => {
|
|
2772
2504
|
updateFn(effectiveSetState, payload, path, "");
|
|
@@ -2785,19 +2517,16 @@ function createProxyHandler<T>(
|
|
|
2785
2517
|
invalidateCachePath(path);
|
|
2786
2518
|
};
|
|
2787
2519
|
}
|
|
2788
|
-
|
|
2789
2520
|
if (prop === "formElement") {
|
|
2790
|
-
return (child: FormControl<T>, formOpts?: FormOptsType) =>
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
);
|
|
2800
|
-
};
|
|
2521
|
+
return (child: FormControl<T>, formOpts?: FormOptsType) => (
|
|
2522
|
+
<FormControlComponent<T>
|
|
2523
|
+
setState={effectiveSetState}
|
|
2524
|
+
stateKey={stateKey}
|
|
2525
|
+
path={path}
|
|
2526
|
+
child={child}
|
|
2527
|
+
formOpts={formOpts}
|
|
2528
|
+
/>
|
|
2529
|
+
);
|
|
2801
2530
|
}
|
|
2802
2531
|
|
|
2803
2532
|
const nextPath = [...path, prop];
|
|
@@ -2809,12 +2538,10 @@ function createProxyHandler<T>(
|
|
|
2809
2538
|
};
|
|
2810
2539
|
|
|
2811
2540
|
const proxyInstance = new Proxy(baseFunction, handler);
|
|
2812
|
-
|
|
2813
2541
|
shapeCache.set(cacheKey, {
|
|
2814
2542
|
proxy: proxyInstance,
|
|
2815
2543
|
stateVersion: stateVersion,
|
|
2816
2544
|
});
|
|
2817
|
-
|
|
2818
2545
|
return proxyInstance;
|
|
2819
2546
|
}
|
|
2820
2547
|
|
|
@@ -2833,7 +2560,6 @@ export function $cogsSignal(proxy: {
|
|
|
2833
2560
|
|
|
2834
2561
|
function SignalMapRenderer({
|
|
2835
2562
|
proxy,
|
|
2836
|
-
|
|
2837
2563
|
rebuildStateShape,
|
|
2838
2564
|
}: {
|
|
2839
2565
|
proxy: {
|
|
@@ -2847,30 +2573,26 @@ function SignalMapRenderer({
|
|
|
2847
2573
|
arraySetter: any
|
|
2848
2574
|
) => ReactNode;
|
|
2849
2575
|
};
|
|
2850
|
-
|
|
2851
2576
|
rebuildStateShape: (
|
|
2852
2577
|
currentState: any,
|
|
2853
2578
|
path: string[],
|
|
2854
|
-
meta?: {
|
|
2579
|
+
meta?: { validIds?: string[] }
|
|
2855
2580
|
) => any;
|
|
2856
2581
|
}) {
|
|
2857
2582
|
const value = getGlobalStore().getNestedState(proxy._stateKey, proxy._path);
|
|
2583
|
+
if (!Array.isArray(value)) return null;
|
|
2858
2584
|
|
|
2859
|
-
if (!Array.isArray(value)) {
|
|
2860
|
-
return null;
|
|
2861
|
-
}
|
|
2862
2585
|
const arraySetter = rebuildStateShape(
|
|
2863
2586
|
value,
|
|
2864
2587
|
proxy._path
|
|
2865
2588
|
) as ArrayEndType<any>;
|
|
2866
|
-
// Use existing global state management
|
|
2867
2589
|
return arraySetter.stateMapNoRender(
|
|
2868
|
-
(item, setter, index,
|
|
2869
|
-
|
|
2870
|
-
return proxy._mapFn(item, setter, index, value, arraysetter);
|
|
2590
|
+
(item, setter, index, array, arraySetter) => {
|
|
2591
|
+
return proxy._mapFn(item, setter, index, array, arraySetter);
|
|
2871
2592
|
}
|
|
2872
2593
|
);
|
|
2873
2594
|
}
|
|
2595
|
+
|
|
2874
2596
|
function SignalRenderer({
|
|
2875
2597
|
proxy,
|
|
2876
2598
|
}: {
|
|
@@ -2907,12 +2629,10 @@ function SignalRenderer({
|
|
|
2907
2629
|
|
|
2908
2630
|
getGlobalStore.getState().addSignalElement(signalId, elementInfo);
|
|
2909
2631
|
|
|
2910
|
-
// Get the raw value from the store
|
|
2911
2632
|
const value = getGlobalStore
|
|
2912
2633
|
.getState()
|
|
2913
2634
|
.getNestedState(proxy._stateKey, proxy._path);
|
|
2914
|
-
|
|
2915
|
-
let displayValue;
|
|
2635
|
+
let displayValue = value;
|
|
2916
2636
|
if (proxy._effect) {
|
|
2917
2637
|
try {
|
|
2918
2638
|
displayValue = new Function(
|
|
@@ -2920,11 +2640,8 @@ function SignalRenderer({
|
|
|
2920
2640
|
`return (${proxy._effect})(state)`
|
|
2921
2641
|
)(value);
|
|
2922
2642
|
} catch (err) {
|
|
2923
|
-
console.error("Error evaluating effect function
|
|
2924
|
-
displayValue = value; // Fallback to raw value
|
|
2643
|
+
console.error("Error evaluating effect function:", err);
|
|
2925
2644
|
}
|
|
2926
|
-
} else {
|
|
2927
|
-
displayValue = value;
|
|
2928
2645
|
}
|
|
2929
2646
|
|
|
2930
2647
|
if (displayValue !== null && typeof displayValue === "object") {
|
|
@@ -2941,6 +2658,7 @@ function SignalRenderer({
|
|
|
2941
2658
|
"data-signal-id": signalId,
|
|
2942
2659
|
});
|
|
2943
2660
|
}
|
|
2661
|
+
|
|
2944
2662
|
export function $cogsSignalStore(proxy: {
|
|
2945
2663
|
_path: string[];
|
|
2946
2664
|
_stateKey: string;
|
|
@@ -2949,13 +2667,14 @@ export function $cogsSignalStore(proxy: {
|
|
|
2949
2667
|
(notify) => {
|
|
2950
2668
|
const stateEntry = getGlobalStore
|
|
2951
2669
|
.getState()
|
|
2952
|
-
.stateComponents.get(proxy._stateKey) || {
|
|
2953
|
-
components: new Map(),
|
|
2954
|
-
};
|
|
2670
|
+
.stateComponents.get(proxy._stateKey) || { components: new Map() };
|
|
2955
2671
|
stateEntry.components.set(proxy._stateKey, {
|
|
2956
2672
|
forceUpdate: notify,
|
|
2957
2673
|
paths: new Set([proxy._path.join(".")]),
|
|
2958
2674
|
});
|
|
2675
|
+
getGlobalStore
|
|
2676
|
+
.getState()
|
|
2677
|
+
.stateComponents.set(proxy._stateKey, stateEntry);
|
|
2959
2678
|
return () => stateEntry.components.delete(proxy._stateKey);
|
|
2960
2679
|
},
|
|
2961
2680
|
() => getGlobalStore.getState().getNestedState(proxy._stateKey, proxy._path)
|
|
@@ -2963,12 +2682,10 @@ export function $cogsSignalStore(proxy: {
|
|
|
2963
2682
|
return createElement("text", {}, String(value));
|
|
2964
2683
|
}
|
|
2965
2684
|
|
|
2966
|
-
// Modified CogsItemWrapper that stores the DOM ref
|
|
2967
2685
|
function CogsItemWrapper({
|
|
2968
2686
|
stateKey,
|
|
2969
2687
|
itemComponentId,
|
|
2970
2688
|
itemPath,
|
|
2971
|
-
formOpts,
|
|
2972
2689
|
children,
|
|
2973
2690
|
}: {
|
|
2974
2691
|
stateKey: string;
|
|
@@ -2977,16 +2694,11 @@ function CogsItemWrapper({
|
|
|
2977
2694
|
formOpts?: FormOptsType;
|
|
2978
2695
|
children: React.ReactNode;
|
|
2979
2696
|
}) {
|
|
2980
|
-
// This hook handles the re-rendering when the item's own data changes.
|
|
2981
2697
|
const [, forceUpdate] = useState({});
|
|
2982
|
-
// This hook measures the element.
|
|
2983
2698
|
const [measureRef, bounds] = useMeasure();
|
|
2984
|
-
// Store the actual DOM element
|
|
2985
2699
|
const elementRef = useRef<HTMLDivElement | null>(null);
|
|
2986
|
-
// This ref prevents sending the same height update repeatedly.
|
|
2987
2700
|
const lastReportedHeight = useRef<number | null>(null);
|
|
2988
2701
|
|
|
2989
|
-
// Combine both refs
|
|
2990
2702
|
const setRefs = useCallback(
|
|
2991
2703
|
(element: HTMLDivElement | null) => {
|
|
2992
2704
|
measureRef(element);
|
|
@@ -2995,50 +2707,32 @@ function CogsItemWrapper({
|
|
|
2995
2707
|
[measureRef]
|
|
2996
2708
|
);
|
|
2997
2709
|
|
|
2998
|
-
// This is the primary effect for this component.
|
|
2999
2710
|
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
2711
|
if (bounds.height > 0 && bounds.height !== lastReportedHeight.current) {
|
|
3003
|
-
// Store the new height so we don't report it again.
|
|
3004
2712
|
lastReportedHeight.current = bounds.height;
|
|
3005
|
-
|
|
3006
|
-
// Call the store function to save the height AND the ref
|
|
3007
2713
|
getGlobalStore.getState().setShadowMetadata(stateKey, itemPath, {
|
|
3008
|
-
virtualizer: {
|
|
3009
|
-
itemHeight: bounds.height,
|
|
3010
|
-
domRef: elementRef.current, // Store the actual DOM element reference
|
|
3011
|
-
},
|
|
2714
|
+
virtualizer: { itemHeight: bounds.height, domRef: elementRef.current },
|
|
3012
2715
|
});
|
|
3013
2716
|
}
|
|
3014
|
-
}, [bounds.height, stateKey, itemPath]);
|
|
2717
|
+
}, [bounds.height, stateKey, itemPath]);
|
|
3015
2718
|
|
|
3016
|
-
// This effect handles subscribing the item to its own data path for updates.
|
|
3017
2719
|
useLayoutEffect(() => {
|
|
3018
2720
|
const fullComponentId = `${stateKey}////${itemComponentId}`;
|
|
3019
2721
|
const stateEntry = getGlobalStore
|
|
3020
2722
|
.getState()
|
|
3021
|
-
.stateComponents.get(stateKey) || {
|
|
3022
|
-
components: new Map(),
|
|
3023
|
-
};
|
|
3024
|
-
|
|
2723
|
+
.stateComponents.get(stateKey) || { components: new Map() };
|
|
3025
2724
|
stateEntry.components.set(fullComponentId, {
|
|
3026
2725
|
forceUpdate: () => forceUpdate({}),
|
|
3027
2726
|
paths: new Set([itemPath.join(".")]),
|
|
3028
2727
|
});
|
|
3029
|
-
|
|
3030
2728
|
getGlobalStore.getState().stateComponents.set(stateKey, stateEntry);
|
|
3031
|
-
|
|
3032
2729
|
return () => {
|
|
3033
2730
|
const currentEntry = getGlobalStore
|
|
3034
2731
|
.getState()
|
|
3035
2732
|
.stateComponents.get(stateKey);
|
|
3036
|
-
if (currentEntry)
|
|
3037
|
-
currentEntry.components.delete(fullComponentId);
|
|
3038
|
-
}
|
|
2733
|
+
if (currentEntry) currentEntry.components.delete(fullComponentId);
|
|
3039
2734
|
};
|
|
3040
2735
|
}, [stateKey, itemComponentId, itemPath.join(".")]);
|
|
3041
2736
|
|
|
3042
|
-
// The rendered output is a simple div that gets measured.
|
|
3043
2737
|
return <div ref={setRefs}>{children}</div>;
|
|
3044
2738
|
}
|