文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

73 行
2.1 KiB
TypeScript

import { useEffect, useCallback, useRef } from 'react';
import shim from '@joplin/lib/shim';
interface HookDependencies {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TinyMCE editor instance; the typed Editor surface is narrower than what we actually use (getDoc/getWin)
editor: any;
onScroll: (event: { percent: number })=> void;
}
export default function useScroll(dependencies: HookDependencies) {
const { editor, onScroll } = dependencies;
const scrollTimeoutId_ = useRef(null);
const maxScrollTop = useCallback(() => {
if (!editor) return 0;
const doc = editor.getDoc();
const win = editor.getWin();
if (!doc || !win) return 0;
const firstChild = doc.firstElementChild;
if (!firstChild) return 0;
const winHeight = win.innerHeight;
const contentHeight = firstChild.scrollHeight;
return contentHeight < winHeight ? 0 : contentHeight - winHeight;
}, [editor]);
const scrollTop = useCallback(() => {
if (!editor) return 0;
const win = editor.getWin();
if (!win) return 0;
return win.scrollY;
}, [editor]);
const scrollPercent = useCallback(() => {
const m = maxScrollTop();
const t = scrollTop();
return m <= 0 ? 0 : t / m;
}, [maxScrollTop, scrollTop]);
const scrollToPercent = useCallback((percent: number) => {
if (!editor) return;
editor.getWin().scrollTo(0, maxScrollTop() * percent);
}, [editor, maxScrollTop]);
const scheduleOnScroll = useCallback((event: { percent: number }) => {
if (scrollTimeoutId_.current) {
shim.clearTimeout(scrollTimeoutId_.current);
scrollTimeoutId_.current = null;
}
scrollTimeoutId_.current = shim.setTimeout(() => {
scrollTimeoutId_.current = null;
onScroll(event);
}, 10);
}, [onScroll]);
const onEditorScroll = useCallback(() => {
scheduleOnScroll({ percent: scrollPercent() });
}, [scheduleOnScroll, scrollPercent]);
useEffect(() => {
if (!editor) return () => {};
editor.getDoc().addEventListener('scroll', onEditorScroll);
return () => {
editor.getDoc().removeEventListener('scroll', onEditorScroll);
};
}, [editor, onEditorScroll]);
return { scrollToPercent };
}