70 lines
3.8 KiB
TypeScript
70 lines
3.8 KiB
TypeScript
/**
|
||
* Модель открытых документов центра — та же, что у VS Code (замечание владельца 2).
|
||
* Закрепляет вкладку двойной клик либо содержательное взаимодействие с содержимым; что у нас
|
||
* считается содержательным и почему — `Document.tsx`. Функции чистые: модель проверяется
|
||
* тестом, а не кадром (documents.test.ts).
|
||
*/
|
||
export interface Documents {
|
||
/** Закреплённые вкладки в порядке открытия. */
|
||
pinned: string[];
|
||
/** Вкладка предпросмотра. Её ровно одна или ни одной. */
|
||
preview: string | null;
|
||
/** Выбранная вкладка; пустая строка означает «не открыто ничего». */
|
||
active: string;
|
||
}
|
||
|
||
export const noDocuments: Documents = { pinned: [], preview: null, active: '' };
|
||
|
||
/** Предпросмотр всегда крайний справа: закрепление не двигает ряд. */
|
||
export const openedIds = ({ pinned, preview }: Documents): string[] =>
|
||
preview === null ? pinned : [...pinned, preview];
|
||
|
||
/** Одиночный клик. Уже закреплённая вкладка просто выбирается — предпросмотр её не трогает. */
|
||
export function previewDocument(documents: Documents, id: string): Documents {
|
||
if (documents.pinned.includes(id)) return { ...documents, active: id };
|
||
return { ...documents, preview: id, active: id };
|
||
}
|
||
|
||
/** Двойной клик или содержательное взаимодействие: предпросмотр становится постоянной вкладкой. */
|
||
export function pinDocument(documents: Documents, id: string): Documents {
|
||
if (documents.pinned.includes(id)) return { ...documents, active: id };
|
||
return {
|
||
pinned: [...documents.pinned, id],
|
||
preview: documents.preview === id ? null : documents.preview,
|
||
active: id,
|
||
};
|
||
}
|
||
|
||
export function selectDocument(documents: Documents, id: string): Documents {
|
||
return { ...documents, active: id };
|
||
}
|
||
|
||
/**
|
||
* Закрытие. Выбор переходит на соседнюю вкладку СПРАВА, а если её нет — слева: так место
|
||
* в ряду не теряется, и закрытие подряд не выбрасывает читателя в пустой центр.
|
||
*/
|
||
export function closeDocument(documents: Documents, id: string): Documents {
|
||
const order = openedIds(documents);
|
||
const index = order.indexOf(id);
|
||
const next: Documents = {
|
||
pinned: documents.pinned.filter((other) => other !== id),
|
||
preview: documents.preview === id ? null : documents.preview,
|
||
active: documents.active,
|
||
};
|
||
if (documents.active !== id) return next;
|
||
const rest = openedIds(next);
|
||
next.active = rest[Math.min(index, rest.length - 1)] ?? '';
|
||
return next;
|
||
}
|
||
|
||
/**
|
||
* Что открыто, пока читатель не трогал вкладки: одна закреплённая и одна предпросмотра — иначе
|
||
* обе формы вкладки на статичном кадре не видны, а различает их модель VS Code курсивом.
|
||
* Берутся второй и третий разделы: в фикстуре по умолчанию именно у них есть и текст, и
|
||
* замечания. Это подпорка ВИТРИНЫ и уйдёт вместе с ней, когда появится настоящая библиотека.
|
||
*/
|
||
export function firstLook(ids: string[]): Documents {
|
||
const [, pinned, preview] = ids;
|
||
if (pinned === undefined) return noDocuments;
|
||
return { pinned: [pinned], preview: preview ?? null, active: pinned };
|
||
}
|