Track QWord current page in status bar

This commit is contained in:
Курнат Андрей
2026-06-02 07:16:15 +03:00
parent 07f7335c74
commit 7ace47dd2a
3 changed files with 110 additions and 9 deletions
+37 -5
View File
@@ -45,6 +45,7 @@ interface QWordProps {
}
export interface QWordStatus {
currentPage: number;
pageCount: number;
wordCount: number;
}
@@ -155,14 +156,42 @@ function pageHeightFromStyles(element: HTMLElement) {
return Number.isFinite(pageHeight) && pageHeight > 0 ? pageHeight : defaultPageViewHeight;
}
export function qwordCurrentPageFromViewport(input: { stageTop: number; paperTop: number; pageHeight: number; pageCount: number; zoom: number }) {
const pageCount = Math.max(1, input.pageCount);
const zoom = Number.isFinite(input.zoom) && input.zoom > 0 ? input.zoom : 1;
const visualPageHeight = Math.max(1, input.pageHeight * zoom);
const visiblePaperOffset = Math.max(0, input.stageTop - input.paperTop);
return Math.min(pageCount, Math.max(1, Math.floor(visiblePaperOffset / visualPageHeight) + 1));
}
export function QWord({ document, zoom = 100, onChange, onStatusChange }: QWordProps) {
const stageRef = useRef<HTMLElement>(null);
const editorRef = useRef<HTMLDivElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const imageInputRef = useRef<HTMLInputElement>(null);
const [pageView, setPageView] = useState({ count: 1, height: defaultPageViewHeight });
const [pageView, setPageView] = useState({ count: 1, current: 1, height: defaultPageViewHeight });
const wordCount = useMemo(() => countWords(document.html), [document.html]);
const stageStyle = { "--qword-zoom": String(zoom / 100) } as CSSProperties;
function currentPageFromStage(pageCount: number, pageHeight: number) {
const stage = stageRef.current;
const editor = editorRef.current;
if (!stage || !editor) {
return 1;
}
const stageRect = stage.getBoundingClientRect();
const paperRect = editor.getBoundingClientRect();
const currentZoom = Number.parseFloat(window.getComputedStyle(stage).getPropertyValue("--qword-zoom"));
return qwordCurrentPageFromViewport({
stageTop: stageRect.top,
paperTop: paperRect.top,
pageHeight,
pageCount,
zoom: Number.isFinite(currentZoom) ? currentZoom : 1
});
}
function refreshPageView() {
window.requestAnimationFrame(() => {
const editor = editorRef.current;
@@ -172,7 +201,10 @@ export function QWord({ document, zoom = 100, onChange, onStatusChange }: QWordP
const height = pageHeightFromStyles(editor);
const count = Math.max(1, Math.ceil(editor.scrollHeight / height));
setPageView((current) => (current.count === count && current.height === height ? current : { count, height }));
const currentPage = currentPageFromStage(count, height);
setPageView((current) =>
current.count === count && current.height === height && current.current === currentPage ? current : { count, current: currentPage, height }
);
});
}
@@ -189,8 +221,8 @@ export function QWord({ document, zoom = 100, onChange, onStatusChange }: QWordP
}, [document.html, zoom]);
useLayoutEffect(() => {
onStatusChange?.({ pageCount: pageView.count, wordCount });
}, [onStatusChange, pageView.count, wordCount]);
onStatusChange?.({ currentPage: pageView.current, pageCount: pageView.count, wordCount });
}, [onStatusChange, pageView.count, pageView.current, wordCount]);
function runCommand(command: string, value?: string) {
editorRef.current?.focus();
@@ -371,7 +403,7 @@ export function QWord({ document, zoom = 100, onChange, onStatusChange }: QWordP
return (
<div className="editor-layout qword-layout">
<section className="document-stage" style={stageStyle} aria-label="Редактор QWord">
<section ref={stageRef} className="document-stage" style={stageStyle} onScroll={refreshPageView} aria-label="Редактор QWord">
<div className="module-toolbar word-module-toolbar">
<input
ref={fileInputRef}