@vesture/react 0.2.9 → 0.2.10

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.ts CHANGED
@@ -539,4 +539,47 @@ interface TreeViewProps<T = unknown> {
539
539
 
540
540
  declare function TreeView<T = unknown>({ nodes, expanded: controlledExpanded, defaultExpanded, onExpandedChange, selected: controlledSelected, defaultSelected, onSelectedChange, selectable, onLoadChildren, height, rowHeight, }: TreeViewProps<T>): ReactElement;
541
541
 
542
- export { Accordion, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, type AvatarStatus, Badge, type BadgeProps, type BadgeVariant, Breadcrumbs, BreadcrumbsItem, type BreadcrumbsItemProps, type BreadcrumbsProps, Button, type ButtonProps, type ButtonVariant, Calendar, type CalendarProps, Card, type CardElevation, type CardProps, Checkbox, type CheckboxProps, Combobox, type ComboboxOption, type ComboboxProps, DataGrid, type DataGridColumn, type DataGridExportOptions, type DataGridHandle, type DataGridProps, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, Divider, type DividerOrientation, type DividerProps, DropdownMenu, DropdownMenuItem, type DropdownMenuItemProps, type DropdownMenuProps, Input, type InputProps, Label, type LabelProps, Modal, type ModalProps, NumberInput, type NumberInputProps, Pagination, type PaginationProps, Popover, type PopoverProps, Progress, type ProgressProps, Radio, type RadioProps, Select, type SelectProps, Slider, type SliderProps, type SliderValue, type SortDirection, type SortState, Spinner, type SpinnerProps, type SpinnerSize, Stack, type StackAlign, type StackDirection, type StackGap, type StackJustify, type StackProps, Switch, type SwitchProps, Tabs, TabsList, type TabsListProps, TabsPanel, type TabsPanelProps, type TabsProps, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, type ToastContextValue, type ToastOptions, ToastProvider, type ToastVariant, Tooltip, type TooltipProps, type TreeNode, TreeView, type TreeViewProps, type TreeViewSelectable, useToast };
542
+ interface CommandItem {
543
+ id: string;
544
+ label: string;
545
+ description?: string;
546
+ icon?: ReactNode;
547
+ /** Extra searchable terms not shown in the label. */
548
+ keywords?: string[];
549
+ /** Display-only, e.g. "⌘K". */
550
+ shortcut?: string;
551
+ /** Groups render under a section header, in the order groups first appear in `commands`. */
552
+ group?: string;
553
+ onSelect: () => void;
554
+ disabled?: boolean;
555
+ }
556
+ interface CommandPaletteProps {
557
+ open: boolean;
558
+ onOpenChange: (open: boolean) => void;
559
+ commands: CommandItem[];
560
+ placeholder?: string;
561
+ emptyMessage?: string;
562
+ /** Overrides the default case-insensitive substring match against label + keywords + group. */
563
+ filterCommands?: (commands: CommandItem[], query: string) => CommandItem[];
564
+ /** Number of rows (commands + group headers) at/above which the list switches to virtualized rendering. */
565
+ virtualizationThreshold?: number;
566
+ /** Row height (px) used for virtualized rows; defaults to the `.option` class's rendered height. */
567
+ rowHeight?: number;
568
+ "aria-label"?: string;
569
+ }
570
+
571
+ declare function CommandPalette({ open, onOpenChange, commands, placeholder, emptyMessage, filterCommands, virtualizationThreshold, rowHeight, "aria-label": ariaLabel }: CommandPaletteProps): ReactElement | null;
572
+
573
+ interface UseCommandPaletteShortcutOptions {
574
+ key?: string;
575
+ metaKey?: boolean;
576
+ ctrlKey?: boolean;
577
+ }
578
+ /**
579
+ * Attaches a document-level Cmd+K (Mac) / Ctrl+K (elsewhere) listener that
580
+ * calls onOpenChange(true). Separate from CommandPalette so consumers can
581
+ * wire their own trigger instead.
582
+ */
583
+ declare function useCommandPaletteShortcut(onOpenChange: (open: boolean) => void, options?: UseCommandPaletteShortcutOptions): void;
584
+
585
+ export { Accordion, AccordionContent, type AccordionContentProps, AccordionItem, type AccordionItemProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, type AvatarStatus, Badge, type BadgeProps, type BadgeVariant, Breadcrumbs, BreadcrumbsItem, type BreadcrumbsItemProps, type BreadcrumbsProps, Button, type ButtonProps, type ButtonVariant, Calendar, type CalendarProps, Card, type CardElevation, type CardProps, Checkbox, type CheckboxProps, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, DataGrid, type DataGridColumn, type DataGridExportOptions, type DataGridHandle, type DataGridProps, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, Divider, type DividerOrientation, type DividerProps, DropdownMenu, DropdownMenuItem, type DropdownMenuItemProps, type DropdownMenuProps, Input, type InputProps, Label, type LabelProps, Modal, type ModalProps, NumberInput, type NumberInputProps, Pagination, type PaginationProps, Popover, type PopoverProps, Progress, type ProgressProps, Radio, type RadioProps, Select, type SelectProps, Slider, type SliderProps, type SliderValue, type SortDirection, type SortState, Spinner, type SpinnerProps, type SpinnerSize, Stack, type StackAlign, type StackDirection, type StackGap, type StackJustify, type StackProps, Switch, type SwitchProps, Tabs, TabsList, type TabsListProps, TabsPanel, type TabsPanelProps, type TabsProps, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, type ToastContextValue, type ToastOptions, ToastProvider, type ToastVariant, Tooltip, type TooltipProps, type TreeNode, TreeView, type TreeViewProps, type TreeViewSelectable, type UseCommandPaletteShortcutOptions, useCommandPaletteShortcut, useToast };
package/dist/index.js CHANGED
@@ -3657,8 +3657,8 @@ function TreeView({
3657
3657
  const rowRefs = useRef10(/* @__PURE__ */ new Map());
3658
3658
  const nodeIndex = useMemo4(() => {
3659
3659
  const map = /* @__PURE__ */ new Map();
3660
- function walk(list4, parentId) {
3661
- for (const node of list4) {
3660
+ function walk(list5, parentId) {
3661
+ for (const node of list5) {
3662
3662
  map.set(node.id, { node, parentId });
3663
3663
  const children = getChildren(node, loadedChildren);
3664
3664
  if (children) walk(children, node.id);
@@ -3669,8 +3669,8 @@ function TreeView({
3669
3669
  }, [nodes, loadedChildren]);
3670
3670
  const flatNodes = useMemo4(() => {
3671
3671
  const result = [];
3672
- function walk(list4, depth) {
3673
- for (const node of list4) {
3672
+ function walk(list5, depth) {
3673
+ for (const node of list5) {
3674
3674
  const children = getChildren(node, loadedChildren);
3675
3675
  const nodeHasChildren = children ? children.length > 0 : node.hasChildren === true;
3676
3676
  const isExpanded = expandedSet.has(node.id);
@@ -3935,6 +3935,284 @@ function TreeView({
3935
3935
  );
3936
3936
  }
3937
3937
 
3938
+ // src/components/CommandPalette/CommandPalette.tsx
3939
+ import { useEffect as useEffect9, useId as useId3, useMemo as useMemo5, useRef as useRef11, useState as useState16 } from "react";
3940
+ import {
3941
+ FloatingFocusManager as FloatingFocusManager6,
3942
+ FloatingOverlay as FloatingOverlay2,
3943
+ FloatingPortal as FloatingPortal8,
3944
+ useDismiss as useDismiss8,
3945
+ useFloating as useFloating8,
3946
+ useInteractions as useInteractions8,
3947
+ useRole as useRole8
3948
+ } from "@floating-ui/react";
3949
+
3950
+ // src/components/CommandPalette/CommandPalette.css.ts
3951
+ var emptyState3 = "CommandPalette_emptyState__14otg5db";
3952
+ var groupHeader = "CommandPalette_groupHeader__14otg5d5";
3953
+ var inputEl2 = "CommandPalette_inputEl__14otg5d2";
3954
+ var list4 = "CommandPalette_list__14otg5d3";
3955
+ var option2 = "CommandPalette_option__14otg5d6";
3956
+ var optionDescription = "CommandPalette_optionDescription__14otg5d9";
3957
+ var optionIcon = "CommandPalette_optionIcon__14otg5d7";
3958
+ var optionLabel = "CommandPalette_optionLabel__14otg5d8";
3959
+ var overlay2 = "CommandPalette_overlay__14otg5d0";
3960
+ var palette = "CommandPalette_palette__14otg5d1";
3961
+ var shortcut = "CommandPalette_shortcut__14otg5da";
3962
+ var virtualSpacer2 = "CommandPalette_virtualSpacer__14otg5d4";
3963
+
3964
+ // src/components/CommandPalette/CommandPalette.tsx
3965
+ import { jsx as jsx42, jsxs as jsxs25 } from "react/jsx-runtime";
3966
+ var DEFAULT_ROW_HEIGHT = 40;
3967
+ var OVERSCAN2 = 5;
3968
+ function defaultFilterCommands(commands, query) {
3969
+ const q = query.trim().toLowerCase();
3970
+ if (!q) return commands;
3971
+ return commands.filter((command) => {
3972
+ const haystack = [command.label, command.group, ...command.keywords ?? []].filter(Boolean).join(" ").toLowerCase();
3973
+ return haystack.includes(q);
3974
+ });
3975
+ }
3976
+ function buildRows(commands) {
3977
+ const rows = [];
3978
+ const itemRowIndices = [];
3979
+ const seenGroups = /* @__PURE__ */ new Set();
3980
+ commands.forEach((command, itemIndex) => {
3981
+ if (command.group && !seenGroups.has(command.group)) {
3982
+ seenGroups.add(command.group);
3983
+ rows.push({ type: "header", key: `__group-${command.group}`, group: command.group });
3984
+ }
3985
+ itemRowIndices[itemIndex] = rows.length;
3986
+ rows.push({ type: "item", key: command.id, command, itemIndex });
3987
+ });
3988
+ return { rows, itemRowIndices };
3989
+ }
3990
+ function CommandPalette({
3991
+ open,
3992
+ onOpenChange,
3993
+ commands,
3994
+ placeholder = "Type a command or search...",
3995
+ emptyMessage = "No matching commands",
3996
+ filterCommands = defaultFilterCommands,
3997
+ virtualizationThreshold = 50,
3998
+ rowHeight = DEFAULT_ROW_HEIGHT,
3999
+ "aria-label": ariaLabel
4000
+ }) {
4001
+ const [query, setQuery] = useState16("");
4002
+ const [activeIndex, setActiveIndex] = useState16(null);
4003
+ const [scrollTop, setScrollTop] = useState16(0);
4004
+ const [viewportHeight, setViewportHeight] = useState16(320);
4005
+ const inputRef = useRef11(null);
4006
+ const listRef = useRef11(null);
4007
+ const listId = useId3();
4008
+ const { refs, context } = useFloating8({ open, onOpenChange });
4009
+ const { getFloatingProps } = useInteractions8([useDismiss8(context), useRole8(context, { role: "dialog" })]);
4010
+ const filteredCommands = useMemo5(() => filterCommands(commands, query), [commands, filterCommands, query]);
4011
+ const { rows, itemRowIndices } = useMemo5(() => buildRows(filteredCommands), [filteredCommands]);
4012
+ const isVirtualized = rows.length >= virtualizationThreshold;
4013
+ useEffect9(() => {
4014
+ if (!open) return;
4015
+ setQuery("");
4016
+ setActiveIndex(null);
4017
+ setScrollTop(0);
4018
+ if (listRef.current) listRef.current.scrollTop = 0;
4019
+ }, [open]);
4020
+ useEffect9(() => {
4021
+ setActiveIndex(null);
4022
+ setScrollTop(0);
4023
+ if (listRef.current) listRef.current.scrollTop = 0;
4024
+ }, [query]);
4025
+ useEffect9(() => {
4026
+ if (!open) return;
4027
+ const node = listRef.current;
4028
+ if (!node) return;
4029
+ if (node.clientHeight > 0) setViewportHeight(node.clientHeight);
4030
+ if (typeof ResizeObserver === "undefined") return;
4031
+ const observer = new ResizeObserver((entries) => {
4032
+ const entry = entries[0];
4033
+ if (entry) setViewportHeight(entry.contentRect.height);
4034
+ });
4035
+ observer.observe(node);
4036
+ return () => observer.disconnect();
4037
+ }, [open]);
4038
+ if (!open) {
4039
+ return null;
4040
+ }
4041
+ const totalHeight = rows.length * rowHeight;
4042
+ const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
4043
+ const effectiveScrollTop = Math.min(scrollTop, maxScrollTop);
4044
+ const visibleCount = Math.ceil(viewportHeight / rowHeight) + OVERSCAN2 * 2;
4045
+ const startIndex = isVirtualized ? Math.max(0, Math.floor(effectiveScrollTop / rowHeight) - OVERSCAN2) : 0;
4046
+ const endIndex = isVirtualized ? Math.min(rows.length, startIndex + visibleCount) : rows.length;
4047
+ const visibleRows = rows.slice(startIndex, endIndex);
4048
+ const scrollRowIntoView = (rowIndex) => {
4049
+ if (!isVirtualized) return;
4050
+ const rowTop = rowIndex * rowHeight;
4051
+ const rowBottom = rowTop + rowHeight;
4052
+ let next = scrollTop;
4053
+ if (rowTop < scrollTop) {
4054
+ next = rowTop;
4055
+ } else if (rowBottom > scrollTop + viewportHeight) {
4056
+ next = rowBottom - viewportHeight;
4057
+ } else {
4058
+ return;
4059
+ }
4060
+ setScrollTop(next);
4061
+ if (listRef.current) listRef.current.scrollTop = next;
4062
+ };
4063
+ const moveActiveIndex = (direction2) => {
4064
+ if (filteredCommands.length === 0) return;
4065
+ let next = activeIndex ?? (direction2 === 1 ? -1 : filteredCommands.length);
4066
+ for (let step = 0; step < filteredCommands.length; step++) {
4067
+ next = (next + direction2 + filteredCommands.length) % filteredCommands.length;
4068
+ if (!filteredCommands[next]?.disabled) {
4069
+ setActiveIndex(next);
4070
+ const rowIndex = itemRowIndices[next];
4071
+ if (rowIndex !== void 0) scrollRowIntoView(rowIndex);
4072
+ return;
4073
+ }
4074
+ }
4075
+ };
4076
+ const selectCommand = (command) => {
4077
+ if (command.disabled) return;
4078
+ command.onSelect();
4079
+ onOpenChange(false);
4080
+ };
4081
+ const handleKeyDown = (event) => {
4082
+ switch (event.key) {
4083
+ case "ArrowDown":
4084
+ event.preventDefault();
4085
+ moveActiveIndex(1);
4086
+ return;
4087
+ case "ArrowUp":
4088
+ event.preventDefault();
4089
+ moveActiveIndex(-1);
4090
+ return;
4091
+ case "Enter": {
4092
+ event.preventDefault();
4093
+ const active = activeIndex !== null ? filteredCommands[activeIndex] : void 0;
4094
+ if (active) selectCommand(active);
4095
+ return;
4096
+ }
4097
+ case "Escape":
4098
+ event.preventDefault();
4099
+ onOpenChange(false);
4100
+ return;
4101
+ default:
4102
+ }
4103
+ };
4104
+ const activeCommand = activeIndex !== null ? filteredCommands[activeIndex] : void 0;
4105
+ const activeOptionId = activeCommand ? `${listId}-option-${activeCommand.id}` : void 0;
4106
+ const renderRow = (row3, rowIndex, rowStyle) => {
4107
+ if (row3.type === "header") {
4108
+ return /* @__PURE__ */ jsx42("div", { role: "presentation", className: groupHeader, style: rowStyle, children: row3.group }, row3.key);
4109
+ }
4110
+ const { command, itemIndex } = row3;
4111
+ const isActive = itemIndex === activeIndex;
4112
+ return /* @__PURE__ */ jsxs25(
4113
+ "div",
4114
+ {
4115
+ id: `${listId}-option-${command.id}`,
4116
+ role: "option",
4117
+ "aria-selected": isActive,
4118
+ "aria-disabled": command.disabled || void 0,
4119
+ "data-active": isActive || void 0,
4120
+ className: option2,
4121
+ style: rowStyle,
4122
+ onMouseDown: (event) => {
4123
+ event.preventDefault();
4124
+ selectCommand(command);
4125
+ },
4126
+ onMouseEnter: () => {
4127
+ if (!command.disabled) setActiveIndex(itemIndex);
4128
+ },
4129
+ children: [
4130
+ command.icon ? /* @__PURE__ */ jsx42("span", { className: optionIcon, children: command.icon }) : null,
4131
+ /* @__PURE__ */ jsxs25("span", { className: optionLabel, children: [
4132
+ command.label,
4133
+ command.description ? /* @__PURE__ */ jsx42("span", { className: optionDescription, children: command.description }) : null
4134
+ ] }),
4135
+ command.shortcut ? /* @__PURE__ */ jsx42("span", { className: shortcut, children: command.shortcut }) : null
4136
+ ]
4137
+ },
4138
+ row3.key
4139
+ );
4140
+ };
4141
+ return /* @__PURE__ */ jsx42(FloatingPortal8, { children: /* @__PURE__ */ jsx42(FloatingOverlay2, { className: overlay2, lockScroll: true, children: /* @__PURE__ */ jsx42(FloatingFocusManager6, { context, initialFocus: inputRef, children: /* @__PURE__ */ jsxs25(
4142
+ "div",
4143
+ {
4144
+ ref: refs.setFloating,
4145
+ className: palette,
4146
+ "aria-label": ariaLabel ?? "Command palette",
4147
+ ...getFloatingProps(),
4148
+ children: [
4149
+ /* @__PURE__ */ jsx42(
4150
+ "input",
4151
+ {
4152
+ ref: inputRef,
4153
+ type: "text",
4154
+ role: "combobox",
4155
+ className: inputEl2,
4156
+ value: query,
4157
+ placeholder,
4158
+ autoComplete: "off",
4159
+ "aria-autocomplete": "list",
4160
+ "aria-expanded": true,
4161
+ "aria-controls": listId,
4162
+ "aria-activedescendant": activeOptionId,
4163
+ onChange: (event) => setQuery(event.target.value),
4164
+ onKeyDown: handleKeyDown
4165
+ }
4166
+ ),
4167
+ /* @__PURE__ */ jsx42(
4168
+ "div",
4169
+ {
4170
+ ref: listRef,
4171
+ id: listId,
4172
+ role: "listbox",
4173
+ className: list4,
4174
+ onScroll: (event) => setScrollTop(event.currentTarget.scrollTop),
4175
+ children: filteredCommands.length === 0 ? /* @__PURE__ */ jsx42("div", { className: emptyState3, children: emptyMessage }) : isVirtualized ? /* @__PURE__ */ jsx42("div", { className: virtualSpacer2, style: { height: totalHeight }, children: visibleRows.map((row3, i) => {
4176
+ const rowIndex = startIndex + i;
4177
+ return renderRow(row3, rowIndex, {
4178
+ position: "absolute",
4179
+ top: rowIndex * rowHeight,
4180
+ left: 0,
4181
+ right: 0,
4182
+ height: rowHeight
4183
+ });
4184
+ }) }) : rows.map((row3, rowIndex) => renderRow(row3, rowIndex))
4185
+ }
4186
+ )
4187
+ ]
4188
+ }
4189
+ ) }) }) });
4190
+ }
4191
+
4192
+ // src/components/CommandPalette/useCommandPaletteShortcut.ts
4193
+ import { useEffect as useEffect10 } from "react";
4194
+ function isMac() {
4195
+ if (typeof navigator === "undefined") return false;
4196
+ return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? navigator.userAgent ?? "");
4197
+ }
4198
+ function useCommandPaletteShortcut(onOpenChange, options = {}) {
4199
+ const { key = "k", metaKey, ctrlKey } = options;
4200
+ useEffect10(() => {
4201
+ const explicit = metaKey !== void 0 || ctrlKey !== void 0;
4202
+ const useMeta = explicit ? Boolean(metaKey) : isMac();
4203
+ const useCtrl = explicit ? Boolean(ctrlKey) : !isMac();
4204
+ const handleKeyDown = (event) => {
4205
+ if (event.key.toLowerCase() !== key.toLowerCase()) return;
4206
+ if (event.metaKey !== useMeta) return;
4207
+ if (event.ctrlKey !== useCtrl) return;
4208
+ event.preventDefault();
4209
+ onOpenChange(true);
4210
+ };
4211
+ document.addEventListener("keydown", handleKeyDown);
4212
+ return () => document.removeEventListener("keydown", handleKeyDown);
4213
+ }, [onOpenChange, key, metaKey, ctrlKey]);
4214
+ }
4215
+
3938
4216
  // src/index.ts
3939
4217
  import { vars as vars3, defaultThemeClass, defaultThemeVars } from "@vesture/tokens";
3940
4218
  export {
@@ -3952,6 +4230,7 @@ export {
3952
4230
  Card,
3953
4231
  Checkbox,
3954
4232
  Combobox,
4233
+ CommandPalette,
3955
4234
  DataGrid,
3956
4235
  DatePicker,
3957
4236
  DateRangePicker,
@@ -3981,6 +4260,7 @@ export {
3981
4260
  TreeView,
3982
4261
  defaultThemeClass,
3983
4262
  defaultThemeVars,
4263
+ useCommandPaletteShortcut,
3984
4264
  useToast,
3985
4265
  vars3 as vars
3986
4266
  };