Перейти к содержимому
FastOffice

Минимальный лист

Сколько строк нужно, чтобы получить работающий лист.

сколько строк нужно, чтобы получить работающий лист
открыть в новой вкладке ↗Работающий пример. Ниже — его код целиком, файл за файлом.

В рамке выше работает тот самый проект, чей код лежит ниже, — собранный, но не переписанный: те же файлы, только пропущенные через сборку. Считает в нём не ядро, а дублёр транспорта, поэтому числа в ячейках ничего не значат: смотреть нужно на код.

Страница, которую открывает браузер.

index.html
<!doctype html>
<!--
МИНИМАЛЬНЫЙ ЛИСТ — НАСТОЯЩИЙ МАЛЕНЬКИЙ ПРОЕКТ.
Это не «страница витрины», а тот проект, который встраивающий скопирует к
себе целиком: точка входа, один модуль, немного стилей и манифест с
зависимостями. Витрина ЗАПУСКАЕТ эти файлы (рамка грузит именно этот
index.html) и ПОКАЗЫВАЕТ их же — расхождению между показанным и работающим
взяться неоткуда.
-->
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Минимальный лист — @f1</title>
</head>
<body>
<!-- Лист монтируется в ВАШ узел: своей страницы он не заводит и корень
документа не забирает. -->
<div id="app" data-example="minimal"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>

Точка входа: здесь лист создаётся и сюда же приходят события.

src/main.ts
import { createSheetsCore } from "@f1/preset-sheets-core";
import { transport } from "./book.js";
import { hostAsks } from "./host.js";
import "./styles.css";
/**
* МИНИМАЛЬНЫЙ ЛИСТ: сколько кода нужно, чтобы получить работающую таблицу.
*
* Три строки — импорт, вызов, уборка. Всё остальное в этом проекте
* необязательно: книга-заглушка (`book.ts`) в продукте заменяется настоящей
* транспортом контракта, а чтение темы и языка из адреса (`host.ts`) — это то,
* что у встраивающего уже есть своё.
*/
/* Тема и язык — от ХОЗЯИНА страницы (см. `host.ts`): он ставит их на свой
корень документа, а листу язык передаёт свойством. */
const { locale } = hostAsks();
const core = createSheetsCore({
mount: document.getElementById("app")!,
transport,
/* Язык листа — СВОЙСТВОМ сборки, а не чтением документа: два листа на одной
странице вправе говорить на разном. */
locale,
});
/* Уборка — обязанность хозяина: лист снимается вместе с вашим экраном.
`import.meta.hot` здесь только для разработки витрины. */
import.meta.hot?.dispose(() => core.destroy());

Книга примера: данные, с которыми лист открывается.

src/book.ts
import { createMockTransport } from "@f1/contract";
/**
* КНИГА-ЗАГЛУШКА — ВМЕСТО НАСТОЯЩЕГО ЯДРА.
*
* Транспорт контракта — единственное место, где лист разговаривает с ядром
* (инвариант №1: считает ядро, оболочка только показывает). В продукте здесь
* стоит `wasm` или мост хоста, и меняется РОВНО эта строка — всё остальное в
* проекте остаётся как есть.
*/
export const transport = createMockTransport({
cellAt: (row, col) => (col === 0 ? `строка ${row + 1}` : `${(row + 1) * (col + 1)}`),
});

Сторона хозяина страницы: что приложение отвечает листу и что от него получает.

