@uniweb/runtime 0.6.18 → 0.6.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/runtime",
3
- "version": "0.6.18",
3
+ "version": "0.6.19",
4
4
  "description": "Minimal runtime for loading Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@vitejs/plugin-react": "^4.5.2",
43
43
  "vite": "^7.3.1",
44
- "@uniweb/build": "0.8.21"
44
+ "@uniweb/build": "0.8.22"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "react": "^18.0.0 || ^19.0.0",
@@ -11,9 +11,16 @@
11
11
  * - Scrolls to top on new navigation (PUSH/REPLACE)
12
12
  * - Skips when location.hash is present (defers to useLinkInterceptor)
13
13
  * - Optionally resets block states on scroll restoration
14
+ *
15
+ * Implementation note:
16
+ * Uniweb scrolls `window` (not a fixed-size container). When React swaps
17
+ * page content, the browser may clamp scrollY and fire scroll events BEFORE
18
+ * useEffect runs. To prevent those events from corrupting the saved position
19
+ * of the page we're leaving, useLayoutEffect captures the target value and
20
+ * updates the location key ref synchronously — before any scroll events.
14
21
  */
15
22
 
16
- import { useEffect, useRef, useCallback } from 'react'
23
+ import { useEffect, useLayoutEffect, useRef, useCallback } from 'react'
17
24
  import { useLocation, useNavigationType } from 'react-router-dom'
18
25
 
19
26
  /** In-memory scroll positions keyed by React Router's location.key. */
@@ -32,6 +39,7 @@ export function useRememberScroll(options = {}) {
32
39
  const location = useLocation()
33
40
  const navigationType = useNavigationType()
34
41
  const locationKeyRef = useRef(location.key)
42
+ const savedScrollRef = useRef(null)
35
43
 
36
44
  // Continuously save scroll position for the current location
37
45
  const handleScroll = useCallback(() => {
@@ -45,13 +53,21 @@ export function useRememberScroll(options = {}) {
45
53
  return () => window.removeEventListener('scroll', handleScroll)
46
54
  }, [enabled, handleScroll])
47
55
 
56
+ // Synchronously capture saved scroll and update key BEFORE browser
57
+ // scroll events can fire (which would corrupt the previous page's value)
58
+ useLayoutEffect(() => {
59
+ if (navigationType === 'POP') {
60
+ savedScrollRef.current = scrollPositions.get(location.key) ?? null
61
+ } else {
62
+ savedScrollRef.current = null
63
+ }
64
+ locationKeyRef.current = location.key
65
+ }, [location.key, navigationType])
66
+
48
67
  // On navigation: restore on POP, scroll to top on PUSH/REPLACE
49
68
  useEffect(() => {
50
69
  if (!enabled) return
51
70
 
52
- // Update ref so the scroll listener saves under the new key
53
- locationKeyRef.current = location.key
54
-
55
71
  // Reset block states if requested (for animations, etc.)
56
72
  if (resetBlockStates) {
57
73
  const page = globalThis.uniweb?.activeWebsite?.activePage
@@ -64,7 +80,7 @@ export function useRememberScroll(options = {}) {
64
80
  if (location.hash) return
65
81
 
66
82
  if (navigationType === 'POP') {
67
- const saved = scrollPositions.get(location.key)
83
+ const saved = savedScrollRef.current
68
84
  if (saved == null) return
69
85
 
70
86
  // Retry scroll restoration as content may load asynchronously.