GrTreeSections
Берут, когда корни — категории, а не элементы.
Обзор
Когда брать
- корни — категории, а не элементы — настройки, права доступа, справочники: выбирать саму рубрику бессмысленно, а называть её нужно;
- два уровня содержимого — под каждой рубрикой обычное дерево, глубина которого уже не про рубрики;
- разделам нужен вес в структуре страницы — заголовок получает настоящий
уровень (
h2…h6) и попадает в оглавление документа; - счётчик у рубрики — сколько узлов в разделе, без раскрытия.
Когда взять другое
| Нужно | Берите |
|---|---|
| Верхний уровень — такой же узел, как остальные | GrTree |
| Своя разметка и свои роли целиком | useTree |
| Дерево в панели выбора, а не на экране | GrTreeSelect |
| Разделы, из которых открыт один | GrCollapse |
| Оглавление страницы с подсветкой по прокрутке | GrScrollSpy |
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrTreeSections :data="data" />Примеры 3 у каждого — живое демо и его код
Боковая панель справочника API
Ресурсы — рубрики, а не эндпоинты: выбирать сам «Invoices» бессмысленно, а назвать его нужно. С
heading-levelкорни становятся настоящими заголовками нужного странице уровня — здесь 4, под собственным заголовком примера — и попадают в её оглавление; под каждым — обычное дерево со своим клавиатурным кольцом: Tab ходит между группами, стрелки — внутри одной.v-model:current-keyодин на все группы; слот по умолчанию добавляет HTTP-метод.Finalize an invoicePOST /v1/invoices/{id}/finalizeLocks the lines of a draft and assigns the invoice number. A finalized invoice can be voided, not edited.Какие события присылают письмо
show-checkboxработает как в дереве, но каждая группа считает своих родителей сама и соседей не трогает — поэтому все группы без потерь делят одинv-model:checked-keys. Слотheadingзаменяет подпись группы сводкой того, что в ней включено.Email notifications4 events send an email Billing2 of 4 on
Invoice issuedPayment failedFirst attemptFinal attemptRenewal in 7 daysSecurity2 of 2 on
Sign-in from a new deviceAPI key createdTeam0 of 2 on
Member joinedMember removedИсточники в редакторе запросов
Плотная колонка:
size="xs"с более узкимindentукладывают три уровня в 15rem, аshow-countставит рядом с источником число записей первого уровня, не раскрывая его.node-clickотдаёт строку и её узел, и родительская схема дописывает имя таблицы в запрос.Untitled queryselect * from billing.invoices limit 100
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
dataобязательный | T[] | — | Данные обычного дерева. Корни становятся заголовками, их дети — деревьями. |
nodeKey | "id" | Extract<keyof T, string> | undefined | "id" as never | Поле с ключом узла. Ключи уникальны на все группы сразу: по ним текущий узел и отметки ведутся одним набором. По умолчанию `id`. |
props | GrTreePropsMap | undefined | undefined | Имена полей в данных: `children`, `label`, `isLeaf`. По умолчанию `children` и `label`. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Размер строк всех групп, как у `GrTree`. Не задан — из `GrConfigProvider`, иначе `md`. |
headingLevel | 2 | 3 | 4 | 5 | 6 | undefined | 3 | Уровень заголовка группы под структуру страницы. |
showCount | boolean | undefined | false | Показывать число узлов первого уровня в группе. |
showCheckbox | boolean | undefined | false | Чекбоксы у узлов: множественный выбор поверх групп. |
checkStrictly | boolean | undefined | false | Не связывать родителей и детей: каждый узел отмечается сам по себе. |
branchLine | boolean | GrTreeBranchLine | undefined | undefined | Направляющие уровней: `line` — вертикаль на уровень, `elbow` — ещё и колено к строке. По умолчанию `line`. |
indent | number | undefined | 0 | Шаг отступа уровня в пикселях. `0` (по умолчанию) — значение из темы. |
defaultExpandedKeys | GrTreeKey[] | undefined | [] | Стартовый набор раскрытых узлов во всех группах. Новый массив заменяет раскрытие целиком. |
currentKeyv-model | GrTreeKey | undefined | undefined | Текущий узел (`v-model:currentKey`) — один на все группы. |
checkedKeysv-model | GrTreeKey[] | undefined | undefined | Отмеченные ключи всех групп одним списком (`v-model:checkedKeys`). |
Слоты
| Slot | Type | Описание |
|---|---|---|
heading | { data: T; label: string; count: number; } | Заголовок группы целиком — вместо подписи корневого узла. |
default | { node: GrTreeNode<T>; data: T; } | Строка внутри группы, как у `GrTree`. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:currentKey | (e: "update:currentKey", key: GrTreeKey | undefined): void | Текущий узел сменился кликом или `Enter` в любой из групп (`v-model:currentKey`). |
update:checkedKeys | (e: "update:checkedKeys", keys: GrTreeKey[]): void | Отметки изменились (`v-model:checkedKeys`): отметки группы сводятся с отметками остальных групп из `checkedKeys`, родители, отмеченные каскадом, входят тоже. |
nodeClick | (e: "nodeClick", data: any, node: GrTreeNode<any>): void | Узел выбран кликом или `Enter` и стал текущим — данные строки и узел, как у `GrTree`. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrTreeSectionsProps, GrTreeSectionsEmits, GrTreeSectionsInstance } from '@feugene/granularity/components/GrTreeSections'Файлdist/types/components/GrTreeSections/GrTreeSections.vue.d.ts
GrTreeSections — дерево, у которого верхний уровень стал рубрикой.
Внутри это не одно дерево с заголовками между строк, а **несколько деревьев**:
role="tree" требует, чтобы его детьми были treeitem, и заголовок посреди
них либо ломал бы паттерн, либо врал диктору, оставаясь строкой. Заголовки
между отдельными деревьями законны, и каждая группа объявляется сама собой.
Отсюда же граница: клавиатурное кольцо у каждой группы своё, Tab переходит
между ними. Это верно по существу — группы независимы.
interface GrTreeSectionsProps<TData extends object = any> {
/** Данные обычного дерева. Корни становятся заголовками, их дети — деревьями. */
data: TData[];
/**
* Поле с ключом узла. Ключи уникальны на все группы сразу: по ним текущий узел и
* отметки ведутся одним набором. По умолчанию `id`.
*/
nodeKey?: GrTreeDataProps<TData>['nodeKey'];
/** Имена полей в данных: `children`, `label`, `isLeaf`. По умолчанию `children` и `label`. */
props?: GrTreePropsMap;
/** Размер строк всех групп, как у `GrTree`. Не задан — из `GrConfigProvider`, иначе `md`. */
size?: GrComponentSize;
/** Уровень заголовка группы под структуру страницы. */
headingLevel?: 2 | 3 | 4 | 5 | 6;
/** Показывать число узлов первого уровня в группе. */
showCount?: boolean;
/** Чекбоксы у узлов: множественный выбор поверх групп. */
showCheckbox?: boolean;
/** Не связывать родителей и детей: каждый узел отмечается сам по себе. */
checkStrictly?: boolean;
/**
* Направляющие уровней: `line` — вертикаль на уровень, `elbow` — ещё и колено к строке.
* По умолчанию `line`.
*/
branchLine?: boolean | GrTreeBranchLine;
/** Шаг отступа уровня в пикселях. `0` (по умолчанию) — значение из темы. */
indent?: number;
/** Стартовый набор раскрытых узлов во всех группах. Новый массив заменяет раскрытие целиком. */
defaultExpandedKeys?: GrTreeKey[];
/** Текущий узел (`v-model:currentKey`) — один на все группы. */
currentKey?: GrTreeKey;
/** Отмеченные ключи всех групп одним списком (`v-model:checkedKeys`). */
checkedKeys?: GrTreeKey[];
}interface GrTreeSectionsEmits {
/** Текущий узел сменился кликом или `Enter` в любой из групп (`v-model:currentKey`). */
(e: 'update:currentKey', key: GrTreeKey | undefined): void;
/**
* Отметки изменились (`v-model:checkedKeys`): отметки группы сводятся с отметками
* остальных групп из `checkedKeys`, родители, отмеченные каскадом, входят тоже.
*/
(e: 'update:checkedKeys', keys: GrTreeKey[]): void;
/** Узел выбран кликом или `Enter` и стал текущим — данные строки и узел, как у `GrTree`. */
(e: 'nodeClick', data: any, node: GrTreeNode<any>): void;
}Файлdist/types/components/GrTreeSections/config.d.ts
const grTreeSectionsConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrTreeSections">;Файлdist/types/components/GrTreeSections/safelist.d.ts
Пуст: составных классов у секций нет. Классы grTreeSectionsStyles.ts лежат
целыми литералами, и granum извлекает их из чанков компонента сам, включая
общие. Строки рисует GrTree — его классы приходят с зависимостью.
const grTreeSectionsSafelist: string[];Файлdist/types/components/GrTreeSections/index.d.ts
type GrTreeSectionsInstance = ComponentExposed<typeof GrTreeSectionsComponent>;Использование из прозы библиотеки
Внутри — несколько деревьев, а не одно с заголовками
Это не деталь реализации, а требование паттерна: role="tree" обязывает своих
детей быть treeitem, и заголовок между строками либо ломает роль, либо остаётся
строкой и врёт диктору о том, чем является. Заголовки между отдельными
деревьями законны, и каждая группа объявляет себя сама — имя дерева берётся из
подписи рубрики.
Отсюда следствие, которое стоит знать заранее: клавиатурное кольцо у каждой
группы своё, и Tab переходит между ними, а стрелки ходят внутри одной. Это
верно по существу — группы независимы, — но отличается от одного дерева, где
стрелки проходят весь список насквозь.
Выбор один, отметки складываются
Выбранный узел один на все группы: ключи уникальны, поэтому чужой ключ остальные группы просто не найдут у себя.
Отметки собираются объединением по группам. Свести их в один список можно без потерь именно потому, что общего родителя у групп нет: наследование внутри каждой считается своё и на соседей не влияет.
Границы
- рубрики не сворачиваются. Заголовок — это заголовок, а не переключатель;
нужен свёртываемый раздел — это
GrCollapseс деревом внутри; - глубже трёх уровней приём перестаёт помогать. Первый уровень снимает заголовок, второй ложится ровно, а дальше идёт обычная лестница отступов — и рубрик становится больше, чем содержимого.
Пропы этого примера