src/host.ts
import { applyLocaleAttributes, resolveLocale, setLocale, type Locale } from "@f1/i18n";
/**
* ЧТО ХОЗЯИН СТРАНИЦЫ ДАЁТ ЛИСТУ — тема и язык.
*
* У встраивающего это уже есть своё: тему он знает из настроек, язык — из
* профиля пользователя. Здесь и то и другое читается из адреса
* (`?theme=dark&locale=ar`) ровно затем, чтобы на любое состояние можно было
* дать ссылку и чтобы его проверял обход: настройка, доступная только щелчком,
* недоступна ни ссылке, ни автоматической проверке.
*
* ПРИЗНАКИ СТАВЯТСЯ НА КОРЕНЬ ДОКУМЕНТА — потому что это делает ХОЗЯИН, а не
* лист. Лист читает тему оттуда, где стоит (`:host-context`), а язык получает
* СВОЙСТВОМ сборки: два листа на одной странице вправе говорить на разном.
*
* НАПРАВЛЕНИЕ ПИСЬМА — ИНТЕРФЕЙСА, А НЕ ДОКУМЕНТА. `dir="rtl"` зеркалит хром
* страницы; «Лист справа налево» из Excel — свойство КНИГИ, и сетка от
* арабского языка интерфейса не переворачивается. Это не пропуск, а решение
* (`locale.ts`).
*
* ЛИСТУ ОТДАЁТСЯ СТРОКА ХОЗЯИНА КАК ЕСТЬ, А НЕ СУЖЕННАЯ, и это не
* мелочь. Язык листа — это ЕЩЁ И РАСКЛАДКА ВВОДА: сборка передаёт эту же строку
* ядру (`inputLocale`), и от неё зависит, чем окажется набранное — «3,5» без
* метки ложится текстом, с «ru-RU» становится числом 3.5. Сужай мы строку
* здесь — пример показывал бы лист, до которого язык хозяина не доходит вовсе,
* то есть учил бы неправде о самой заметной своей настройке. Сужение осталось
* там, где оно и нужно: для КОРНЯ ДОКУМЕНТА, которым распоряжается хозяин.
*
* ЗАПИСЬ КОДА МОЖНО ДАВАТЬ СВОЮ. `ru_RU`, `ru_RU.UTF-8` (POSIX), `ru-RU`
* (BCP-47) — сборка приведёт тег к правописанию, которого ждёт ядро, и
* толковать его всё равно будет оно. Отказать ядро может, но по существу: тег
* длиннее 255 байт оно не примет (`raw-input-invalid-locale`), и сборка об
* этом СКАЖЕТ, а не промолчит.
*/
export function hostAsks(): { readonly theme: "light" | "dark"; readonly locale: string } {
const params = new URLSearchParams(location.search);
const theme = params.get("theme") === "dark" ? "dark" : "light";
const asked = params.get("locale") ?? undefined;
/* Признакам корня нужен язык, на котором поверхность ПРАВДА говорит. */
const shown: Locale = resolveLocale(asked, "ru");
document.documentElement.setAttribute("data-theme", theme);
/* Язык — один на процесс: словарь модульный, и ставит его хозяин страницы. */
setLocale(shown);
applyLocaleAttributes(document.documentElement, shown);
return { theme, locale: asked ?? shown };
}

Оформление страницы вокруг листа.

src/styles.css
/* ЛИСТ СТИЛЕЙ НАБОРА КОМПОНЕНТОВ — ЕДИНСТВЕННАЯ СТРОКА, КОТОРУЮ НЕЛЬЗЯ ЗАБЫТЬ.
Собственное оформление лист приносит в теневые корни сам, но утилиты
раскладки, которыми он держит свои органы, компилируются ЭТИМ листом. Без
него страница не просто выглядит иначе — вкладка ЗАВИСАЕТ (проверено:
бесконечный цикл замера у сетки без размеров). У потребителя вне репозитория
это `@fastoffices/formula-kit` из его node_modules. */
@import "@fastoffices/formula-kit"; /* см. примечание выше */
/*
* ОСТАЛЬНОЕ — СТИЛИ ХОЗЯИНА, А НЕ ЛИСТА.
*
* Лист приносит своё оформление сам — оно живёт в теневых корнях и наружу не
* течёт (ради этого и затевались web-components). Здесь только то, что нужно
* СТРАНИЦЕ: убрать поля документа и отдать листу всю высоту.
*/
html,
body,
#app {
height: 100%;
margin: 0;
}
#app {
display: flex;
flex-direction: column;
}

Из каких пакетов собран пример.

package.json
{
"name": "f1-example-minimal",
"private": true,
"type": "module",
"description": "Минимальный лист: один импорт, один вызов, один destroy().",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"@f1/preset-sheets-core": "*",
"@f1/contract": "*",
"@f1/i18n": "*"
},
"devDependencies": {
"vite": "^7.0.0"
}
}

Ассистент документации

Ответ собран из документации и может быть неточным — сверяйтесь с источниками.