@nextlyhq/ui 0.0.2-alpha.52 → 0.0.2-alpha.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2453,4 +2453,115 @@ declare const ResizableHandle: ({ withGrip, className, children, ...props }: rea
2453
2453
  withGrip?: boolean;
2454
2454
  }) => react_jsx_runtime.JSX.Element;
2455
2455
 
2456
- export { Accordion, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, type ActionCallbacks, Alert, AlertDescription, type AlertDescriptionProps, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, type AlertDialogCancelProps, AlertDialogContent, type AlertDialogContentProps, AlertDialogDescription, type AlertDialogDescriptionProps, AlertDialogFooter, type AlertDialogFooterProps, AlertDialogHeader, type AlertDialogHeaderProps, AlertDialogOverlay, type AlertDialogOverlayProps, AlertDialogPortal, AlertDialogTitle, type AlertDialogTitleProps, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTitleProps, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, type AvatarImageProps, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, type CommandDialogProps, CommandEmpty, type CommandEmptyProps, CommandGroup, type CommandGroupProps, CommandInput, type CommandInputProps, CommandItem, type CommandItemProps, CommandList, type CommandListProps, type CommandProps, CommandSeparator, type CommandSeparatorProps, CommandShortcut, type CommandShortcutProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, type DataFetcher, Dialog, DialogClose, DialogContent, type DialogContentProps, DialogDescription, type DialogDescriptionProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogOverlay, type DialogOverlayProps, DialogPortal, DialogTitle, type DialogTitleProps, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, type DropdownMenuCheckboxItemProps, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, type DropdownMenuLabelProps, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, type DropdownMenuRadioItemProps, DropdownMenuSeparator, type DropdownMenuSeparatorProps, DropdownMenuShortcut, type DropdownMenuShortcutProps, DropdownMenuSub, DropdownMenuSubContent, type DropdownMenuSubContentProps, DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, DropdownMenuTrigger, type FilterInfo, FormLabelWithTooltip, type FormLabelWithTooltipProps, Grid, type GridProps, Input, type InputProps, Label, type ListResponse, type PaginationConfig, type PaginationMeta, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalProvider, Progress, type ProgressProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, Sheet, SheetClose, type SheetCloseProps, SheetContent, type SheetContentProps, type SheetContentRef, SheetDescription, type SheetDescriptionProps, type SheetDescriptionRef, SheetFooter, type SheetFooterProps, SheetHeader, type SheetHeaderProps, SheetOverlay, type SheetOverlayProps, type SheetOverlayRef, SheetPortal, type SheetProps, SheetTitle, type SheetTitleProps, type SheetTitleRef, SheetTrigger, type SheetTriggerProps, Skeleton, type SkeletonProps, type SortInfo, Spinner, type SpinnerProps, Stack, type StackProps, Stat, type StatProps, Switch, Table, TableBody, TableCaption, TableCell, TableEmpty, type TableEmptyProps, TableError, type TableErrorProps, TableFooter, TableHead, TableHeader, TableLoading, type TableParams, TableRow, TableSearch, type TableSearchProps, TableSkeleton, type TableSkeletonProps, Tabs, TabsContent, type TabsContentProps, TabsList, type TabsListProps, type TabsProps, TabsTrigger, type TabsTriggerProps, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, avatarVariants, badgeVariants, buttonVariants, cardVariants, dialogContentVariants, inputVariants, progressVariants, selectTriggerVariants, sheetVariants, spinnerVariants, usePortalContainer };
2456
+ /**
2457
+ * TreeView
2458
+ *
2459
+ * A keyboard-operable, virtualized tree. The layers panel of an editor is this control: a
2460
+ * hierarchy that can hold thousands of nodes, that someone navigates with arrow keys as much as
2461
+ * with a pointer, and that has to stay responsive while they do.
2462
+ *
2463
+ * **Why virtualized, and why that decides the markup.** A document of a few thousand blocks
2464
+ * renders a few thousand rows, and the cost is not the React work — it is layout and paint on
2465
+ * every expand, scroll and selection. Only the visible window is rendered here.
2466
+ *
2467
+ * That has a consequence the accessibility notes below depend on: with only a window in the DOM,
2468
+ * the nested `role="group"` markup the tree pattern usually uses **cannot be built**, because an
2469
+ * item's children may not be rendered at all. The APG covers this exact case by allowing a FLAT
2470
+ * set of `treeitem`s that describe the hierarchy through `aria-level`, `aria-setsize` and
2471
+ * `aria-posinset` instead of through nesting. A screen reader reads depth and position from those
2472
+ * attributes, so they are not decoration — without them a virtualized tree announces itself as a
2473
+ * flat list of whatever happens to be on screen.
2474
+ *
2475
+ * **Why a headless virtualizer.** The markup above is the requirement, so anything that owns the
2476
+ * DOM is unusable. `@tanstack/react-virtual` computes offsets and renders nothing.
2477
+ *
2478
+ * **State is controllable, not controlled.** `expandedIds`/`selectedId` may be passed with their
2479
+ * `onChange` partners to drive the tree from a store — which an editor will do, since selection is
2480
+ * shared with the canvas and the inspector — or omitted entirely, in which case the tree keeps its
2481
+ * own. Requiring a store for a tree in a settings dialog would be a poor trade.
2482
+ *
2483
+ * **Design specifications**:
2484
+ * - Row height: fixed 28px (`--tree-row`), so the virtualizer needs no measurement pass
2485
+ * - Indent: 12px per level, applied as padding so the whole row stays a hit target
2486
+ * - Selection: `bg-muted`, matching the menu highlight rather than a full-contrast flip
2487
+ * - Focus: `focus-visible` ring in the focus token
2488
+ *
2489
+ * **Accessibility**:
2490
+ * - `role="tree"` with flat `role="treeitem"` children carrying `aria-level`, `aria-setsize`,
2491
+ * `aria-posinset`, and `aria-expanded` on anything with children
2492
+ * - Roving tabindex: exactly one row is in the tab order, so Tab enters and leaves the tree once
2493
+ * rather than walking every node
2494
+ * - Arrow keys move and expand, per the APG tree pattern: Right expands then descends, Left
2495
+ * collapses then ascends, Home/End jump to the ends, `*` expands every sibling
2496
+ * - Typeahead focuses the next row whose label starts with what was typed
2497
+ * - Moving focus scrolls the row into view, which virtualization would otherwise prevent: a row
2498
+ * outside the window has no element to focus
2499
+ *
2500
+ * @example
2501
+ * ```tsx
2502
+ * <TreeView
2503
+ * nodes={layers}
2504
+ * aria-label="Layers"
2505
+ * selectedId={selected}
2506
+ * onSelectedChange={setSelected}
2507
+ * className="h-full"
2508
+ * />
2509
+ * ```
2510
+ *
2511
+ * @module
2512
+ */
2513
+
2514
+ /**
2515
+ * One node of the tree. Children are omitted or empty for a leaf.
2516
+ *
2517
+ * @experimental
2518
+ */
2519
+ interface TreeNode {
2520
+ /** Stable identity. What selection and expansion are keyed by. */
2521
+ id: string;
2522
+ /** What the row shows. A string also feeds typeahead; anything else needs `textValue`. */
2523
+ label: react.ReactNode;
2524
+ /**
2525
+ * The text typeahead matches on, when `label` is not a string.
2526
+ *
2527
+ * Without it a row rendered as markup cannot be typed to, and a keyboard user loses the fastest
2528
+ * way through a long tree.
2529
+ */
2530
+ textValue?: string;
2531
+ /** Children, if any. An empty array still marks the node as a parent. */
2532
+ children?: readonly TreeNode[];
2533
+ /** Shown before the label, after the twisty. */
2534
+ icon?: react.ReactNode;
2535
+ /** Skipped by every keyboard move and not selectable. */
2536
+ disabled?: boolean;
2537
+ }
2538
+ /** @experimental */
2539
+ interface TreeViewProps extends Omit<react.HTMLAttributes<HTMLDivElement>, "onSelect"> {
2540
+ /** The roots of the tree. */
2541
+ nodes: readonly TreeNode[];
2542
+ /** Expanded node ids, if the caller owns them. */
2543
+ expandedIds?: readonly string[];
2544
+ /** Which ids start expanded when the caller does not own expansion. */
2545
+ defaultExpandedIds?: readonly string[];
2546
+ /** Called with the full next set whenever a branch opens or closes. */
2547
+ onExpandedChange?: (ids: string[]) => void;
2548
+ /** The selected node id, if the caller owns it. */
2549
+ selectedId?: string | null;
2550
+ /** Which id starts selected when the caller does not own selection. */
2551
+ defaultSelectedId?: string | null;
2552
+ /** Called when a row is chosen, by pointer or by Enter. */
2553
+ onSelectedChange?: (id: string) => void;
2554
+ /**
2555
+ * Names the tree for a screen reader. One of this or `aria-labelledby` is required: a tree
2556
+ * announced only as "tree" tells a user nothing about which one they are in.
2557
+ */
2558
+ "aria-label"?: string;
2559
+ }
2560
+ /**
2561
+ * A virtualized tree.
2562
+ *
2563
+ * @experimental
2564
+ */
2565
+ declare const TreeView: react.ForwardRefExoticComponent<TreeViewProps & react.RefAttributes<HTMLDivElement>>;
2566
+
2567
+ export { Accordion, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, type ActionCallbacks, Alert, AlertDescription, type AlertDescriptionProps, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, type AlertDialogCancelProps, AlertDialogContent, type AlertDialogContentProps, AlertDialogDescription, type AlertDialogDescriptionProps, AlertDialogFooter, type AlertDialogFooterProps, AlertDialogHeader, type AlertDialogHeaderProps, AlertDialogOverlay, type AlertDialogOverlayProps, AlertDialogPortal, AlertDialogTitle, type AlertDialogTitleProps, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTitleProps, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, type AvatarImageProps, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, type CommandDialogProps, CommandEmpty, type CommandEmptyProps, CommandGroup, type CommandGroupProps, CommandInput, type CommandInputProps, CommandItem, type CommandItemProps, CommandList, type CommandListProps, type CommandProps, CommandSeparator, type CommandSeparatorProps, CommandShortcut, type CommandShortcutProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, type DataFetcher, Dialog, DialogClose, DialogContent, type DialogContentProps, DialogDescription, type DialogDescriptionProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogOverlay, type DialogOverlayProps, DialogPortal, DialogTitle, type DialogTitleProps, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, type DropdownMenuCheckboxItemProps, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, type DropdownMenuLabelProps, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, type DropdownMenuRadioItemProps, DropdownMenuSeparator, type DropdownMenuSeparatorProps, DropdownMenuShortcut, type DropdownMenuShortcutProps, DropdownMenuSub, DropdownMenuSubContent, type DropdownMenuSubContentProps, DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, DropdownMenuTrigger, type FilterInfo, FormLabelWithTooltip, type FormLabelWithTooltipProps, Grid, type GridProps, Input, type InputProps, Label, type ListResponse, type PaginationConfig, type PaginationMeta, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalProvider, Progress, type ProgressProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, Sheet, SheetClose, type SheetCloseProps, SheetContent, type SheetContentProps, type SheetContentRef, SheetDescription, type SheetDescriptionProps, type SheetDescriptionRef, SheetFooter, type SheetFooterProps, SheetHeader, type SheetHeaderProps, SheetOverlay, type SheetOverlayProps, type SheetOverlayRef, SheetPortal, type SheetProps, SheetTitle, type SheetTitleProps, type SheetTitleRef, SheetTrigger, type SheetTriggerProps, Skeleton, type SkeletonProps, type SortInfo, Spinner, type SpinnerProps, Stack, type StackProps, Stat, type StatProps, Switch, Table, TableBody, TableCaption, TableCell, TableEmpty, type TableEmptyProps, TableError, type TableErrorProps, TableFooter, TableHead, TableHeader, TableLoading, type TableParams, TableRow, TableSearch, type TableSearchProps, TableSkeleton, type TableSkeletonProps, Tabs, TabsContent, type TabsContentProps, TabsList, type TabsListProps, type TabsProps, TabsTrigger, type TabsTriggerProps, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TreeNode, TreeView, type TreeViewProps, alertVariants, avatarVariants, badgeVariants, buttonVariants, cardVariants, dialogContentVariants, inputVariants, progressVariants, selectTriggerVariants, sheetVariants, spinnerVariants, usePortalContainer };
package/dist/index.d.ts CHANGED
@@ -2453,4 +2453,115 @@ declare const ResizableHandle: ({ withGrip, className, children, ...props }: rea
2453
2453
  withGrip?: boolean;
2454
2454
  }) => react_jsx_runtime.JSX.Element;
