76 lines
2.8 KiB
TypeScript
76 lines
2.8 KiB
TypeScript
import type { UnitList } from '../api';
|
|
import { useText } from '../i18n/text';
|
|
import { Callout } from '../ui/Callout';
|
|
import { Loaded } from './Loaded';
|
|
import { noteTone } from './format';
|
|
import styles from './Reader.module.css';
|
|
|
|
interface Props {
|
|
/** Codes, not names, and they come from the book's data — see the note on the columns below. */
|
|
sourceLang?: string;
|
|
targetLang?: string;
|
|
units: Parameters<typeof Loaded<UnitList>>[0]['query'];
|
|
}
|
|
|
|
/**
|
|
* Two columns with NO scroll synchronisation: one scroll container, and the columns sit inside the
|
|
* row of a pair — then they physically cannot drift apart (STACK_DECISIONS §2). The unit of a pair
|
|
* is the export's edit unit, and a whole chapter is sometimes a single block.
|
|
*
|
|
* The columns are told apart by ONE device — a hairline down the gutter (owner's remark 9), drawn
|
|
* on the sheet rather than per block: a rule repeated on every pair breaks into dashes at the gaps
|
|
* between them, which reads as a table rather than as two columns.
|
|
*/
|
|
export function Reader({ sourceLang, targetLang, units }: Props) {
|
|
const text = useText();
|
|
return (
|
|
<Loaded
|
|
query={units}
|
|
waiting={text('reader.waiting')}
|
|
isEmpty={(data) => data.units.length === 0}
|
|
empty={{
|
|
title: text('reader.emptyTitle'),
|
|
description: text('reader.emptyDescription'),
|
|
}}
|
|
>
|
|
{(data) => (
|
|
// tabIndex: a scrollable region has to be reachable from the keyboard, or the chapter can
|
|
// only be paged with a mouse. Found by the axe pass over the OPEN state — the route gate
|
|
// never saw it, because on its frame the text did not overflow the panel.
|
|
<div className={styles.pairs} tabIndex={0}>
|
|
<div className={styles.sheet}>
|
|
{data.units.map((unit) => {
|
|
const columns = (
|
|
<div className={styles.columns}>
|
|
<div className={styles.source} lang={sourceLang}>
|
|
{unit.source}
|
|
</div>
|
|
<div className={styles.target} lang={targetLang}>
|
|
{unit.target === undefined || unit.target === '' ? (
|
|
<span className={styles.missing}>{text('reader.missingTarget')}</span>
|
|
) : (
|
|
unit.target
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
return unit.note ? (
|
|
<Callout
|
|
key={unit.id}
|
|
tone={noteTone(unit.note.severity)}
|
|
caption={unit.note.message}
|
|
>
|
|
{columns}
|
|
</Callout>
|
|
) : (
|
|
<article key={unit.id} className={styles.pair}>
|
|
{columns}
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Loaded>
|
|
);
|
|
}
|