@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/README.md +1 -0
- package/dist/index.cjs +321 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -1
- package/dist/index.d.ts +112 -1
- package/dist/index.mjs +320 -0
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.scoped.css +1 -1
- package/package.json +5 -4
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
|
-
|
|
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
|
-
|
|
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,
|