2455
2455
 
2456
- export { Accordion, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, type ActionCallbacks, Alert, AlertDescription, type AlertDescriptionProps, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, type AlertDialogCancelProps, AlertDialogContent, type AlertDialogContentProps, AlertDialogDescription, type AlertDialogDescriptionProps, AlertDialogFooter, type AlertDialogFooterProps, AlertDialogHeader, type AlertDialogHeaderProps, AlertDialogOverlay, type AlertDialogOverlayProps, AlertDialogPortal, AlertDialogTitle, type AlertDialogTitleProps, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTitleProps, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, type AvatarImageProps, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, type CommandDialogProps, CommandEmpty, type CommandEmptyProps, CommandGroup, type CommandGroupProps, CommandInput, type CommandInputProps, CommandItem, type CommandItemProps, CommandList, type CommandListProps, type CommandProps, CommandSeparator, type CommandSeparatorProps, CommandShortcut, type CommandShortcutProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, type DataFetcher, Dialog, DialogClose, DialogContent, type DialogContentProps, DialogDescription, type DialogDescriptionProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogOverlay, type DialogOverlayProps, DialogPortal, DialogTitle, type DialogTitleProps, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, type DropdownMenuCheckboxItemProps, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, type DropdownMenuLabelProps, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, type DropdownMenuRadioItemProps, DropdownMenuSeparator, type DropdownMenuSeparatorProps, DropdownMenuShortcut, type DropdownMenuShortcutProps, DropdownMenuSub, DropdownMenuSubContent, type DropdownMenuSubContentProps, DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, DropdownMenuTrigger, type FilterInfo, FormLabelWithTooltip, type FormLabelWithTooltipProps, Grid, type GridProps, Input, type InputProps, Label, type ListResponse, type PaginationConfig, type PaginationMeta, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalProvider, Progress, type ProgressProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, Sheet, SheetClose, type SheetCloseProps, SheetContent, type SheetContentProps, type SheetContentRef, SheetDescription, type SheetDescriptionProps, type SheetDescriptionRef, SheetFooter, type SheetFooterProps, SheetHeader, type SheetHeaderProps, SheetOverlay, type SheetOverlayProps, type SheetOverlayRef, SheetPortal, type SheetProps, SheetTitle, type SheetTitleProps, type SheetTitleRef, SheetTrigger, type SheetTriggerProps, Skeleton, type SkeletonProps, type SortInfo, Spinner, type SpinnerProps, Stack, type StackProps, Stat, type StatProps, Switch, Table, TableBody, TableCaption, TableCell, TableEmpty, type TableEmptyProps, TableError, type TableErrorProps, TableFooter, TableHead, TableHeader, TableLoading, type TableParams, TableRow, TableSearch, type TableSearchProps, TableSkeleton, type TableSkeletonProps, Tabs, TabsContent, type TabsContentProps, TabsList, type TabsListProps, type TabsProps, TabsTrigger, type TabsTriggerProps, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, avatarVariants, badgeVariants, buttonVariants, cardVariants, dialogContentVariants, inputVariants, progressVariants, selectTriggerVariants, sheetVariants, spinnerVariants, usePortalContainer };
2456
+ /**
2457
+ * TreeView
2458
+ *
2459
+ * A keyboard-operable, virtualized tree. The layers panel of an editor is this control: a
2460
+ * hierarchy that can hold thousands of nodes, that someone navigates with arrow keys as much as
2461
+ * with a pointer, and that has to stay responsive while they do.
2462
+ *
2463
+ * **Why virtualized, and why that decides the markup.** A document of a few thousand blocks
2464
+ * renders a few thousand rows, and the cost is not the React work — it is layout and paint on
2465
+ * every expand, scroll and selection. Only the visible window is rendered here.
2466
+ *
2467
+ * That has a consequence the accessibility notes below depend on: with only a window in the DOM,
2468
+ * the nested `role="group"` markup the tree pattern usually uses **cannot be built**, because an
2469
+ * item's children may not be rendered at all. The APG covers this exact case by allowing a FLAT
2470
+ * set of `treeitem`s that describe the hierarchy through `aria-level`, `aria-setsize` and
2471
+ * `aria-posinset` instead of through nesting. A screen reader reads depth and position from those
2472
+ * attributes, so they are not decoration — without them a virtualized tree announces itself as a
2473
+ * flat list of whatever happens to be on screen.
2474
+ *
2475
+ * **Why a headless virtualizer.** The markup above is the requirement, so anything that owns the
2476
+ * DOM is unusable. `@tanstack/react-virtual` computes offsets and renders nothing.
2477
+ *
2478
+ * **State is controllable, not controlled.** `expandedIds`/`selectedId` may be passed with their
2479
+ * `onChange` partners to drive the tree from a store — which an editor will do, since selection is
2480
+ * shared with the canvas and the inspector — or omitted entirely, in which case the tree keeps its
2481
+ * own. Requiring a store for a tree in a settings dialog would be a poor trade.
2482
+ *
2483
+ * **Design specifications**:
2484
+ * - Row height: fixed 28px (`--tree-row`), so the virtualizer needs no measurement pass
2485
+ * - Indent: 12px per level, applied as padding so the whole row stays a hit target
2486
+ * - Selection: `bg-muted`, matching the menu highlight rather than a full-contrast flip
2487
+ * - Focus: `focus-visible` ring in the focus token
2488
+ *
2489
+ * **Accessibility**:
2490
+ * - `role="tree"` with flat `role="treeitem"` children carrying `aria-level`, `aria-setsize`,
2491
+ * `aria-posinset`, and `aria-expanded` on anything with children
2492
+ * - Roving tabindex: exactly one row is in the tab order, so Tab enters and leaves the tree once
2493
+ * rather than walking every node
2494
+ * - Arrow keys move and expand, per the APG tree pattern: Right expands then descends, Left
2495
+ * collapses then ascends, Home/End jump to the ends, `*` expands every sibling
2496
+ * - Typeahead focuses the next row whose label starts with what was typed
2497
+ * - Moving focus scrolls the row into view, which virtualization would otherwise prevent: a row
2498
+ * outside the window has no element to focus
2499
+ *
2500
+ * @example
2501
+ * ```tsx
2502
+ * <TreeView
2503
+ * nodes={layers}
2504
+ * aria-label="Layers"
2505
+ * selectedId={selected}
2506
+ * onSelectedChange={setSelected}
2507
+ * className="h-full"
2508
+ * />
2509
+ * ```
2510
+ *
2511
+ * @module
2512
+ */
2513
+
2514
+ /**
2515
+ * One node of the tree. Children are omitted or empty for a leaf.
2516
+ *
2517
+ * @experimental
2518
+ */
2519
+ interface TreeNode {
2520
+ /** Stable identity. What selection and expansion are keyed by. */
2521
+ id: string;
2522
+ /** What the row shows. A string also feeds typeahead; anything else needs `textValue`. */
2523
+ label: react.ReactNode;
2524
+ /**
2525
+ * The text typeahead matches on, when `label` is not a string.
2526
+ *
2527
+ * Without it a row rendered as markup cannot be typed to, and a keyboard user loses the fastest
2528
+ * way through a long tree.
2529
+ */
2530
+ textValue?: string;
2531
+ /** Children, if any. An empty array still marks the node as a parent. */
2532
+ children?: readonly TreeNode[];
2533
+ /** Shown before the label, after the twisty. */
2534
+ icon?: react.ReactNode;
2535
+ /** Skipped by every keyboard move and not selectable. */
2536
+ disabled?: boolean;
2537
+ }
2538
+ /** @experimental */
2539
+ interface TreeViewProps extends Omit<react.HTMLAttributes<HTMLDivElement>, "onSelect"> {
2540
+ /** The roots of the tree. */
2541
+ nodes: readonly TreeNode[];
2542
+ /** Expanded node ids, if the caller owns them. */
2543
+ expandedIds?: readonly string[];
2544
+ /** Which ids start expanded when the caller does not own expansion. */
2545
+ defaultExpandedIds?: readonly string[];
2546
+ /** Called with the full next set whenever a branch opens or closes. */
2547
+ onExpandedChange?: (ids: string[]) => void;
2548
+ /** The selected node id, if the caller owns it. */
2549
+ selectedId?: string | null;
2550
+ /** Which id starts selected when the caller does not own selection. */
2551
+ defaultSelectedId?: string | null;
2552
+ /** Called when a row is chosen, by pointer or by Enter. */
2553
+ onSelectedChange?: (id: string) => void;
2554
+ /**
2555
+ * Names the tree for a screen reader. One of this or `aria-labelledby` is required: a tree
2556
+ * announced only as "tree" tells a user nothing about which one they are in.
2557
+ */
2558
+ "aria-label"?: string;
2559
+ }
2560
+ /**
2561
+ * A virtualized tree.
2562
+ *
2563
+ * @experimental
2564
+ */
2565
+ declare const TreeView: react.ForwardRefExoticComponent<TreeViewProps & react.RefAttributes<HTMLDivElement>>;
2566
+
2567
+ export { Accordion, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, type ActionCallbacks, Alert, AlertDescription, type AlertDescriptionProps, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, type AlertDialogCancelProps, AlertDialogContent, type AlertDialogContentProps, AlertDialogDescription, type AlertDialogDescriptionProps, AlertDialogFooter, type AlertDialogFooterProps, AlertDialogHeader, type AlertDialogHeaderProps, AlertDialogOverlay, type AlertDialogOverlayProps, AlertDialogPortal, AlertDialogTitle, type AlertDialogTitleProps, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTitleProps, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, type AvatarImageProps, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, type CommandDialogProps, CommandEmpty, type CommandEmptyProps, CommandGroup, type CommandGroupProps, CommandInput, type CommandInputProps, CommandItem, type CommandItemProps, CommandList, type CommandListProps, type CommandProps, CommandSeparator, type CommandSeparatorProps, CommandShortcut, type CommandShortcutProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, type DataFetcher, Dialog, DialogClose, DialogContent, type DialogContentProps, DialogDescription, type DialogDescriptionProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogOverlay, type DialogOverlayProps, DialogPortal, DialogTitle, type DialogTitleProps, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, type DropdownMenuCheckboxItemProps, DropdownMenuContent, type DropdownMenuContentProps, DropdownMenuGroup, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuLabel, type DropdownMenuLabelProps, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, type DropdownMenuRadioItemProps, DropdownMenuSeparator, type DropdownMenuSeparatorProps, DropdownMenuShortcut, type DropdownMenuShortcutProps, DropdownMenuSub, DropdownMenuSubContent, type DropdownMenuSubContentProps, DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, DropdownMenuTrigger, type FilterInfo, FormLabelWithTooltip, type FormLabelWithTooltipProps, Grid, type GridProps, Input, type InputProps, Label, type ListResponse, type PaginationConfig, type PaginationMeta, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalProvider, Progress, type ProgressProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, Sheet, SheetClose, type SheetCloseProps, SheetContent, type SheetContentProps, type SheetContentRef, SheetDescription, type SheetDescriptionProps, type SheetDescriptionRef, SheetFooter, type SheetFooterProps, SheetHeader, type SheetHeaderProps, SheetOverlay, type SheetOverlayProps, type SheetOverlayRef, SheetPortal, type SheetProps, SheetTitle, type SheetTitleProps, type SheetTitleRef, SheetTrigger, type SheetTriggerProps, Skeleton, type SkeletonProps, type SortInfo, Spinner, type SpinnerProps, Stack, type StackProps, Stat, type StatProps, Switch, Table, TableBody, TableCaption, TableCell, TableEmpty, type TableEmptyProps, TableError, type TableErrorProps, TableFooter, TableHead, TableHeader, TableLoading, type TableParams, TableRow, TableSearch, type TableSearchProps, TableSkeleton, type TableSkeletonProps, Tabs, TabsContent, type TabsContentProps, TabsList, type TabsListProps, type TabsProps, TabsTrigger, type TabsTriggerProps, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TreeNode, TreeView, type TreeViewProps, alertVariants, avatarVariants, badgeVariants, buttonVariants, cardVariants, dialogContentVariants, inputVariants, progressVariants, selectTriggerVariants, sheetVariants, spinnerVariants, usePortalContainer };
package/dist/index.mjs CHANGED
@@ -2317,6 +2317,325 @@ var ResizableHandle = ({
2317
2317
  ]
2318
2318
  }
