textmachine/frontend/src/showcase/Reader.tsx

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>
);
}