Tree View Row
grida/tree-view-rowFreeComponent
Canonical shadcn-styled row for `@grida/tree-view`.
Live preview
live · rendered by the registry
Rendered by grida on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://grida.co/r/tree-view-row.jsonSource
1"use client";23import type { Row } from "@grida/tree-view";4import { useTree, useTreeSnapshot } from "@grida/tree-view/react";5import { cn } from "@/lib/utils";6import { ChevronDownIcon, ChevronRightIcon } from "lucide-react";7import * as React from "react";89export interface TreeViewRowProps {10 /** Row produced by `controller.getRows()`. */11 row: Row;12 /** Optional label override. Falls back to `row.meta.label ?? row.id`. */13 label?: React.ReactNode;14 /** Optional leading icon. */15 icon?: React.ReactNode;16 /** Number of pixels per depth step. Defaults to 16. */17 indent?: number;18}1920/**21 * Canonical shadcn-styled row for `@grida/tree-view`.22 *23 * - Subscribes to its own selection / focus / expanded slice via24 * `useTreeSnapshot` so siblings don't re-render when only your row's25 * state changes.26 * - Drives a single `data-state` attribute so Tailwind variants can style27 * every state from one className.28 * - Toggles expansion on chevron click; emits selection on the row body.29 */30export function TreeViewRow({31 row,32 label,33 icon,34 indent = 16,35}: TreeViewRowProps) {36 const controller = useTree();37 const isContainer =38 controller.source.isContainer?.(row.id) ?? row.isContainer;3940 const state = useTreeSnapshot(41 (c) => ({42 selected: c.getSelection().includes(row.id),43 focused: c.getFocused() === row.id,44 expanded: c.isExpanded(row.id),45 }),46 (a, b) =>47 a.selected === b.selected &&48 a.focused === b.focused &&49 a.expanded === b.expanded50 );5152 const dataState = state.selected53 ? "selected"54 : state.focused55 ? "focused"56 : "idle";5758 const handleToggle = (e: React.MouseEvent) => {59 e.stopPropagation();60 if (state.expanded) controller.collapse(row.id);61 else controller.expand(row.id);62 };6364 const handleSelect = (e: React.MouseEvent) => {65 const mode = e.shiftKey66 ? "range"67 : e.metaKey || e.ctrlKey68 ? "toggle"69 : "replace";70 controller.select([row.id], mode);71 controller.focus(row.id);72 };7374 return (75 <div76 role="treeitem"77 aria-selected={state.selected}78 aria-expanded={isContainer ? state.expanded : undefined}79 data-state={dataState}80 onClick={handleSelect}81 style={{ paddingLeft: row.depth * indent }}82 className={cn(83 "flex h-7 items-center gap-1.5 px-1.5 text-sm cursor-default select-none rounded-sm",84// … truncated
