57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { FileClock } from 'lucide-react';
|
|
|
|
import { unitsQuery } from '../api';
|
|
import { useText } from '../i18n/text';
|
|
import { icon } from '../ui/icon';
|
|
import { Reader } from './Reader';
|
|
import styles from './Document.module.css';
|
|
|
|
interface Props {
|
|
bookId: string;
|
|
chapterId: string;
|
|
sourceLang?: string;
|
|
targetLang?: string;
|
|
/** The run over the book has not brought it to ready yet — the text may be regenerated. */
|
|
draft: boolean;
|
|
/** A meaningful interaction with the content pins the tab (the VS Code model). */
|
|
onEngage: () => void;
|
|
}
|
|
|
|
/**
|
|
* An open chapter as a document tab. The query lives HERE, not on the screen: tabs are not
|
|
* unmounted when switching (keep-alive, Ф-19), and one shared query would hand every open tab the
|
|
* text of the active chapter.
|
|
*/
|
|
export function Document({ bookId, chapterId, sourceLang, targetLang, draft, onEngage }: Props) {
|
|
const text = useText();
|
|
// There are no tabs without a book, but the query is switched off explicitly: an address built
|
|
// from an empty id would go out to the network and come back a refusal, which the screen would
|
|
// show as a breakage.
|
|
const units = useQuery({ ...unitsQuery(bookId, chapterId), enabled: bookId !== '' });
|
|
|
|
return (
|
|
<div
|
|
className={styles.document}
|
|
onMouseUp={(event) => {
|
|
// Selecting text is that very "meaningful interaction": the reader compared or copied a
|
|
// piece, which means the chapter is needed further on. Plain scrolling does not pin.
|
|
//
|
|
// We ask about exactly THIS chapter and exactly the left button: a right click and a
|
|
// selection in a neighbouring panel arrive here too, and a non-empty global selection was
|
|
// enough for them to pin a tab nobody touched.
|
|
const selection = document.getSelection();
|
|
if (event.button !== 0 || selection === null || selection.isCollapsed) return;
|
|
if (event.currentTarget.contains(selection.anchorNode)) onEngage();
|
|
}}
|
|
>
|
|
{draft && (
|
|
<p className={styles.draft} title={text('document.draftTooltip')}>
|
|
<FileClock {...icon} className={styles.draftMark} />
|
|
{text('document.draftNote')}
|
|
</p>
|
|
)}
|
|
<Reader sourceLang={sourceLang} targetLang={targetLang} units={units} />
|
|
</div>
|
|
);
|
|
}
|