2319
2319
  );
2320
+
2321
+ // src/components/tree-view.tsx
2322
+ import { useVirtualizer } from "@tanstack/react-virtual";
2323
+ import { ChevronRight as ChevronRight3 } from "lucide-react";
2324
+ import * as React10 from "react";
2325
+ import { jsx as jsx36, jsxs as jsxs15 } from "react/jsx-runtime";
2326
+ var ROW_HEIGHT = 28;
2327
+ var INDENT_PER_LEVEL = 12;
2328
+ function textOf(node) {
2329
+ if (typeof node.textValue === "string") return node.textValue;
2330
+ return typeof node.label === "string" ? node.label : "";
2331
+ }
2332
+ function flatten(nodes, expanded) {
2333
+ const rows = [];
2334
+ const pending = [{ list: nodes, index: 0, level: 0 }];
2335
+ while (pending.length > 0) {
2336
+ const frame = pending[pending.length - 1];
2337
+ if (frame === void 0 || frame.index >= frame.list.length) {
2338
+ pending.pop();
2339
+ continue;
2340
+ }
2341
+ const node = frame.list[frame.index];
2342
+ const posInSet = frame.index;
2343
+ frame.index += 1;
2344
+ if (node === void 0) continue;
2345
+ const hasChildren = node.children !== void 0;
2346
+ rows.push({
2347
+ node,
2348
+ level: frame.level,
2349
+ setSize: frame.list.length,
2350
+ posInSet,
2351
+ parentId: frame.parentId,
2352
+ hasChildren
2353
+ });
2354
+ if (hasChildren && expanded.has(node.id)) {
2355
+ pending.push({
2356
+ list: node.children ?? [],
2357
+ index: 0,
2358
+ level: frame.level + 1,
2359
+ parentId: node.id
2360
+ });
2361
+ }
2362
+ }
2363
+ return rows;
2364
+ }
2365
+ function useControllable(controlled, fallback) {
2366
+ const [uncontrolled, setUncontrolled] = React10.useState(fallback);
2367
+ return [
2368
+ controlled === void 0 ? uncontrolled : controlled,
2369
+ setUncontrolled
2370
+ ];
2371
+ }
2372
+ var TreeView = React10.forwardRef(
2373
+ ({
2374
+ nodes,
2375
+ expandedIds,
2376
+ defaultExpandedIds,
2377
+ onExpandedChange,
2378
+ selectedId,
2379
+ defaultSelectedId,
2380
+ onSelectedChange,
2381
+ className,
2382
+ "aria-label": ariaLabel,
2383
+ "aria-labelledby": ariaLabelledBy,
2384
+ "aria-describedby": ariaDescribedBy,
2385
+ ...props
2386
+ }, forwardedRef) => {
2387
+ const scrollRef = React10.useRef(null);
2388
+ const attachScroll = React10.useCallback(
2389
+ (node) => {
2390
+ scrollRef.current = node;
2391
+ if (typeof forwardedRef === "function") forwardedRef(node);
2392
+ else if (forwardedRef !== null && forwardedRef !== void 0) {
2393
+ forwardedRef.current = node;
2394
+ }
2395
+ },
2396
+ [forwardedRef]
2397
+ );
2398
+ const [expandedState, setExpandedState] = useControllable(
2399
+ expandedIds === void 0 ? void 0 : [...expandedIds],
2400
+ [...defaultExpandedIds ?? []]
2401
+ );
2402
+ const expanded = React10.useMemo(
2403
+ () => new Set(expandedIds ?? expandedState),
2404
+ [expandedIds, expandedState]
2405
+ );
2406
+ const [selected, setSelected] = useControllable(
2407
+ selectedId === void 0 ? void 0 : selectedId,
2408
+ defaultSelectedId ?? null
2409
+ );
2410
+ const rows = React10.useMemo(
2411
+ () => flatten(nodes, expanded),
2412
+ [nodes, expanded]
2413
+ );
2414
+ const [activeId, setActiveId] = React10.useState(null);
2415
+ const activeIndex = Math.max(
2416
+ 0,
2417
+ rows.findIndex((row) => row.node.id === (activeId ?? selected))
2418
+ );
2419
+ const virtualizer = useVirtualizer({
2420
+ count: rows.length,
2421
+ getScrollElement: () => scrollRef.current,
2422
+ estimateSize: () => ROW_HEIGHT,
2423
+ overscan: 8
2424
+ });
2425
+ const commitExpanded = (next) => {
2426
+ const ids = [...next];
2427
+ if (expandedIds === void 0) setExpandedState(ids);
2428
+ onExpandedChange?.(ids);
2429
+ };
2430
+ const setExpansion = (id, open) => {
2431
+ const next = new Set(expanded);
2432
+ if (open) next.add(id);
2433
+ else next.delete(id);
2434
+ commitExpanded(next);
2435
+ };
2436
+ const choose = (id) => {
2437
+ if (selectedId === void 0) setSelected(id);
2438
+ onSelectedChange?.(id);
2439
+ };
2440
+ const focusRow = (index) => {
2441
+ const row = rows[index];
2442
+ if (row === void 0) return;
2443
+ setActiveId(row.node.id);
2444
+ virtualizer.scrollToIndex(index, { align: "auto" });
2445
+ requestAnimationFrame(() => {
2446
+ const element = scrollRef.current?.querySelector(
2447
+ `[data-tree-index="${index}"]`
2448
+ );
2449
+ element?.focus();
2450
+ });
2451
+ };
2452
+ const step = (from, delta) => {
2453
+ for (let index = from + delta; index >= 0 && index < rows.length; index += delta) {
2454
+ if (rows[index]?.node.disabled !== true) return index;
2455
+ }
2456
+ return from;
2457
+ };
2458
+ const typeahead = React10.useRef({ query: "", at: 0 });
2459
+ const onKeyDown = (event) => {
2460
+ const index = activeIndex;
2461
+ const row = rows[index];
2462
+ if (row === void 0) return;
2463
+ switch (event.key) {
2464
+ case "ArrowDown":
2465
+ event.preventDefault();
2466
+ focusRow(step(index, 1));
2467
+ return;
2468
+ case "ArrowUp":
2469
+ event.preventDefault();
2470
+ focusRow(step(index, -1));
2471
+ return;
2472
+ case "ArrowRight":
2473
+ event.preventDefault();
2474
+ if (row.hasChildren && !expanded.has(row.node.id)) {
2475
+ setExpansion(row.node.id, true);
2476
+ } else if (row.hasChildren) {
2477
+ for (let child = index + 1; child < rows.length && (rows[child]?.level ?? 0) > row.level; child += 1) {
2478
+ if (rows[child]?.parentId === row.node.id && rows[child]?.node.disabled !== true) {
2479
+ focusRow(child);
2480
+ break;
2481
+ }
2482
+ }
2483
+ }
2484
+ return;
2485
+ case "ArrowLeft":
2486
+ event.preventDefault();
2487
+ if (row.hasChildren && expanded.has(row.node.id)) {
2488
+ setExpansion(row.node.id, false);
2489
+ } else if (row.parentId !== void 0) {
2490
+ let ancestor = row.parentId;
2491
+ while (ancestor !== void 0) {
2492
+ const at = rows.findIndex(
2493
+ (candidate) => candidate.node.id === ancestor
2494
+ );
2495
+ if (at < 0) break;
2496
+ if (rows[at]?.node.disabled !== true) {
2497
+ focusRow(at);
2498
+ break;
2499
+ }
2500
+ ancestor = rows[at]?.parentId;
2501
+ }
2502
+ }
2503
+ return;
2504
+ case "Home":
2505
+ event.preventDefault();
2506
+ focusRow(rows[0]?.node.disabled === true ? step(0, 1) : 0);
2507
+ return;
2508
+ case "End": {
2509
+ event.preventDefault();
2510
+ const last = rows.length - 1;
2511
+ focusRow(rows[last]?.node.disabled === true ? step(last, -1) : last);
2512
+ return;
2513
+ }
2514
+ case "Enter":
2515
+ case " ":
2516
+ event.preventDefault();
2517
+ if (row.node.disabled !== true) choose(row.node.id);
2518
+ return;
2519
+ case "*": {
2520
+ event.preventDefault();
2521
+ const next = new Set(expanded);
2522
+ for (const sibling of rows) {
2523
+ if (sibling.parentId === row.parentId && sibling.hasChildren) {
2524
+ next.add(sibling.node.id);
2525
+ }
2526
+ }
2527
+ commitExpanded(next);
2528
+ return;
2529
+ }
2530
+ default:
2531
+ break;
2532
+ }
2533
+ if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey) {
2534
+ return;
2535
+ }
2536
+ event.preventDefault();
2537
+ const now = Date.now();
2538
+ const state = typeahead.current;
2539
+ state.query = now - state.at > 500 ? event.key : state.query + event.key;
2540
+ state.at = now;
2541
+ const query = state.query.toLowerCase();
2542
+ for (let offset = 1; offset <= rows.length; offset += 1) {
2543
+ const candidate = rows[(index + offset) % rows.length];
2544
+ if (candidate === void 0 || candidate.node.disabled === true)
2545
+ continue;
2546
+ if (textOf(candidate.node).toLowerCase().startsWith(query)) {
2547
+ focusRow(rows.indexOf(candidate));
2548
+ return;
2549
+ }
2550
+ }
2551
+ };
2552
+ const virtualItems = virtualizer.getVirtualItems();
2553
+ const usable = (index) => rows[index]?.node.disabled !== true;
2554
+ const tabStopIndex = virtualItems.some((item) => item.index === activeIndex) && usable(activeIndex) ? activeIndex : virtualItems.find((item) => usable(item.index))?.index ?? -1;
2555
+ return /* @__PURE__ */ jsx36(
2556
+ "div",
2557
+ {
2558
+ ref: attachScroll,
2559
+ className: cn("overflow-auto", className),
2560
+ ...props,
2561
+ children: /* @__PURE__ */ jsx36(
2562
+ "div",
2563
+ {
2564
+ role: "tree",
2565
+ "aria-label": ariaLabel,
2566
+ "aria-labelledby": ariaLabelledBy,
2567
+ "aria-describedby": ariaDescribedBy,
2568
+ onKeyDown,
2569
+ style: { height: virtualizer.getTotalSize(), position: "relative" },
2570
+ children: virtualItems.map((item) => {
2571
+ const row = rows[item.index];
2572
+ if (row === void 0) return null;
2573
+ const isSelected = selected === row.node.id;
2574
+ return /* @__PURE__ */ jsxs15(
2575
+ "div",
2576
+ {
2577
+ "data-tree-index": item.index,
2578
+ role: "treeitem",
2579
+ "aria-level": row.level + 1,
2580
+ "aria-setsize": row.setSize,
2581
+ "aria-posinset": row.posInSet + 1,
2582
+ "aria-selected": isSelected,
2583
+ "aria-expanded": row.hasChildren ? expanded.has(row.node.id) : void 0,
2584
+ "aria-disabled": row.node.disabled === true ? true : void 0,
2585
+ tabIndex: item.index === tabStopIndex ? 0 : -1,
2586
+ onFocus: () => setActiveId(row.node.id),
2587
+ onClick: () => {
2588
+ if (row.node.disabled === true) return;
2589
+ setActiveId(row.node.id);
2590
+ choose(row.node.id);
2591
+ },
2592
+ className: cn(
2593
+ "absolute left-0 flex w-full select-none items-center gap-1 rounded-sm pr-2 text-sm outline-none",
2594
+ "focus-visible:ring-1 focus-visible:ring-ring",
2595
+ row.node.disabled === true ? "pointer-events-none opacity-50" : "cursor-pointer",
2596
+ isSelected ? "bg-muted text-foreground" : "hover:bg-muted/50"
2597
+ ),
2598
+ style: {
2599
+ height: item.size,
2600
+ transform: `translateY(${item.start}px)`,
2601
+ paddingLeft: 4 + row.level * INDENT_PER_LEVEL
2602
+ },
2603
+ children: [
2604
+ /* @__PURE__ */ jsx36(
2605
+ "span",
2606
+ {
2607
+ "aria-hidden": "true",
2608
+ className: "flex size-4 shrink-0 items-center justify-center",
2609
+ onClick: (event) => {
2610
+ if (!row.hasChildren) return;
2611
+ event.stopPropagation();
2612
+ setExpansion(row.node.id, !expanded.has(row.node.id));
2613
+ },
2614
+ children: row.hasChildren ? /* @__PURE__ */ jsx36(
2615
+ ChevronRight3,
2616
+ {
2617
+ className: cn(
2618
+ "size-3.5 text-muted-foreground transition-transform",
2619
+ expanded.has(row.node.id) && "rotate-90"
2620
+ )
2621
+ }
2622
+ ) : null
2623
+ }
2624
+ ),
2625
+ row.node.icon !== void 0 ? /* @__PURE__ */ jsx36("span", { className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground", children: row.node.icon }) : null,
2626
+ /* @__PURE__ */ jsx36("span", { className: "truncate", children: row.node.label })
2627
+ ]
2628
+ },
2629
+ row.node.id
2630
+ );
2631
+ })
2632
+ }
2633
+ )
2634
+ }
2635
+ );
2636
+ }
2637
+ );
2638
+ TreeView.displayName = "TreeView";
2320
2639
  export {
2321
2640
  Accordion,
2322
2641
  AccordionContent,
@@ -2464,6 +2783,7 @@ export {
2464
2783
  TooltipContent,
2465
2784
  TooltipProvider,
2466
2785
  TooltipTrigger,
2786
+ TreeView,
2467
2787
  alertVariants,
2468
2788
  avatarVariants,
2469
2789
  badgeVariants,