cogsbox-state 0.5.388 → 0.5.390
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 +5 -0
- package/dist/CogsState.jsx +564 -550
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +59 -21
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -261,8 +261,13 @@ export type ObjectEndType<T> = EndType<T> & {
|
|
|
261
261
|
stateObject: (callbackfn: (value: T, setter: StateObject<T>) => void) => any;
|
|
262
262
|
delete: () => void;
|
|
263
263
|
};
|
|
264
|
+
export type ValidationError = {
|
|
265
|
+
path: (string | number)[];
|
|
266
|
+
message: string;
|
|
267
|
+
};
|
|
264
268
|
type EffectFunction<T, R> = (state: T) => R;
|
|
265
269
|
export type EndType<T, IsArrayElement = false> = {
|
|
270
|
+
addValidation: (errors: ValidationError[]) => void;
|
|
266
271
|
applyJsonPatch: (patches: any[]) => void;
|
|
267
272
|
update: UpdateType<T>;
|
|
268
273
|
_path: string[];
|
|
@@ -1885,26 +1890,37 @@ function createProxyHandler<T>(
|
|
|
1885
1890
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1886
1891
|
|
|
1887
1892
|
// --- 1. STATE CONTROLLER ---
|
|
1888
|
-
//
|
|
1893
|
+
// --- 1. STATE CONTROLLER (CORRECTED) ---
|
|
1889
1894
|
useLayoutEffect(() => {
|
|
1890
|
-
// THIS `IF` BLOCK IS THE NEW LOGIC
|
|
1891
1895
|
const container = containerRef.current;
|
|
1896
|
+
if (!container) return;
|
|
1897
|
+
|
|
1892
1898
|
const hasNewItems = totalCount > prevTotalCountRef.current;
|
|
1893
1899
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1900
|
+
// THIS IS THE NEW, IMPORTANT LOGIC FOR ANCHORING
|
|
1901
|
+
if (hasNewItems && scrollAnchorRef.current) {
|
|
1896
1902
|
const { top: prevScrollTop, height: prevScrollHeight } =
|
|
1897
1903
|
scrollAnchorRef.current;
|
|
1904
|
+
|
|
1905
|
+
// This is the key: Tell the app we are about to programmatically scroll.
|
|
1906
|
+
isProgrammaticScroll.current = true;
|
|
1907
|
+
|
|
1908
|
+
// Restore the scroll position.
|
|
1898
1909
|
container.scrollTop =
|
|
1899
1910
|
prevScrollTop + (container.scrollHeight - prevScrollHeight);
|
|
1900
|
-
|
|
1901
|
-
// IMPORTANT: Clear the anchor after using it.
|
|
1902
|
-
scrollAnchorRef.current = null;
|
|
1903
1911
|
console.log(
|
|
1904
1912
|
`ANCHOR RESTORED to scrollTop: ${container.scrollTop}`
|
|
1905
1913
|
);
|
|
1914
|
+
|
|
1915
|
+
// IMPORTANT: After the scroll, allow user scroll events again.
|
|
1916
|
+
// Use a timeout to ensure this runs after the scroll event has fired and been ignored.
|
|
1917
|
+
setTimeout(() => {
|
|
1918
|
+
isProgrammaticScroll.current = false;
|
|
1919
|
+
}, 100);
|
|
1920
|
+
|
|
1921
|
+
scrollAnchorRef.current = null; // Clear the anchor after using it.
|
|
1906
1922
|
}
|
|
1907
|
-
// YOUR
|
|
1923
|
+
// YOUR ORIGINAL LOGIC CONTINUES UNCHANGED IN THE `ELSE` BLOCK
|
|
1908
1924
|
else {
|
|
1909
1925
|
const depsChanged = !isDeepEqual(
|
|
1910
1926
|
dependencies,
|
|
@@ -1914,7 +1930,7 @@ function createProxyHandler<T>(
|
|
|
1914
1930
|
if (depsChanged) {
|
|
1915
1931
|
console.log("TRANSITION: Deps changed -> IDLE_AT_TOP");
|
|
1916
1932
|
setStatus("IDLE_AT_TOP");
|
|
1917
|
-
return;
|
|
1933
|
+
return;
|
|
1918
1934
|
}
|
|
1919
1935
|
|
|
1920
1936
|
if (
|
|
@@ -2021,35 +2037,35 @@ function createProxyHandler<T>(
|
|
|
2021
2037
|
const scrollThreshold = itemHeight;
|
|
2022
2038
|
|
|
2023
2039
|
const handleUserScroll = () => {
|
|
2040
|
+
// This guard is now critical. It will ignore our anchor restoration scroll.
|
|
2024
2041
|
if (isProgrammaticScroll.current) {
|
|
2025
2042
|
return;
|
|
2026
2043
|
}
|
|
2027
2044
|
|
|
2028
2045
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
2029
2046
|
|
|
2047
|
+
// Part 1: Handle Status and Anchoring
|
|
2030
2048
|
const isAtBottom =
|
|
2031
2049
|
scrollHeight - scrollTop - clientHeight < 10;
|
|
2032
2050
|
|
|
2033
2051
|
if (isAtBottom) {
|
|
2034
2052
|
if (status !== "LOCKED_AT_BOTTOM") {
|
|
2035
2053
|
setStatus("LOCKED_AT_BOTTOM");
|
|
2036
|
-
// We are at the bottom, so we don't need an anchor.
|
|
2037
|
-
scrollAnchorRef.current = null;
|
|
2038
2054
|
}
|
|
2055
|
+
// If we are at the bottom, there is no anchor needed.
|
|
2056
|
+
scrollAnchorRef.current = null;
|
|
2039
2057
|
} else {
|
|
2040
2058
|
if (status !== "IDLE_NOT_AT_BOTTOM") {
|
|
2041
2059
|
setStatus("IDLE_NOT_AT_BOTTOM");
|
|
2042
|
-
// THE USER SCROLLED UP. SET THE ANCHOR.
|
|
2043
|
-
scrollAnchorRef.current = {
|
|
2044
|
-
top: scrollTop,
|
|
2045
|
-
height: scrollHeight,
|
|
2046
|
-
};
|
|
2047
|
-
console.log(`ANCHOR SET at scrollTop: ${scrollTop}`);
|
|
2048
2060
|
}
|
|
2061
|
+
// User is scrolled up. Continuously update the anchor with their latest position.
|
|
2062
|
+
scrollAnchorRef.current = {
|
|
2063
|
+
top: scrollTop,
|
|
2064
|
+
height: scrollHeight,
|
|
2065
|
+
};
|
|
2049
2066
|
}
|
|
2050
|
-
// --- END OF MINIMAL FIX ---
|
|
2051
2067
|
|
|
2052
|
-
//
|
|
2068
|
+
// Part 2: YOUR original, working logic for updating the visible range.
|
|
2053
2069
|
if (
|
|
2054
2070
|
Math.abs(scrollTop - lastUpdateAtScrollTop.current) <
|
|
2055
2071
|
scrollThreshold
|
|
@@ -2061,7 +2077,7 @@ function createProxyHandler<T>(
|
|
|
2061
2077
|
`Threshold passed at ${scrollTop}px. Recalculating range...`
|
|
2062
2078
|
);
|
|
2063
2079
|
|
|
2064
|
-
//
|
|
2080
|
+
// ... your logic to find startIndex and endIndex ...
|
|
2065
2081
|
let high = totalCount - 1;
|
|
2066
2082
|
let low = 0;
|
|
2067
2083
|
let topItemIndex = 0;
|
|
@@ -2090,7 +2106,6 @@ function createProxyHandler<T>(
|
|
|
2090
2106
|
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
2091
2107
|
});
|
|
2092
2108
|
|
|
2093
|
-
// Finally, we record that we did the work at THIS scroll position.
|
|
2094
2109
|
lastUpdateAtScrollTop.current = scrollTop;
|
|
2095
2110
|
};
|
|
2096
2111
|
|
|
@@ -2648,6 +2663,29 @@ function createProxyHandler<T>(
|
|
|
2648
2663
|
};
|
|
2649
2664
|
}
|
|
2650
2665
|
if (path.length == 0) {
|
|
2666
|
+
if (prop === "addValidation") {
|
|
2667
|
+
return (errors: ValidationError[]) => {
|
|
2668
|
+
const init = getGlobalStore
|
|
2669
|
+
.getState()
|
|
2670
|
+
.getInitialOptions(stateKey)?.validation;
|
|
2671
|
+
|
|
2672
|
+
if (!init?.key) {
|
|
2673
|
+
throw new Error("Validation key not found");
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
// Clear existing errors for this validation key
|
|
2677
|
+
removeValidationError(init.key);
|
|
2678
|
+
|
|
2679
|
+
// Add each new error
|
|
2680
|
+
errors.forEach((error) => {
|
|
2681
|
+
const fullErrorPath = [init.key, ...error.path].join(".");
|
|
2682
|
+
addValidationError(fullErrorPath, error.message);
|
|
2683
|
+
});
|
|
2684
|
+
|
|
2685
|
+
// Notify components to update
|
|
2686
|
+
notifyComponents(stateKey);
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2651
2689
|
if (prop === "applyJsonPatch") {
|
|
2652
2690
|
return (patches: any[]) => {
|
|
2653
2691
|
// This part is correct.
|