Newer
Older
gnexus-ui-kit / docs / tasks / gn-table-header-slot.md
@Eugene Sukhodolskiy Eugene Sukhodolskiy 4 hours ago 8 KB New tasks

Задача: слот заголовка и ручки колонок у GnTable

Статус: не начата. Тип: API компонента Vue-адаптера (слоты + ручки колонок). Vanilla-разметка не затрагивается. Повод: потребитель кита (gntodo) собирает на GnTable журнал действий, и шапке понадобился заголовок-иконка вместо слова — сейчас это достижимо только передачей готового узла в label, нигде не описанной. Там же понадобилось прятать колонки на узком экране, и сделано это по номеру колонки. Запись ведётся по правилу платформы: находка про общий ресурс живёт в доке ресурса, у потребителя остаётся только рабочий обход и ссылка сюда.

Источник: gntodo — frontend/src/views/JournalView.vue, страница /journal (правки 0.94); ui-kit 1.0.0 (коммит e28f382). Автор находки: ИИ-агент (сессия разработки gntodo). Автор задачи: владелец gntodo («замени текст на иконку коммента», «попробуй лучше адаптировать эту табличку на моб»). Дата: 2026-10-10. Задача в трекере: не заведена.

Проблема

src/vue/components/GnTable.js рендерит шапку так:

h("tr", { class: "table-row" }, props.columns.map(column => h("th", { scope: "col" }, column.label)))

То есть у шапки нет ни одного слота (объявлены только cell-${column.key} и empty), а column.label кладётся как есть. Отсюда два следствия, оба пойманы на живом приложении:

  1. Заголовок-иконка достижим только через label. В gntodo в label передан узел: h('i', { class: 'ph ph-chat-text', role: 'img', 'aria-label': … }) — слово «Комментарий» было шире колонки, в которой стоит одна кнопка. Работает (и это спасибо — слот не понадобился), но поведение нигде не описано: в component-api.md у GnTable перечислены только слоты ячеек, и потребитель сначала ищет несуществующий header-*. Ничего, кроме label, в шапку положить нельзя — ни сортировку, ни фильтр по колонке, ни кнопку.
  2. Адаптив таблицы потребитель делает по номеру колонки. В gntodo на телефоне скрыты две колонки: :deep(.table-row > :nth-child(4)), :nth-child(5) — потому что у columns нет ни class, ни align, ни hidden, а таблице целиком attrs.class передаётся (table data-list), но колонкам — нет. Стоит поменять порядок колонок в массиве — и стиль молча скроет не ту колонку, ошибка не проявится ни сборкой, ни типами.

Предложение

Минимальный шаг — слот заголовка, по аналогии с ячейками:

h("th", { scope: "col", class: column.class }, slots[`header-${column.key}`]?.({ column }) || column.label)

Плюс column.class в тот же th/td (у td сейчас тоже пустой объект атрибутов) — тогда потребитель прячет колонку по стабильному имени, а не по индексу, и та же ручка годится для выравнивания и ширины через его собственный CSS.

Если решите делать слот — объявлять его в JSDoc и в component-api.md обязательно: именно отсутствие строчки в доке и заставило искать обход.

Необязательные шаги на будущее (не обязательны к этой задаче): column.align (left/right), column.width, и сортировка по клику на th — последнее уже потребует событий и состояния сортировки в компоненте, а сейчас GnTable полностью управляется пропами.

Границы задачи

  • Разметка и классы не меняются: .table-wrapper, table.table.data-list, thead.table-head, tr.table-row, td.is-empty, caption.table-caption — на них завязаны стили кита и потребители.
  • Слоты cell-${column.key} и empty — контракт не трогаем (cell-* получает { row, column, value }; у нового header-* достаточно { column }).
  • columns без новых полей обязан работать как раньше: column.class не задан — атрибута нет вовсе (не class="undefined").
  • Vanilla-разметка таблиц в демо — обычный <table>, ей ручки колонок не нужны; но строку «заголовок можно отдать узлом» в docs/components/data-display.md стоит добавить, потому что этот файл читают и потребители vanilla-сборки.

Что нужно сдать (приёмка по правилам репозитория)

  1. src/vue/components/GnTable.js — слот header-${key} и column.class в th/td, JSDoc с новыми слотами.
  2. docs/vue/component-api.md — строки про слоты GnTable (сейчас там только cell-* и empty) и про то, что label рендерится как есть.
  3. docs/components/data-display.md — пример таблицы с иконкой в заголовке.
  4. docs/catalog.json (npm run gen:catalog) — если в каталоге у GnTable есть список слотов.
  5. Юнит-тест контракта в tests/unit/: слот header-* перекрывает label; column.class попадает и в th, и в td; без слота и класса разметка прежняя.
  6. npm run release:check.

Как проверить глазами

Таблица из трёх колонок, у средней — :class="'col-comment'", заголовок передан узлом-иконкой, слот #header-comment передан кнопкой:

  • в DOM у th видно class="col-comment" и тот же класс у td во всех строках;
  • при ширине 767px и меньше потребительский CSS скрывает колонку по имени класса, и шапка остаётся согласованной с телом (число th равно числу td);
  • колонки без class и без слота рендерятся ровно как в 1.0.0 (сравнить скриншотом).

Почему именно так (контекст от потребителя)

В gntodo обе правки уже живут и проверены живым прогоном на 390px: заголовок-иконка через label, скрытие колонок по :nth-child. Как только кит отдаст header-* и column.class, потребитель заменит оба обхода — и вёрстка колонок перестанет зависеть от порядка массива columns. До этого момента обход остаётся, а ссылка на эту запись — в комментарии рядом с ним.