Skip to main content

Tree

Плотное дерево строк, каким его показывает файловый навигатор редактора: уровни с отступами, треугольник у ветки, значок, подпись, метка справа. Отдельного примитива shadcn для этого нет — Tree собран из Item / ItemGroup / ScrollArea кита, по дереву ресурсов ReFormer Builder, где он и был доведён до рабочего состояния.

Источник узлов задаётся одним из двух способов. nodes — дерево известно целиком: узел это { id, label, kind?, children?, badge?, disabled? }, где id — адрес, уникальный в пределах всего дерева (для файлов удобно брать полный путь), а kind объявляется, а не выводится из children, иначе пустой каталог был бы неотличим от файла. loadChildren — уровни читаются лениво, по одному, при первом раскрытии ветки; корневой уровень дерево запрашивает само, передав в загрузчик null. Прочитанный уровень запоминается: свернуть и раскрыть обратно повторного обращения не стоит, а перечитать его после переименования или удаления файла можно через TreeHandle.refresh(). Пока уровень читается, на месте треугольника крутится спиннер; отказ красит подпись ветки в тревожный цвет и уходит в onLoadError. Оба способа сочетаются: nodes задают верхний уровень, loadChildren дочитывает всё остальное.

Виртуальный скролл включён по умолчанию, и это не оптимизация «на будущее»: раскрытый каталог реального проекта — тысячи строк, у каждой свои обработчики, а поверх строки у потребителя может висеть контекстное меню, то есть ещё один узел с подписками. Отрисовывается только окно видимых строк — для этого дереву нужна определённая высота: её задаёт либо maxRows (высота по содержимому — то, что нужно списку в поповере), либо className вызывающего (h-full в панели). Выключают виртуализацию (virtualized={false}) там, где дерево заведомо короткое, а разметка нужна целиком — например на этой самой странице: Docusaurus отрисовывает её на сервере.

Дерево — навигация, а не поле формы: у него нет ни value, ни onChange, ни статики reformerAdapter, и в component поля его не кладут. Причина не в недоделке: поле обязано отдавать одно значение, а у дерева значений столько же, сколько строк, и вопросов оно решает два разных — «где я сейчас» (selectedId, ровно одна строка, туда же уходит фокус) и «что я выбрал» (checkedIds, сколько угодно строк). Если форме нужно выбрать файл, это делает вариант комбобокса — ComboboxTree (значение string | null) или ComboboxTreeMulti (string[] | null): в поповере у них живёт ровно это же дерево, а наружу они отдают контракт поля. Само Tree остаётся там, где выбор не единственная цель, — в панелях, навигаторах, деревьях ресурсов.