@lotics/ui 11.0.0 → 11.1.0
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 +1 -1
- package/src/use_section_nav.ts +25 -5
package/package.json
CHANGED
package/src/use_section_nav.ts
CHANGED
|
@@ -31,20 +31,40 @@ export interface SectionNavHandle<K extends string> {
|
|
|
31
31
|
*
|
|
32
32
|
* Contract: every key's section stays MOUNTED (an unmounted section leaves its
|
|
33
33
|
* last offset registered — conditional sections belong inside an always-mounted
|
|
34
|
-
* wrapper that carries the `onLayout`)
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* wrapper that carries the `onLayout`). On WEB, `jumpTo` re-measures the live
|
|
35
|
+
* DOM position, so async content growing ABOVE a section (a photo grid, a
|
|
36
|
+
* late-loading table) can never stale a jump; the cached offsets only drive
|
|
37
|
+
* the scroll-spy highlight, which self-corrects as jumps refresh them. On
|
|
38
|
+
* native there is no re-measure — content above should settle before precision
|
|
39
|
+
* matters (`onLayout` won't refire on a pure position shift).
|
|
38
40
|
*/
|
|
39
41
|
export function useSectionNav<K extends string>(keys: readonly [K, ...K[]]): SectionNavHandle<K> {
|
|
40
42
|
const scrollRef = useRef<ScrollView>(null);
|
|
41
43
|
const sectionY = useRef<Partial<Record<K, number>>>({});
|
|
44
|
+
// The registered wrapper's DOM node (web only) — RN-web's layout event
|
|
45
|
+
// exposes it as `nativeEvent.target`, which lets jumpTo re-measure live.
|
|
46
|
+
const sectionEl = useRef<Partial<Record<K, Element>>>({});
|
|
42
47
|
const [activeKey, setActiveKey] = useState<K>(keys[0]);
|
|
43
48
|
const register = (key: K) => (e: LayoutChangeEvent) => {
|
|
44
49
|
sectionY.current[key] = e.nativeEvent.layout.y;
|
|
50
|
+
const target: unknown = (e.nativeEvent as { target?: unknown }).target;
|
|
51
|
+
if (typeof Element !== "undefined" && target instanceof Element) sectionEl.current[key] = target;
|
|
52
|
+
};
|
|
53
|
+
/** The section's CURRENT content-relative offset — measured live on web
|
|
54
|
+
* (immune to async content shifting the page), cached `layout.y` otherwise. */
|
|
55
|
+
const currentY = (key: K): number => {
|
|
56
|
+
const el = sectionEl.current[key];
|
|
57
|
+
const sv = scrollRef.current as (ScrollView & { getScrollableNode?: () => unknown }) | null;
|
|
58
|
+
const sc = typeof sv?.getScrollableNode === "function" ? sv.getScrollableNode() : null;
|
|
59
|
+
if (typeof Element !== "undefined" && el instanceof Element && sc instanceof Element) {
|
|
60
|
+
const y = el.getBoundingClientRect().top - sc.getBoundingClientRect().top + sc.scrollTop;
|
|
61
|
+
sectionY.current[key] = y; // refresh the spy's cache while we're here
|
|
62
|
+
return y;
|
|
63
|
+
}
|
|
64
|
+
return sectionY.current[key] ?? 0;
|
|
45
65
|
};
|
|
46
66
|
const jumpTo = (key: K) => {
|
|
47
|
-
scrollRef.current?.scrollTo({ y: Math.max(0, (
|
|
67
|
+
scrollRef.current?.scrollTo({ y: Math.max(0, currentY(key) - 12), animated: true });
|
|
48
68
|
};
|
|
49
69
|
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
50
70
|
const y = e.nativeEvent.contentOffset.y + 80;
|