@vesture/react 0.2.8 → 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
@@ -501,13 +501,17 @@ interface ComboboxProps {
501
501
  disabled?: boolean;
502
502
  invalid?: boolean;
503
503
  loading?: boolean;
504
+ /** Number of filtered options at/above which the listbox switches to virtualized (windowed) rendering. */
505
+ virtualizationThreshold?: number;
506
+ /** Row height (px) used for virtualized listbox rows; defaults to the `.option` class's rendered height. */
507
+ optionHeight?: number;
504
508
  id?: string;
505
509
  className?: string;
506
510
  "aria-label"?: string;
507
511
  "aria-labelledby"?: string;
508
512
  }
509
513
 
510
- declare function Combobox({ options, multiple, value: controlledValue, defaultValue, onChange, onInputChange, filterOptions, placeholder, noOptionsMessage, disabled, invalid, loading, id, className, ...rest }: ComboboxProps): ReactElement;
514
+ declare function Combobox({ options, multiple, value: controlledValue, defaultValue, onChange, onInputChange, filterOptions, placeholder, noOptionsMessage, disabled, invalid, loading, virtualizationThreshold, optionHeight, id, className, ...rest }: ComboboxProps): ReactElement;
511
515
 
512
516
  interface TreeNode<T = unknown> {
513
517
  id: string;
@@ -535,4 +539,47 @@ interface TreeViewProps<T = unknown> {
535
539
 
536
540
  declare function TreeView<T = unknown>({ nodes, expanded: controlledExpanded, defaultExpanded, onExpandedChange, selected: controlledSelected, defaultSelected, onSelectedChange, selectable, onLoadChildren, height, rowHeight, }: TreeViewProps<T>): ReactElement;
537
541
 
538
- 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
@@ -3246,14 +3246,17 @@ import {
3246
3246
  var chip = "Combobox_chip__26nhic2";
3247
3247
  var chipRemove = "Combobox_chipRemove__26nhic3";
3248
3248
  var chipsWrapper = "Combobox_chipsWrapper__26nhic1";
3249
- var emptyState2 = "Combobox_emptyState__26nhic7";
3249
+ var emptyState2 = "Combobox_emptyState__26nhic8";
3250
3250
  var inputEl = "Combobox_inputEl__26nhic4";
3251
3251
  var listbox = "Combobox_listbox__26nhic5";
3252
3252
  var option = "Combobox_option__26nhic6";
3253
+ var virtualSpacer = "Combobox_virtualSpacer__26nhic7";
3253
3254
  var wrapper9 = "Combobox_wrapper__26nhic0";
3254
3255
 
3255
3256
  // src/components/Combobox/Combobox.tsx
3256
3257
  import { jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
3258
+ var DEFAULT_OPTION_HEIGHT = 37;
3259
+ var OVERSCAN = 5;
3257
3260
  function defaultFilter(options, inputText) {
3258
3261
  const query = inputText.trim().toLowerCase();
3259
3262
  if (!query) return options;
@@ -3276,6 +3279,8 @@ function Combobox({
3276
3279
  disabled = false,
3277
3280
  invalid,
3278
3281
  loading = false,
3282
+ virtualizationThreshold = 50,
3283
+ optionHeight = DEFAULT_OPTION_HEIGHT,
3279
3284
  id,
3280
3285
  className,
3281
3286
  ...rest
@@ -3302,8 +3307,37 @@ function Combobox({
3302
3307
  }
3303
3308
  }, [singleValue, multiple]);
3304
3309
  const inputRef = useRef9(null);
3310
+ const listboxRef = useRef9(null);
3305
3311
  const listboxId = useId2();
3312
+ const [scrollTop, setScrollTop] = useState14(0);
3313
+ const [viewportHeight, setViewportHeight] = useState14(280);
3306
3314
  const filteredOptions = filterOptions(options, multiple ? inputText : open ? inputText : "");
3315
+ const isVirtualized = filteredOptions.length >= virtualizationThreshold;
3316
+ useEffect7(() => {
3317
+ setScrollTop(0);
3318
+ setActiveIndex(null);
3319
+ if (listboxRef.current) listboxRef.current.scrollTop = 0;
3320
+ }, [options]);
3321
+ useEffect7(() => {
3322
+ if (!open) return;
3323
+ const node = listboxRef.current;
3324
+ if (!node) return;
3325
+ if (node.clientHeight > 0) setViewportHeight(node.clientHeight);
3326
+ if (typeof ResizeObserver === "undefined") return;
3327
+ const observer = new ResizeObserver((entries) => {
3328
+ const entry = entries[0];
3329
+ if (entry) setViewportHeight(entry.contentRect.height);
3330
+ });
3331
+ observer.observe(node);
3332
+ return () => observer.disconnect();
3333
+ }, [open]);
3334
+ const totalHeight = filteredOptions.length * optionHeight;
3335
+ const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
3336
+ const effectiveScrollTop = Math.min(scrollTop, maxScrollTop);
3337
+ const visibleCount = Math.ceil(viewportHeight / optionHeight) + OVERSCAN * 2;
3338
+ const startIndex = isVirtualized ? Math.max(0, Math.floor(effectiveScrollTop / optionHeight) - OVERSCAN) : 0;
3339
+ const endIndex = isVirtualized ? Math.min(filteredOptions.length, startIndex + visibleCount) : filteredOptions.length;
3340
+ const visibleOptions = filteredOptions.slice(startIndex, endIndex);
3307
3341
  const { refs, floatingStyles, context } = useFloating7({
3308
3342
  open,
3309
3343
  onOpenChange: setOpen,
@@ -3348,6 +3382,21 @@ function Combobox({
3348
3382
  setOpen(true);
3349
3383
  setActiveIndex(null);
3350
3384
  };
3385
+ const scrollIndexIntoView = (index) => {
3386
+ if (!isVirtualized) return;
3387
+ const rowTop = index * optionHeight;
3388
+ const rowBottom = rowTop + optionHeight;
3389
+ let next = scrollTop;
3390
+ if (rowTop < scrollTop) {
3391
+ next = rowTop;
3392
+ } else if (rowBottom > scrollTop + viewportHeight) {
3393
+ next = rowBottom - viewportHeight;
3394
+ } else {
3395
+ return;
3396
+ }
3397
+ setScrollTop(next);
3398
+ if (listboxRef.current) listboxRef.current.scrollTop = next;
3399
+ };
3351
3400
  const moveActiveIndex = (direction2) => {
3352
3401
  if (filteredOptions.length === 0) return;
3353
3402
  let next = activeIndex ?? (direction2 === 1 ? -1 : filteredOptions.length);
@@ -3355,6 +3404,7 @@ function Combobox({
3355
3404
  next = (next + direction2 + filteredOptions.length) % filteredOptions.length;
3356
3405
  if (!filteredOptions[next]?.disabled) {
3357
3406
  setActiveIndex(next);
3407
+ scrollIndexIntoView(next);
3358
3408
  return;
3359
3409
  }
3360
3410
  }
@@ -3448,14 +3498,40 @@ function Combobox({
3448
3498
  open ? /* @__PURE__ */ jsx40(FloatingPortal7, { children: /* @__PURE__ */ jsx40(
3449
3499
  "div",
3450
3500
  {
3451
- ref: refs.setFloating,
3501
+ ref: (node) => {
3502
+ refs.setFloating(node);
3503
+ listboxRef.current = node;
3504
+ },
3452
3505
  id: listboxId,
3453
3506
  role: "listbox",
3454
3507
  "aria-multiselectable": multiple || void 0,
3455
3508
  className: listbox,
3456
3509
  style: floatingStyles,
3510
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
3457
3511
  ...getFloatingProps(),
3458
- children: loading ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: "Loading\u2026" }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: noOptionsMessage }) : filteredOptions.map((opt, index) => {
3512
+ children: loading ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: "Loading\u2026" }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: noOptionsMessage }) : isVirtualized ? /* @__PURE__ */ jsx40("div", { className: virtualSpacer, style: { height: totalHeight }, children: visibleOptions.map((opt, i) => {
3513
+ const index = startIndex + i;
3514
+ const isSelected = multiple ? selectedValues.includes(opt.value) : opt.value === singleValue;
3515
+ return /* @__PURE__ */ jsx40(
3516
+ "div",
3517
+ {
3518
+ id: `${listboxId}-option-${index}`,
3519
+ role: "option",
3520
+ "aria-selected": isSelected,
3521
+ "aria-disabled": opt.disabled || void 0,
3522
+ "data-active": activeIndex === index || void 0,
3523
+ className: option,
3524
+ style: { position: "absolute", top: index * optionHeight, left: 0, right: 0, height: optionHeight },
3525
+ onMouseDown: (event) => {
3526
+ event.preventDefault();
3527
+ selectOption(opt);
3528
+ },
3529
+ onMouseEnter: () => setActiveIndex(index),
3530
+ children: opt.label
3531
+ },
3532
+ opt.value
3533
+ );
3534
+ }) }) : filteredOptions.map((opt, index) => {
3459
3535
  const isSelected = multiple ? selectedValues.includes(opt.value) : opt.value === singleValue;
3460
3536
  return /* @__PURE__ */ jsx40(
3461
3537
  "div",
@@ -3581,8 +3657,8 @@ function TreeView({
3581
3657
  const rowRefs = useRef10(/* @__PURE__ */ new Map());
3582
3658
  const nodeIndex = useMemo4(() => {
3583
3659
  const map = /* @__PURE__ */ new Map();
3584
- function walk(list4, parentId) {
3585
- for (const node of list4) {
3660
+ function walk(list5, parentId) {
3661
+ for (const node of list5) {
3586
3662
  map.set(node.id, { node, parentId });
3587
3663
  const children = getChildren(node, loadedChildren);
3588
3664
  if (children) walk(children, node.id);
@@ -3593,8 +3669,8 @@ function TreeView({
3593
3669
  }, [nodes, loadedChildren]);
3594
3670
  const flatNodes = useMemo4(() => {
3595
3671
  const result = [];
3596
- function walk(list4, depth) {
3597
- for (const node of list4) {
3672
+ function walk(list5, depth) {
3673
+ for (const node of list5) {
3598
3674
  const children = getChildren(node, loadedChildren);
3599
3675
  const nodeHasChildren = children ? children.length > 0 : node.hasChildren === true;
3600
3676
  const isExpanded = expandedSet.has(node.id);
@@ -3859,6 +3935,284 @@ function TreeView({
3859
3935
  );
3860
3936
  }
3861
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
+
3862
4216
  // src/index.ts
3863
4217
  import { vars as vars3, defaultThemeClass, defaultThemeVars } from "@vesture/tokens";
3864
4218
  export {
@@ -3876,6 +4230,7 @@ export {
3876
4230
  Card,
3877
4231
  Checkbox,
3878
4232
  Combobox,
4233
+ CommandPalette,
3879
4234
  DataGrid,
3880
4235
  DatePicker,
3881
4236
  DateRangePicker,
@@ -3905,6 +4260,7 @@ export {
3905
4260
  TreeView,
3906
4261
  defaultThemeClass,
3907
4262
  defaultThemeVars,
4263
+ useCommandPaletteShortcut,
3908
4264
  useToast,
3909
4265
  vars3 as vars
3910
4266
  };