GrTreeSections

  • ядро
  • Данные
  • API · 13 props

Берут, когда корни — категории, а не элементы.

import { GrTreeSections } from '@feugene/granularity/components/GrTreeSections'

Обзор

Когда брать

  • корни — категории, а не элементы — настройки, права доступа, справочники: выбирать саму рубрику бессмысленно, а называть её нужно;
  • два уровня содержимого — под каждой рубрикой обычное дерево, глубина которого уже не про рубрики;
  • разделам нужен вес в структуре страницы — заголовок получает настоящий уровень (h2…h6) и попадает в оглавление документа;
  • счётчик у рубрики — сколько узлов в разделе, без раскрытия.

Когда взять другое

НужноБерите
Верхний уровень — такой же узел, как остальныеGrTree
Своя разметка и свои роли целикомuseTree
Дерево в панели выбора, а не на экранеGrTreeSelect
Разделы, из которых открыт одинGrCollapse
Оглавление страницы с подсветкой по прокруткеGrScrollSpy

Playground контролы выводятся из API, а не пишутся руками

Сцена

Загружается…

0 изменено· состояние — в адресе

Примеры 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 issued
    Payment failed
    First attempt
    Final attempt
    Renewal in 7 days

    Security2 of 2 on

    Sign-in from a new device
    API key created

    Team0 of 2 on

    Member joined
    Member removed
  • Источники в редакторе запросов

    Плотная колонка: size="xs" с более узким indent укладывают три уровня в 15rem, а show-count ставит рядом с источником число записей первого уровня, не раскрывая его. node-click отдаёт строку и её узел, и родительская схема дописывает имя таблицы в запрос.

    Untitled queryselect * from billing.invoices limit 100

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
dataобязательныйT[]—Данные обычного дерева. Корни становятся заголовками, их дети — деревьями.
nodeKey"id" | Extract<keyof T, string> | undefined"id" as neverПоле с ключом узла. Ключи уникальны на все группы сразу: по ним текущий узел и отметки ведутся одним набором. По умолчанию `id`.
propsGrTreePropsMap | undefinedundefinedИмена полей в данных: `children`, `label`, `isLeaf`. По умолчанию `children` и `label`.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedРазмер строк всех групп, как у `GrTree`. Не задан — из `GrConfigProvider`, иначе `md`.
headingLevel2 | 3 | 4 | 5 | 6 | undefined3Уровень заголовка группы под структуру страницы.
showCountboolean | undefinedfalseПоказывать число узлов первого уровня в группе.
showCheckboxboolean | undefinedfalseЧекбоксы у узлов: множественный выбор поверх групп.
checkStrictlyboolean | undefinedfalseНе связывать родителей и детей: каждый узел отмечается сам по себе.
branchLineboolean | GrTreeBranchLine | undefinedundefinedНаправляющие уровней: `line` — вертикаль на уровень, `elbow` — ещё и колено к строке. По умолчанию `line`.
indentnumber | undefined0Шаг отступа уровня в пикселях. `0` (по умолчанию) — значение из темы.
defaultExpandedKeysGrTreeKey[] | undefined[]Стартовый набор раскрытых узлов во всех группах. Новый массив заменяет раскрытие целиком.
currentKeyv-modelGrTreeKey | undefinedundefinedТекущий узел (`v-model:currentKey`) — один на все группы.
checkedKeysv-modelGrTreeKey[] | undefinedundefinedОтмеченные ключи всех групп одним списком (`v-model:checkedKeys`).

Слоты

SlotTypeОписание
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

GrTreeSectionsPropsinterface

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[];
}
GrTreeSectionsEmitsinterface
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

grTreeSectionsConfigconst
const grTreeSectionsConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrTreeSections">;

Файлdist/types/components/GrTreeSections/safelist.d.ts

grTreeSectionsSafelistconst

Пуст: составных классов у секций нет. Классы grTreeSectionsStyles.ts лежат целыми литералами, и granum извлекает их из чанков компонента сам, включая общие. Строки рисует GrTree — его классы приходят с зависимостью.

const grTreeSectionsSafelist: string[];

Файлdist/types/components/GrTreeSections/index.d.ts

GrTreeSectionsInstancetype
type GrTreeSectionsInstance = ComponentExposed<typeof GrTreeSectionsComponent>;

Использование из прозы библиотеки

Внутри — несколько деревьев, а не одно с заголовками

Это не деталь реализации, а требование паттерна: role="tree" обязывает своих детей быть treeitem, и заголовок между строками либо ломает роль, либо остаётся строкой и врёт диктору о том, чем является. Заголовки между отдельными деревьями законны, и каждая группа объявляет себя сама — имя дерева берётся из подписи рубрики.

Отсюда следствие, которое стоит знать заранее: клавиатурное кольцо у каждой группы своё, и Tab переходит между ними, а стрелки ходят внутри одной. Это верно по существу — группы независимы, — но отличается от одного дерева, где стрелки проходят весь список насквозь.

Выбор один, отметки складываются

Выбранный узел один на все группы: ключи уникальны, поэтому чужой ключ остальные группы просто не найдут у себя.

Отметки собираются объединением по группам. Свести их в один список можно без потерь именно потому, что общего родителя у групп нет: наследование внутри каждой считается своё и на соседей не влияет.

Границы
  • рубрики не сворачиваются. Заголовок — это заголовок, а не переключатель; нужен свёртываемый раздел — это GrCollapse с деревом внутри;
  • глубже трёх уровней приём перестаёт помогать. Первый уровень снимает заголовок, второй ложится ровно, а дальше идёт обычная лестница отступов — и рубрик становится больше, чем содержимого.