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

Свои заголовки

Подписи строк и столбцов задаёт встраивающий, а ширина полосы считается по его тексту.

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

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

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

index.html
<!doctype html>
<!--
Свои заголовки — НАСТОЯЩИЙ МАЛЕНЬКИЙ ПРОЕКТ.
Не «страница витрины», а тот проект, который встраивающий копирует к себе
целиком. Галерея примеров ЗАПУСКАЕТ эти файлы и ПОКАЗЫВАЕТ их же.
-->
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Свои заголовки — @f1</title>
</head>
<body>
<div data-example="custom-headers" class="flex h-screen w-screen flex-col bg-base-100 text-base-content">
<header class="flex flex-wrap items-center gap-3 border-b border-base-300 px-4 py-2">
<div class="me-auto">
<div class="text-sm font-semibold">Свои заголовки</div>
<div class="text-xs opacity-70">
Строки подписаны «Позиция N», столбцы — полями отчёта. Ширина полосы номеров считается
по самой широкой ВИДИМОЙ подписи. Адрес ячейки при этом остаётся A1: подпись меняет
полосу, а не книгу.
</div>
</div>
<label class="label cursor-pointer gap-2" data-headings-toggle>
<input id="own" type="checkbox" class="checkbox checkbox-sm" checked />
<span class="label-text text-xs">свои подписи</span>
</label>
</header>
<!-- ВИД ПОЛОС — ТОКЕНАМИ, а не свойством: тема холста читается с этого
самого элемента, поэтому две переменные красят плашку и чернила. -->
<div
id="app"
data-headings-skin
class="flex min-h-0 flex-1 flex-col [--color-base-200:#e8f0e4] [--color-base-content:#1f4d2b]"
></div>
</div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>

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

src/main.ts
import { createSheetsCore, type SheetsCore } from "@f1/preset-sheets-core";
import { transport } from "./book.js";
import { hostAsks } from "./host.js";
import "./styles.css";
/**
* СВОИ ЗАГОЛОВКИ — И ГЛАВНАЯ ОБЯЗАННОСТЬ ЭТОЙ ТОЧКИ РАСШИРЕНИЯ.
*
* Подписи задаёт встраивающий, а ШИРИНА ПОЛОСЫ считается по ЕГО тексту.
* Подставьте длинную подпись при ширине, посчитанной по номеру строки, — и
* текст обрежется либо полоса станет вдвое шире нужного; здесь она встаёт на ту
* ступень, в которую подпись влезает целиком.
*
* Имя столбца в отчёте почти никогда не «A», а адрес ячейки в ядре при этом
* остаётся A1: подпись меняет ПОЛОСУ, а не книгу.
*/
const FIELDS = ["Товар", "Январь", "Февраль", "Март", "Итого за квартал", "Доля рынка"];
const { locale } = hostAsks();
const mount = document.getElementById("app")!;
const own = document.getElementById("own") as HTMLInputElement;
let core: SheetsCore | null = null;
function build(): void {
core?.destroy();
core = createSheetsCore({
mount,
transport,
locale,
headings: own.checked
? {
rowLabel: (row) => `Позиция ${row + 1}`,
colLabel: (col) => FIELDS[col] ?? `Поле ${col + 1}`,
}
: undefined,
});
}
own.addEventListener("change", build);
build();
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`).
*/
export function hostAsks(): { readonly theme: "light" | "dark"; readonly locale: Locale } {
const params = new URLSearchParams(location.search);
const theme = params.get("theme") === "dark" ? "dark" : "light";
const locale = resolveLocale(params.get("locale") ?? undefined, "ru");
document.documentElement.setAttribute("data-theme", theme);
/* Язык — один на процесс: словарь модульный, и ставит его хозяин страницы. */
setLocale(locale);
applyLocaleAttributes(document.documentElement, locale);
return { theme, locale };
}

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

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

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

package.json
{
"name": "f1-example-custom-headers",
"private": true,
"type": "module",
"description": "Подписи строк и столбцов задаёт встраивающий, а ширина полосы считается по ЕГО тексту.",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"@f1/preset-sheets-core": "*",
"@f1/contract": "*",
"@f1/i18n": "*"
},
"devDependencies": {
"vite": "^7.0.0"
}
}

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

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