112 lines
3.9 KiB
TypeScript
112 lines
3.9 KiB
TypeScript
import type { Bank as BankData, BookDetail, Chapter, NoteList } from '../api';
|
||
import { Panel } from '../shell/Panel';
|
||
import { About } from './About';
|
||
import { Bank } from './Bank';
|
||
import { Loaded } from './Loaded';
|
||
import { Notes } from './Notes';
|
||
|
||
interface Props {
|
||
book: Parameters<typeof Loaded<BookDetail>>[0]['query'];
|
||
/** True when the library came back with nothing: "выберите книгу" would point at an empty list. */
|
||
libraryEmpty?: boolean;
|
||
chapters: Chapter[];
|
||
notes: Parameters<typeof Loaded<NoteList>>[0]['query'];
|
||
bank: Parameters<typeof Loaded<BankData>>[0]['query'];
|
||
/** Вкладка поднята на экран: палитра перехода ведёт СЮДА, к найденному термину. */
|
||
tab: string;
|
||
onTabChange: (id: string) => void;
|
||
/** Просьба палитры показать термин. Строку поиска банк держит сам. */
|
||
request: { term: string; seq: number };
|
||
onOpenChapter: (chapterId: string) => void;
|
||
}
|
||
|
||
/**
|
||
* The panel of context for the book being read. The bank lives here rather than on the left
|
||
* (owner, 02.08): the reader's cycle is "read the translation → trip over a term → check it in the
|
||
* bank", and all three have to be visible at once — that is, in columns, not in rows.
|
||
*/
|
||
export function Context({
|
||
book,
|
||
libraryEmpty,
|
||
chapters,
|
||
notes,
|
||
bank,
|
||
tab,
|
||
onTabChange,
|
||
request,
|
||
onOpenChapter,
|
||
}: Props) {
|
||
// Telling the reader to pick a book from a panel that says "Ни одной книги" is a screen arguing
|
||
// with itself; it was on the session's own first-run shot.
|
||
const idle = libraryEmpty
|
||
? { title: 'Книг пока нет', description: 'Добавьте книгу — сведения о ней появятся здесь.' }
|
||
: undefined;
|
||
|
||
return (
|
||
<Panel
|
||
label="О читаемой книге"
|
||
landmark="complementary"
|
||
selectedId={tab}
|
||
onSelect={onTabChange}
|
||
// Панели не размонтируются: прокрутка банка на сотнях терминов переживает уход
|
||
// на «О книге» и обратно (Ф-19).
|
||
keepAlive
|
||
tabs={[
|
||
{
|
||
id: 'about',
|
||
label: 'О книге',
|
||
content: (
|
||
<Loaded query={book} waiting="О книге" idle={idle}>
|
||
{(detail) => <About detail={detail} />}
|
||
</Loaded>
|
||
),
|
||
},
|
||
{
|
||
id: 'notes',
|
||
label: 'Замечания',
|
||
content: (
|
||
<Loaded
|
||
query={notes}
|
||
waiting="Замечания"
|
||
idle={idle}
|
||
isEmpty={(data) => data.notes.length === 0}
|
||
empty={{
|
||
title: 'Замечаний нет',
|
||
description: 'В этой книге нет мест, требующих внимания.',
|
||
}}
|
||
>
|
||
{(data) => (
|
||
<Notes notes={data.notes} chapters={chapters} onOpenChapter={onOpenChapter} />
|
||
)}
|
||
</Loaded>
|
||
),
|
||
},
|
||
{
|
||
id: 'bank',
|
||
label: 'Банк',
|
||
content: (
|
||
<Loaded
|
||
query={bank}
|
||
waiting="Банк памяти"
|
||
idle={idle}
|
||
isEmpty={(data) => data.total === 0}
|
||
empty={{
|
||
title: 'Банк пуст',
|
||
description: 'Термины книги появятся после первого прохода перевода.',
|
||
}}
|
||
>
|
||
{(data) => (
|
||
<Bank
|
||
bank={data}
|
||
sourceLang={book.data?.book.source_lang}
|
||
awaitingSignature={book.data?.book.status === 'awaiting_bank'}
|
||
request={request}
|
||
/>
|
||
)}
|
||
</Loaded>
|
||
),
|
||
},
|
||
]}
|
||
/>
|
||
);
|
||
}
|