style: scroll to field

This commit is contained in:
jialin
2025-10-21 19:59:49 +08:00
parent 1e4018350f
commit fc7350b7e2
6 changed files with 166 additions and 34 deletions
@@ -0,0 +1,73 @@
import { useCallback } from 'react';
interface ScrollOptions {
wait?: number;
behavior?: 'smooth' | 'auto';
block?: 'start' | 'end' | 'center';
offsetTop?: number;
}
export default function useScrollAfterExpand({
form,
activeKey,
setActiveKey,
segmentOptions,
defaultWait = 300
}: {
form: any;
activeKey: string[];
setActiveKey: React.Dispatch<React.SetStateAction<string[]>>;
segmentOptions: { value: string; field: string }[];
defaultWait?: number;
}) {
const scrollToElement = useCallback(
(
el: HTMLElement,
{ behavior = 'smooth', offsetTop = 0 }: ScrollOptions = {}
) => {
// find the nearest scrollable parent
const scrollParent = (() => {
let node: HTMLElement | null = el;
while (node) {
const { overflowY } = getComputedStyle(node);
if (overflowY === 'auto' || overflowY === 'scroll') return node;
node = node.parentElement;
}
return document.scrollingElement || document.documentElement;
})();
const parentRect = scrollParent.getBoundingClientRect();
const elRect = el.getBoundingClientRect();
const top =
elRect.top - parentRect.top + scrollParent.scrollTop - offsetTop;
scrollParent.scrollTo({ top, behavior });
},
[]
);
const scrollToSegment = useCallback(
async (val: string, options?: ScrollOptions) => {
if (!activeKey.includes(val)) {
setActiveKey((prev) => [...prev, val]);
await new Promise((r) => {
setTimeout(r, options?.wait ?? defaultWait);
});
}
await new Promise(requestAnimationFrame);
const current = segmentOptions.find((item) => item.value === val);
if (!current?.field) return;
const el = document.querySelector(
`[data-field="${current.field}"]`
) as HTMLElement | null;
if (el) scrollToElement(el, options);
},
[activeKey, setActiveKey, segmentOptions, defaultWait, scrollToElement]
);
return { scrollToSegment };
}