@mlw-packages/react-components 1.10.27 → 1.10.29

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.css CHANGED
@@ -6002,9 +6002,6 @@ body {
6002
6002
  .group:hover .group-hover\:text-destructive {
6003
6003
  color: hsl(var(--destructive));
6004
6004
  }
6005
- .group:hover .group-hover\:text-foreground {
6006
- color: hsl(var(--foreground));
6007
- }
6008
6005
  .group:hover .group-hover\:text-primary {
6009
6006
  color: hsl(var(--primary));
6010
6007
  }
package/dist/index.d.mts CHANGED
@@ -28,7 +28,6 @@ import * as PopoverPrimitive from '@radix-ui/react-popover';
28
28
  import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
29
29
  import { UniqueIdentifier, DraggableAttributes } from '@dnd-kit/core';
30
30
  import { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';
31
- import * as _phosphor_icons_react from '@phosphor-icons/react';
32
31
  import { IconProps as IconProps$1 } from '@phosphor-icons/react';
33
32
 
34
33
  type ErrorMessageProps = {
@@ -2649,6 +2648,7 @@ interface CommandPaletteProps {
2649
2648
  multiSelect?: boolean;
2650
2649
  onSelectMultiple?: (items: CommandItem[]) => void;
2651
2650
  multiSearch?: boolean;
2651
+ onMultiSearchSubmit?: (terms: string[]) => void;
2652
2652
  debounceDelay?: number;
2653
2653
  footer?: React$1.ReactNode;
2654
2654
  onSelect?: (item: CommandItem) => void;
@@ -2692,7 +2692,7 @@ declare function CommandItemRow({ item, isActive, isSelected, multiSelect, onSel
2692
2692
 
2693
2693
  declare function CommandPalette(props: CommandPaletteProps): react_jsx_runtime.JSX.Element;
2694
2694
 
2695
- declare function useCommandPalette({ items, groups, open, onOpenChange, recentItems, onRecentItemsChange, maxRecentItems, multiSearch, multiSelect, onSelectMultiple, }: Partial<CommandPaletteProps>): {
2695
+ declare function useCommandPalette({ items, groups, open, onOpenChange, recentItems, onRecentItemsChange, maxRecentItems, multiSearch, multiSelect, onSelectMultiple, onMultiSearchSubmit, }: Partial<CommandPaletteProps>): {
2696
2696
  query: string;
2697
2697
  setQuery: React$1.Dispatch<React$1.SetStateAction<string>>;
2698
2698
  activeIndex: number;
@@ -2700,13 +2700,7 @@ declare function useCommandPalette({ items, groups, open, onOpenChange, recentIt
2700
2700
  page: number;
2701
2701
  setPage: React$1.Dispatch<React$1.SetStateAction<number>>;
2702
2702
  searchTerms: string[];
2703
- allMatchedGroups: (CommandGroup | {
2704
- id: string;
2705
- label: string;
2706
- icon: React$1.FunctionComponentElement<_phosphor_icons_react.IconProps>;
2707
- items: CommandItem[];
2708
- priority: number;
2709
- })[];
2703
+ allMatchedGroups: CommandGroup[];
2710
2704
  allFlatItems: CommandItem[];
2711
2705
  displayedGroups: CommandGroup[];
2712
2706
  flatItems: CommandItem[];
package/dist/index.d.ts CHANGED
@@ -28,7 +28,6 @@ import * as PopoverPrimitive from '@radix-ui/react-popover';
28
28
  import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
29
29
  import { UniqueIdentifier, DraggableAttributes } from '@dnd-kit/core';
30
30
  import { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';
31
- import * as _phosphor_icons_react from '@phosphor-icons/react';
32
31
  import { IconProps as IconProps$1 } from '@phosphor-icons/react';
33
32
 
34
33
  type ErrorMessageProps = {
@@ -2649,6 +2648,7 @@ interface CommandPaletteProps {
2649
2648
  multiSelect?: boolean;
2650
2649
  onSelectMultiple?: (items: CommandItem[]) => void;
2651
2650
  multiSearch?: boolean;
2651
+ onMultiSearchSubmit?: (terms: string[]) => void;
2652
2652
  debounceDelay?: number;
2653
2653
  footer?: React$1.ReactNode;
2654
2654
  onSelect?: (item: CommandItem) => void;
@@ -2692,7 +2692,7 @@ declare function CommandItemRow({ item, isActive, isSelected, multiSelect, onSel
2692
2692
 
2693
2693
  declare function CommandPalette(props: CommandPaletteProps): react_jsx_runtime.JSX.Element;
2694
2694
 
2695
- declare function useCommandPalette({ items, groups, open, onOpenChange, recentItems, onRecentItemsChange, maxRecentItems, multiSearch, multiSelect, onSelectMultiple, }: Partial<CommandPaletteProps>): {
2695
+ declare function useCommandPalette({ items, groups, open, onOpenChange, recentItems, onRecentItemsChange, maxRecentItems, multiSearch, multiSelect, onSelectMultiple, onMultiSearchSubmit, }: Partial<CommandPaletteProps>): {
2696
2696
  query: string;
2697
2697
  setQuery: React$1.Dispatch<React$1.SetStateAction<string>>;
2698
2698
  activeIndex: number;
@@ -2700,13 +2700,7 @@ declare function useCommandPalette({ items, groups, open, onOpenChange, recentIt
2700
2700
  page: number;
2701
2701
  setPage: React$1.Dispatch<React$1.SetStateAction<number>>;
2702
2702
  searchTerms: string[];
2703
- allMatchedGroups: (CommandGroup | {
2704
- id: string;
2705
- label: string;
2706
- icon: React$1.FunctionComponentElement<_phosphor_icons_react.IconProps>;
2707
- items: CommandItem[];
2708
- priority: number;
2709
- })[];
2703
+ allMatchedGroups: CommandGroup[];
2710
2704
  allFlatItems: CommandItem[];
2711
2705
  displayedGroups: CommandGroup[];
2712
2706
  flatItems: CommandItem[];
package/dist/index.js CHANGED
@@ -22284,15 +22284,24 @@ function normaliseGroups(items = [], groups = []) {
22284
22284
  }
22285
22285
  function unionGroups(base, terms) {
22286
22286
  if (terms.length === 0) return base;
22287
- const allMatchedIds = /* @__PURE__ */ new Set();
22287
+ const bestScore = /* @__PURE__ */ new Map();
22288
22288
  terms.forEach((term) => {
22289
- const filtered = filterAndScore(base, term);
22290
- filtered.forEach((g) => g.items.forEach((i) => allMatchedIds.add(i.id)));
22289
+ base.forEach((group) => {
22290
+ group.items.forEach((item) => {
22291
+ const s = scoreMatch(item, term);
22292
+ if (s >= 0) {
22293
+ const prev = bestScore.get(item.id) ?? -1;
22294
+ if (s > prev) bestScore.set(item.id, s);
22295
+ }
22296
+ });
22297
+ });
22291
22298
  });
22292
22299
  return base.map((group) => ({
22293
22300
  ...group,
22294
- items: group.items.filter((item) => allMatchedIds.has(item.id))
22295
- })).filter((group) => group.items.length > 0);
22301
+ items: group.items.filter((item) => bestScore.has(item.id)).sort(
22302
+ (a, b) => (bestScore.get(b.id) ?? 0) - (bestScore.get(a.id) ?? 0)
22303
+ )
22304
+ })).filter((group) => group.items.length > 0).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
22296
22305
  }
22297
22306
  function createGroup(id, label, items, opts) {
22298
22307
  return { id, label, items, ...opts };
@@ -22413,33 +22422,31 @@ function CommandItemRow({
22413
22422
  onHover,
22414
22423
  searchQuery
22415
22424
  }) {
22425
+ const handleCheckboxChange = (checked) => {
22426
+ if (checked !== "indeterminate") {
22427
+ onToggleSelection?.({});
22428
+ }
22429
+ };
22430
+ const handleItemClick = (e) => {
22431
+ if (multiSelect) {
22432
+ onToggleSelection?.(e);
22433
+ return;
22434
+ }
22435
+ onSelect(e);
22436
+ };
22416
22437
  return /* @__PURE__ */ jsxRuntime.jsxs(
22417
22438
  framerMotion.motion.button,
22418
22439
  {
22419
22440
  layout: true,
22420
- onClick: (e) => {
22421
- if (multiSelect && onToggleSelection && (e.ctrlKey || e.metaKey || e.shiftKey)) {
22422
- onToggleSelection(e);
22423
- } else {
22424
- onSelect(e);
22425
- }
22426
- },
22441
+ onClick: handleItemClick,
22427
22442
  onMouseEnter: onHover,
22428
22443
  className: `
22429
- w-full flex items-center gap-1 px-2 py-1 rounded-md text-left cursor-pointer
22430
- transition-colors duration-75 group relative
22444
+ w-full flex items-center gap-2 px-2 py-1 rounded-md text-left cursor-pointer
22445
+ transition-colors duration-75 group relative justify-between
22431
22446
  ${isActive ? "text-accent-foreground bg-accent" : "hover:bg-accent hover:text-accent-foreground"}
22432
22447
  `,
22433
22448
  children: [
22434
- item.icon && /* @__PURE__ */ jsxRuntime.jsx(
22435
- "span",
22436
- {
22437
- className: `relative flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-md text-base
22438
- ${isSelected ? "bg-primary text-primary-foreground" : isActive ? "bg-primary/20 text-primary" : "bg-muted text-muted-foreground group-hover:text-foreground"}`,
22439
- children: item.icon
22440
- }
22441
- ),
22442
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0 px-1", children: [
22449
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0", children: [
22443
22450
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
22444
22451
  /* @__PURE__ */ jsxRuntime.jsx(
22445
22452
  "span",
@@ -22453,6 +22460,14 @@ function CommandItemRow({
22453
22460
  item.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground truncate", children: /* @__PURE__ */ jsxRuntime.jsx(HighlightText, { text: item.description, query: searchQuery }) })
22454
22461
  ] }),
22455
22462
  item.shortcut && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative hidden sm:flex items-center gap-1 flex-shrink-0", children: item.shortcut.map((k, i) => /* @__PURE__ */ jsxRuntime.jsx(Kbd, { children: k }, i)) }),
22463
+ multiSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
22464
+ CheckboxBase,
22465
+ {
22466
+ checked: isSelected,
22467
+ onCheckedChange: handleCheckboxChange,
22468
+ className: "h-4 w-4 pointer-events-none"
22469
+ }
22470
+ ) }),
22456
22471
  isSelected && /* @__PURE__ */ jsxRuntime.jsx(
22457
22472
  framerMotion.motion.div,
22458
22473
  {
@@ -22460,7 +22475,8 @@ function CommandItemRow({
22460
22475
  className: "absolute left-0 top-1/2 -translate-y-1/2 w-1 h-2/3 bg-primary rounded-r-md"
22461
22476
  }
22462
22477
  ),
22463
- isActive && !isSelected && /* @__PURE__ */ jsxRuntime.jsx(
22478
+ !isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-1/2 -translate-y-1/2 w-1 h-2/3 bg-transparent rounded-r-md" }),
22479
+ isActive && !isSelected && !multiSelect && /* @__PURE__ */ jsxRuntime.jsx(
22464
22480
  react.CaretRightIcon,
22465
22481
  {
22466
22482
  className: "relative w-4 h-4 text-primary flex-shrink-0",
@@ -22482,7 +22498,8 @@ function useCommandPalette({
22482
22498
  maxRecentItems = 5,
22483
22499
  multiSearch = false,
22484
22500
  multiSelect = false,
22485
- onSelectMultiple
22501
+ onSelectMultiple,
22502
+ onMultiSearchSubmit
22486
22503
  }) {
22487
22504
  const [query, setQuery] = React32__namespace.useState("");
22488
22505
  const [activeIndex, setActiveIndex] = React32__namespace.useState(0);
@@ -22490,6 +22507,13 @@ function useCommandPalette({
22490
22507
  const [selectedItemIds, setSelectedItemIds] = React32__namespace.useState(
22491
22508
  /* @__PURE__ */ new Set()
22492
22509
  );
22510
+ const stateRef = React32.useRef({
22511
+ activeIndex,
22512
+ page,
22513
+ flatItems: [],
22514
+ query,
22515
+ selectedItems: []
22516
+ });
22493
22517
  const toggleSelection = React32.useCallback((id) => {
22494
22518
  setSelectedItemIds((prev) => {
22495
22519
  const next = new Set(prev);
@@ -22505,38 +22529,34 @@ function useCommandPalette({
22505
22529
  );
22506
22530
  React32.useEffect(() => {
22507
22531
  if (open) {
22508
- setQuery("");
22532
+ const savedQuery = localStorage.getItem("commandPaletteQuery") || "";
22533
+ setQuery(savedQuery);
22509
22534
  setActiveIndex(0);
22510
22535
  setPage(0);
22511
- clearSelection();
22536
+ if (!multiSelect) {
22537
+ clearSelection();
22538
+ }
22539
+ }
22540
+ }, [open, clearSelection, multiSelect]);
22541
+ React32.useEffect(() => {
22542
+ if (!open) {
22543
+ localStorage.setItem("commandPaletteQuery", query);
22512
22544
  }
22513
- }, [open, clearSelection]);
22545
+ }, [open, query]);
22514
22546
  const searchTerms = React32.useMemo(() => {
22515
22547
  const parts = query.split(",");
22516
- if (parts.length <= 1 && !multiSearch) return [];
22517
- return parts.map((t) => t.trim().toLowerCase()).filter(Boolean);
22548
+ const terms = parts.map((t) => t.trim().toLowerCase()).filter(Boolean);
22549
+ if (terms.length > 1) return terms;
22550
+ if (multiSearch && terms.length > 0) return terms;
22551
+ return [];
22518
22552
  }, [query, multiSearch]);
22519
22553
  const allMatchedGroups = React32.useMemo(() => {
22520
- if (!query.trim()) {
22521
- if (recentItems.length > 0) {
22522
- return [
22523
- {
22524
- id: "__recent__",
22525
- label: "Recent",
22526
- icon: React32__namespace.createElement(react.ClockCounterClockwiseIcon),
22527
- items: recentItems,
22528
- priority: 999
22529
- },
22530
- ...baseGroups
22531
- ];
22532
- }
22533
- return baseGroups;
22534
- }
22535
- if (searchTerms.length > 1 || multiSearch && searchTerms.length > 0) {
22554
+ if (!query.trim()) return baseGroups;
22555
+ if (searchTerms.length > 0) {
22536
22556
  return unionGroups(baseGroups, searchTerms);
22537
22557
  }
22538
22558
  return filterAndScore(baseGroups, query);
22539
- }, [query, baseGroups, recentItems, multiSearch, searchTerms]);
22559
+ }, [query, baseGroups, searchTerms]);
22540
22560
  const allFlatItems = React32.useMemo(
22541
22561
  () => allMatchedGroups.flatMap((g) => g.items),
22542
22562
  [allMatchedGroups]
@@ -22562,9 +22582,7 @@ function useCommandPalette({
22562
22582
  count++;
22563
22583
  if (count >= end) break;
22564
22584
  }
22565
- if (slicedItems.length > 0) {
22566
- result.push({ ...group, items: slicedItems });
22567
- }
22585
+ if (slicedItems.length > 0) result.push({ ...group, items: slicedItems });
22568
22586
  if (count >= end) break;
22569
22587
  }
22570
22588
  return result;
@@ -22577,10 +22595,9 @@ function useCommandPalette({
22577
22595
  () => allFlatItems.filter((i) => selectedItemIds.has(i.id)),
22578
22596
  [allFlatItems, selectedItemIds]
22579
22597
  );
22580
- const pageItemCount = flatItems.length;
22581
22598
  React32.useEffect(() => {
22582
- setActiveIndex((i) => Math.min(i, Math.max(pageItemCount - 1, 0)));
22583
- }, [pageItemCount]);
22599
+ stateRef.current = { activeIndex, page, flatItems, query, selectedItems };
22600
+ }, [activeIndex, page, flatItems, query, selectedItems]);
22584
22601
  const executeBulkAction = React32.useCallback(() => {
22585
22602
  if (!onSelectMultiple || selectedItems.length === 0) return;
22586
22603
  onSelectMultiple(selectedItems);
@@ -22627,49 +22644,73 @@ function useCommandPalette({
22627
22644
  React32.useEffect(() => {
22628
22645
  if (!open) return;
22629
22646
  const handler = (e) => {
22647
+ const { activeIndex: curIdx, flatItems: curItems } = stateRef.current;
22630
22648
  if (e.key === "ArrowDown") {
22631
22649
  e.preventDefault();
22632
- if (activeIndex === pageItemCount - 1 && page < totalPages - 1) {
22650
+ if (curIdx === curItems.length - 1 && page < totalPages - 1)
22633
22651
  setPage((p) => p + 1);
22634
- } else {
22635
- setActiveIndex((i) => (i + 1) % Math.max(pageItemCount, 1));
22636
- }
22652
+ else setActiveIndex((i) => (i + 1) % Math.max(curItems.length, 1));
22637
22653
  } else if (e.key === "ArrowUp") {
22638
22654
  e.preventDefault();
22639
- if (activeIndex === 0 && page > 0) {
22655
+ if (curIdx === 0 && page > 0) {
22640
22656
  setPage((p) => p - 1);
22641
22657
  setActiveIndex(PAGE_SIZE - 1);
22642
- } else {
22658
+ } else
22643
22659
  setActiveIndex(
22644
- (i) => (i - 1 + Math.max(pageItemCount, 1)) % Math.max(pageItemCount, 1)
22660
+ (i) => (i - 1 + Math.max(curItems.length, 1)) % Math.max(curItems.length, 1)
22645
22661
  );
22646
- }
22647
22662
  } else if (e.key === "Enter") {
22648
22663
  e.preventDefault();
22649
- if (multiSearch && query.includes(",")) {
22664
+ const currentSelectedItems = allFlatItems.filter(
22665
+ (i) => selectedItemIds.has(i.id)
22666
+ );
22667
+ if (multiSelect && currentSelectedItems.length > 0) {
22668
+ const selectedLabels = currentSelectedItems.map((item) => item.label);
22669
+ onMultiSearchSubmit?.(selectedLabels);
22650
22670
  return;
22651
22671
  }
22672
+ if (multiSearch && query.trim().length > 0) {
22673
+ const terms = query.split(",").map((t) => t.trim()).filter(Boolean);
22674
+ if (terms.length > 0) {
22675
+ if (multiSelect) {
22676
+ const matchedItems = allFlatItems.filter(
22677
+ (item) => terms.some(
22678
+ (term) => item.label.toLowerCase().includes(term.toLowerCase()) || item.id.toLowerCase().includes(term.toLowerCase())
22679
+ )
22680
+ );
22681
+ const newSelectedIds = new Set(selectedItemIds);
22682
+ matchedItems.forEach((item) => newSelectedIds.add(item.id));
22683
+ setSelectedItemIds(newSelectedIds);
22684
+ const selectedLabels = matchedItems.map((item) => item.label);
22685
+ onMultiSearchSubmit?.(selectedLabels);
22686
+ return;
22687
+ }
22688
+ onMultiSearchSubmit?.(terms);
22689
+ return;
22690
+ }
22691
+ }
22652
22692
  if (multiSelect && (e.ctrlKey || e.metaKey)) {
22653
22693
  executeBulkAction();
22654
22694
  return;
22655
22695
  }
22656
- handleSelect(flatItems[activeIndex], e);
22696
+ handleSelect(curItems[curIdx], e);
22657
22697
  }
22658
22698
  };
22659
- document.addEventListener("keydown", handler);
22660
- return () => document.removeEventListener("keydown", handler);
22699
+ window.addEventListener("keydown", handler);
22700
+ return () => window.removeEventListener("keydown", handler);
22661
22701
  }, [
22662
22702
  open,
22663
- flatItems,
22664
- activeIndex,
22665
- pageItemCount,
22666
22703
  page,
22667
22704
  totalPages,
22668
- query,
22669
22705
  multiSearch,
22670
22706
  multiSelect,
22707
+ handleSelect,
22671
22708
  executeBulkAction,
22672
- handleSelect
22709
+ onMultiSearchSubmit,
22710
+ selectedItems,
22711
+ allFlatItems,
22712
+ selectedItemIds,
22713
+ query
22673
22714
  ]);
22674
22715
  return {
22675
22716
  query,
@@ -22691,7 +22732,7 @@ function useCommandPalette({
22691
22732
  selectedItems,
22692
22733
  executeBulkAction,
22693
22734
  isEmpty: totalItems === 0 && query.trim().length > 0,
22694
- showList: query.trim() !== "" || recentItems.length > 0
22735
+ showList: true
22695
22736
  };
22696
22737
  }
22697
22738
  function useKeyboardShortcut(key, callback, options = {}) {
@@ -22991,7 +23032,16 @@ function CommandPalette(props) {
22991
23032
  className: "flex-1 bg-transparent border-none shadow-none focus-visible:ring-0 p-0 text-base"
22992
23033
  }
22993
23034
  ),
22994
- query && /* @__PURE__ */ jsxRuntime.jsx(ButtonBase, { variant: "ghost", size: "icon", onClick: handleClearQuery, className: "h-8 w-8", children: /* @__PURE__ */ jsxRuntime.jsx(react.XIcon, { className: "w-4 h-4" }) })
23035
+ query && /* @__PURE__ */ jsxRuntime.jsx(
23036
+ ButtonBase,
23037
+ {
23038
+ variant: "ghost",
23039
+ size: "icon",
23040
+ onClick: handleClearQuery,
23041
+ className: "h-8 w-8",
23042
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.XIcon, { className: "w-4 h-4" })
23043
+ }
23044
+ )
22995
23045
  ]
22996
23046
  }
22997
23047
  ),
@@ -23044,7 +23094,13 @@ function CommandPalette(props) {
23044
23094
  style: { maxHeight: "min(600px, 80vh)" },
23045
23095
  children: [
23046
23096
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-4 py-2 border-b border-border", children: [
23047
- /* @__PURE__ */ jsxRuntime.jsx(react.MagnifyingGlassIcon, { className: "w-4 h-4 text-muted-foreground flex-shrink-0", weight: "bold" }),
23097
+ /* @__PURE__ */ jsxRuntime.jsx(
23098
+ react.MagnifyingGlassIcon,
23099
+ {
23100
+ className: "w-4 h-4 text-muted-foreground flex-shrink-0",
23101
+ weight: "bold"
23102
+ }
23103
+ ),
23048
23104
  /* @__PURE__ */ jsxRuntime.jsx(
23049
23105
  DebouncedInput,
23050
23106
  {
@@ -23075,7 +23131,8 @@ function CommandPalette(props) {
23075
23131
  footer,
23076
23132
  totalItems,
23077
23133
  selectedCount: selectedItemIds.size,
23078
- executeBulkAction
23134
+ executeBulkAction,
23135
+ multiSelect
23079
23136
  }
23080
23137
  )
23081
23138
  ]
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { Slot } from '@radix-ui/react-slot';
5
5
  import { cva } from 'class-variance-authority';
6
6
  import { clsx } from 'clsx';
7
7
  import { twMerge } from 'tailwind-merge';
8
- import { XIcon, CircleNotchIcon, MagnifyingGlassIcon, CaretUpIcon, CaretDownIcon, CheckIcon, CaretRightIcon, CircleIcon, CloudArrowUpIcon, MinusIcon, CaretUpDownIcon, PencilSimpleIcon, ArrowsLeftRightIcon, FloppyDiskIcon, PlusIcon, TrashIcon, SidebarSimpleIcon, CommandIcon, ArrowElbowDownRightIcon, ArrowBendUpLeftIcon, FilePdfIcon, FileDocIcon, FileXlsIcon, FilePptIcon, FileCsvIcon, FileTextIcon, FileImageIcon, FileVideoIcon, FileAudioIcon, FileZipIcon, FileIcon, DotsSixVerticalIcon, CopyIcon, InfoIcon, WarningIcon, XCircleIcon, CheckCircleIcon, CaretLeftIcon, DownloadSimpleIcon, UploadSimpleIcon, ArrowClockwiseIcon, ArrowLeftIcon, GearIcon, BellIcon, DotsThreeIcon, FunnelIcon, HeartIcon, StarIcon, EyeIcon, EyeSlashIcon, LockIcon, LockOpenIcon, FolderIcon, ArrowRightIcon as ArrowRightIcon$1, ArrowsOutIcon, DownloadIcon, CalendarBlankIcon, CalendarIcon, MapPinIcon, CalendarDotsIcon, SunIcon, ClockIcon, AlignLeftIcon, CaretLeft, CaretRight, ArrowDownIcon, ClockUserIcon, EyeSlash, Eye, ArrowUpRightIcon, ArrowDownRightIcon, FunnelSimpleIcon, PencilIcon, ClockCounterClockwiseIcon, FileArchiveIcon, TerminalIcon, CodeIcon, CalendarDotIcon as CalendarDotIcon$1, MoonIcon, DesktopIcon } from '@phosphor-icons/react';
8
+ import { XIcon, CircleNotchIcon, MagnifyingGlassIcon, CaretUpIcon, CaretDownIcon, CheckIcon, CaretRightIcon, CircleIcon, CloudArrowUpIcon, MinusIcon, CaretUpDownIcon, PencilSimpleIcon, ArrowsLeftRightIcon, FloppyDiskIcon, PlusIcon, TrashIcon, SidebarSimpleIcon, CommandIcon, ArrowElbowDownRightIcon, ArrowBendUpLeftIcon, FilePdfIcon, FileDocIcon, FileXlsIcon, FilePptIcon, FileCsvIcon, FileTextIcon, FileImageIcon, FileVideoIcon, FileAudioIcon, FileZipIcon, FileIcon, DotsSixVerticalIcon, CopyIcon, InfoIcon, WarningIcon, XCircleIcon, CheckCircleIcon, CaretLeftIcon, DownloadSimpleIcon, UploadSimpleIcon, ArrowClockwiseIcon, ArrowLeftIcon, GearIcon, BellIcon, DotsThreeIcon, FunnelIcon, HeartIcon, StarIcon, EyeIcon, EyeSlashIcon, LockIcon, LockOpenIcon, FolderIcon, ArrowRightIcon as ArrowRightIcon$1, ArrowsOutIcon, DownloadIcon, CalendarBlankIcon, CalendarIcon, MapPinIcon, CalendarDotsIcon, SunIcon, ClockIcon, AlignLeftIcon, CaretLeft, CaretRight, ArrowDownIcon, ClockUserIcon, EyeSlash, Eye, ArrowUpRightIcon, ArrowDownRightIcon, FunnelSimpleIcon, PencilIcon, FileArchiveIcon, TerminalIcon, CodeIcon, CalendarDotIcon as CalendarDotIcon$1, MoonIcon, DesktopIcon } from '@phosphor-icons/react';
9
9
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
10
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
11
  import * as DialogPrimitive from '@radix-ui/react-dialog';
@@ -22242,15 +22242,24 @@ function normaliseGroups(items = [], groups = []) {
22242
22242
  }
22243
22243
  function unionGroups(base, terms) {
22244
22244
  if (terms.length === 0) return base;
22245
- const allMatchedIds = /* @__PURE__ */ new Set();
22245
+ const bestScore = /* @__PURE__ */ new Map();
22246
22246
  terms.forEach((term) => {
22247
- const filtered = filterAndScore(base, term);
22248
- filtered.forEach((g) => g.items.forEach((i) => allMatchedIds.add(i.id)));
22247
+ base.forEach((group) => {
22248
+ group.items.forEach((item) => {
22249
+ const s = scoreMatch(item, term);
22250
+ if (s >= 0) {
22251
+ const prev = bestScore.get(item.id) ?? -1;
22252
+ if (s > prev) bestScore.set(item.id, s);
22253
+ }
22254
+ });
22255
+ });
22249
22256
  });
22250
22257
  return base.map((group) => ({
22251
22258
  ...group,
22252
- items: group.items.filter((item) => allMatchedIds.has(item.id))
22253
- })).filter((group) => group.items.length > 0);
22259
+ items: group.items.filter((item) => bestScore.has(item.id)).sort(
22260
+ (a, b) => (bestScore.get(b.id) ?? 0) - (bestScore.get(a.id) ?? 0)
22261
+ )
22262
+ })).filter((group) => group.items.length > 0).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
22254
22263
  }
22255
22264
  function createGroup(id, label, items, opts) {
22256
22265
  return { id, label, items, ...opts };
@@ -22371,33 +22380,31 @@ function CommandItemRow({
22371
22380
  onHover,
22372
22381
  searchQuery
22373
22382
  }) {
22383
+ const handleCheckboxChange = (checked) => {
22384
+ if (checked !== "indeterminate") {
22385
+ onToggleSelection?.({});
22386
+ }
22387
+ };
22388
+ const handleItemClick = (e) => {
22389
+ if (multiSelect) {
22390
+ onToggleSelection?.(e);
22391
+ return;
22392
+ }
22393
+ onSelect(e);
22394
+ };
22374
22395
  return /* @__PURE__ */ jsxs(
22375
22396
  motion.button,
22376
22397
  {
22377
22398
  layout: true,
22378
- onClick: (e) => {
22379
- if (multiSelect && onToggleSelection && (e.ctrlKey || e.metaKey || e.shiftKey)) {
22380
- onToggleSelection(e);
22381
- } else {
22382
- onSelect(e);
22383
- }
22384
- },
22399
+ onClick: handleItemClick,
22385
22400
  onMouseEnter: onHover,
22386
22401
  className: `
22387
- w-full flex items-center gap-1 px-2 py-1 rounded-md text-left cursor-pointer
22388
- transition-colors duration-75 group relative
22402
+ w-full flex items-center gap-2 px-2 py-1 rounded-md text-left cursor-pointer
22403
+ transition-colors duration-75 group relative justify-between
22389
22404
  ${isActive ? "text-accent-foreground bg-accent" : "hover:bg-accent hover:text-accent-foreground"}
22390
22405
  `,
22391
22406
  children: [
22392
- item.icon && /* @__PURE__ */ jsx(
22393
- "span",
22394
- {
22395
- className: `relative flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-md text-base
22396
- ${isSelected ? "bg-primary text-primary-foreground" : isActive ? "bg-primary/20 text-primary" : "bg-muted text-muted-foreground group-hover:text-foreground"}`,
22397
- children: item.icon
22398
- }
22399
- ),
22400
- /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-w-0 px-1", children: [
22407
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-w-0", children: [
22401
22408
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
22402
22409
  /* @__PURE__ */ jsx(
22403
22410
  "span",
@@ -22411,6 +22418,14 @@ function CommandItemRow({
22411
22418
  item.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: /* @__PURE__ */ jsx(HighlightText, { text: item.description, query: searchQuery }) })
22412
22419
  ] }),
22413
22420
  item.shortcut && /* @__PURE__ */ jsx("div", { className: "relative hidden sm:flex items-center gap-1 flex-shrink-0", children: item.shortcut.map((k, i) => /* @__PURE__ */ jsx(Kbd, { children: k }, i)) }),
22421
+ multiSelect && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(
22422
+ CheckboxBase,
22423
+ {
22424
+ checked: isSelected,
22425
+ onCheckedChange: handleCheckboxChange,
22426
+ className: "h-4 w-4 pointer-events-none"
22427
+ }
22428
+ ) }),
22414
22429
  isSelected && /* @__PURE__ */ jsx(
22415
22430
  motion.div,
22416
22431
  {
@@ -22418,7 +22433,8 @@ function CommandItemRow({
22418
22433
  className: "absolute left-0 top-1/2 -translate-y-1/2 w-1 h-2/3 bg-primary rounded-r-md"
22419
22434
  }
22420
22435
  ),
22421
- isActive && !isSelected && /* @__PURE__ */ jsx(
22436
+ !isSelected && /* @__PURE__ */ jsx("div", { className: "absolute left-0 top-1/2 -translate-y-1/2 w-1 h-2/3 bg-transparent rounded-r-md" }),
22437
+ isActive && !isSelected && !multiSelect && /* @__PURE__ */ jsx(
22422
22438
  CaretRightIcon,
22423
22439
  {
22424
22440
  className: "relative w-4 h-4 text-primary flex-shrink-0",
@@ -22440,7 +22456,8 @@ function useCommandPalette({
22440
22456
  maxRecentItems = 5,
22441
22457
  multiSearch = false,
22442
22458
  multiSelect = false,
22443
- onSelectMultiple
22459
+ onSelectMultiple,
22460
+ onMultiSearchSubmit
22444
22461
  }) {
22445
22462
  const [query, setQuery] = React32.useState("");
22446
22463
  const [activeIndex, setActiveIndex] = React32.useState(0);
@@ -22448,6 +22465,13 @@ function useCommandPalette({
22448
22465
  const [selectedItemIds, setSelectedItemIds] = React32.useState(
22449
22466
  /* @__PURE__ */ new Set()
22450
22467
  );
22468
+ const stateRef = useRef({
22469
+ activeIndex,
22470
+ page,
22471
+ flatItems: [],
22472
+ query,
22473
+ selectedItems: []
22474
+ });
22451
22475
  const toggleSelection = useCallback((id) => {
22452
22476
  setSelectedItemIds((prev) => {
22453
22477
  const next = new Set(prev);
@@ -22463,38 +22487,34 @@ function useCommandPalette({
22463
22487
  );
22464
22488
  useEffect(() => {
22465
22489
  if (open) {
22466
- setQuery("");
22490
+ const savedQuery = localStorage.getItem("commandPaletteQuery") || "";
22491
+ setQuery(savedQuery);
22467
22492
  setActiveIndex(0);
22468
22493
  setPage(0);
22469
- clearSelection();
22494
+ if (!multiSelect) {
22495
+ clearSelection();
22496
+ }
22497
+ }
22498
+ }, [open, clearSelection, multiSelect]);
22499
+ useEffect(() => {
22500
+ if (!open) {
22501
+ localStorage.setItem("commandPaletteQuery", query);
22470
22502
  }
22471
- }, [open, clearSelection]);
22503
+ }, [open, query]);
22472
22504
  const searchTerms = useMemo(() => {
22473
22505
  const parts = query.split(",");
22474
- if (parts.length <= 1 && !multiSearch) return [];
22475
- return parts.map((t) => t.trim().toLowerCase()).filter(Boolean);
22506
+ const terms = parts.map((t) => t.trim().toLowerCase()).filter(Boolean);
22507
+ if (terms.length > 1) return terms;
22508
+ if (multiSearch && terms.length > 0) return terms;
22509
+ return [];
22476
22510
  }, [query, multiSearch]);
22477
22511
  const allMatchedGroups = useMemo(() => {
22478
- if (!query.trim()) {
22479
- if (recentItems.length > 0) {
22480
- return [
22481
- {
22482
- id: "__recent__",
22483
- label: "Recent",
22484
- icon: React32.createElement(ClockCounterClockwiseIcon),
22485
- items: recentItems,
22486
- priority: 999
22487
- },
22488
- ...baseGroups
22489
- ];
22490
- }
22491
- return baseGroups;
22492
- }
22493
- if (searchTerms.length > 1 || multiSearch && searchTerms.length > 0) {
22512
+ if (!query.trim()) return baseGroups;
22513
+ if (searchTerms.length > 0) {
22494
22514
  return unionGroups(baseGroups, searchTerms);
22495
22515
  }
22496
22516
  return filterAndScore(baseGroups, query);
22497
- }, [query, baseGroups, recentItems, multiSearch, searchTerms]);
22517
+ }, [query, baseGroups, searchTerms]);
22498
22518
  const allFlatItems = useMemo(
22499
22519
  () => allMatchedGroups.flatMap((g) => g.items),
22500
22520
  [allMatchedGroups]
@@ -22520,9 +22540,7 @@ function useCommandPalette({
22520
22540
  count++;
22521
22541
  if (count >= end) break;
22522
22542
  }
22523
- if (slicedItems.length > 0) {
22524
- result.push({ ...group, items: slicedItems });
22525
- }
22543
+ if (slicedItems.length > 0) result.push({ ...group, items: slicedItems });
22526
22544
  if (count >= end) break;
22527
22545
  }
22528
22546
  return result;
@@ -22535,10 +22553,9 @@ function useCommandPalette({
22535
22553
  () => allFlatItems.filter((i) => selectedItemIds.has(i.id)),
22536
22554
  [allFlatItems, selectedItemIds]
22537
22555
  );
22538
- const pageItemCount = flatItems.length;
22539
22556
  useEffect(() => {
22540
- setActiveIndex((i) => Math.min(i, Math.max(pageItemCount - 1, 0)));
22541
- }, [pageItemCount]);
22557
+ stateRef.current = { activeIndex, page, flatItems, query, selectedItems };
22558
+ }, [activeIndex, page, flatItems, query, selectedItems]);
22542
22559
  const executeBulkAction = useCallback(() => {
22543
22560
  if (!onSelectMultiple || selectedItems.length === 0) return;
22544
22561
  onSelectMultiple(selectedItems);
@@ -22585,49 +22602,73 @@ function useCommandPalette({
22585
22602
  useEffect(() => {
22586
22603
  if (!open) return;
22587
22604
  const handler = (e) => {
22605
+ const { activeIndex: curIdx, flatItems: curItems } = stateRef.current;
22588
22606
  if (e.key === "ArrowDown") {
22589
22607
  e.preventDefault();
22590
- if (activeIndex === pageItemCount - 1 && page < totalPages - 1) {
22608
+ if (curIdx === curItems.length - 1 && page < totalPages - 1)
22591
22609
  setPage((p) => p + 1);
22592
- } else {
22593
- setActiveIndex((i) => (i + 1) % Math.max(pageItemCount, 1));
22594
- }
22610
+ else setActiveIndex((i) => (i + 1) % Math.max(curItems.length, 1));
22595
22611
  } else if (e.key === "ArrowUp") {
22596
22612
  e.preventDefault();
22597
- if (activeIndex === 0 && page > 0) {
22613
+ if (curIdx === 0 && page > 0) {
22598
22614
  setPage((p) => p - 1);
22599
22615
  setActiveIndex(PAGE_SIZE - 1);
22600
- } else {
22616
+ } else
22601
22617
  setActiveIndex(
22602
- (i) => (i - 1 + Math.max(pageItemCount, 1)) % Math.max(pageItemCount, 1)
22618
+ (i) => (i - 1 + Math.max(curItems.length, 1)) % Math.max(curItems.length, 1)
22603
22619
  );
22604
- }
22605
22620
  } else if (e.key === "Enter") {
22606
22621
  e.preventDefault();
22607
- if (multiSearch && query.includes(",")) {
22622
+ const currentSelectedItems = allFlatItems.filter(
22623
+ (i) => selectedItemIds.has(i.id)
22624
+ );
22625
+ if (multiSelect && currentSelectedItems.length > 0) {
22626
+ const selectedLabels = currentSelectedItems.map((item) => item.label);
22627
+ onMultiSearchSubmit?.(selectedLabels);
22608
22628
  return;
22609
22629
  }
22630
+ if (multiSearch && query.trim().length > 0) {
22631
+ const terms = query.split(",").map((t) => t.trim()).filter(Boolean);
22632
+ if (terms.length > 0) {
22633
+ if (multiSelect) {
22634
+ const matchedItems = allFlatItems.filter(
22635
+ (item) => terms.some(
22636
+ (term) => item.label.toLowerCase().includes(term.toLowerCase()) || item.id.toLowerCase().includes(term.toLowerCase())
22637
+ )
22638
+ );
22639
+ const newSelectedIds = new Set(selectedItemIds);
22640
+ matchedItems.forEach((item) => newSelectedIds.add(item.id));
22641
+ setSelectedItemIds(newSelectedIds);
22642
+ const selectedLabels = matchedItems.map((item) => item.label);
22643
+ onMultiSearchSubmit?.(selectedLabels);
22644
+ return;
22645
+ }
22646
+ onMultiSearchSubmit?.(terms);
22647
+ return;
22648
+ }
22649
+ }
22610
22650
  if (multiSelect && (e.ctrlKey || e.metaKey)) {
22611
22651
  executeBulkAction();
22612
22652
  return;
22613
22653
  }
22614
- handleSelect(flatItems[activeIndex], e);
22654
+ handleSelect(curItems[curIdx], e);
22615
22655
  }
22616
22656
  };
22617
- document.addEventListener("keydown", handler);
22618
- return () => document.removeEventListener("keydown", handler);
22657
+ window.addEventListener("keydown", handler);
22658
+ return () => window.removeEventListener("keydown", handler);
22619
22659
  }, [
22620
22660
  open,
22621
- flatItems,
22622
- activeIndex,
22623
- pageItemCount,
22624
22661
  page,
22625
22662
  totalPages,
22626
- query,
22627
22663
  multiSearch,
22628
22664
  multiSelect,
22665
+ handleSelect,
22629
22666
  executeBulkAction,
22630
- handleSelect
22667
+ onMultiSearchSubmit,
22668
+ selectedItems,
22669
+ allFlatItems,
22670
+ selectedItemIds,
22671
+ query
22631
22672
  ]);
22632
22673
  return {
22633
22674
  query,
@@ -22649,7 +22690,7 @@ function useCommandPalette({
22649
22690
  selectedItems,
22650
22691
  executeBulkAction,
22651
22692
  isEmpty: totalItems === 0 && query.trim().length > 0,
22652
- showList: query.trim() !== "" || recentItems.length > 0
22693
+ showList: true
22653
22694
  };
22654
22695
  }
22655
22696
  function useKeyboardShortcut(key, callback, options = {}) {
@@ -22949,7 +22990,16 @@ function CommandPalette(props) {
22949
22990
  className: "flex-1 bg-transparent border-none shadow-none focus-visible:ring-0 p-0 text-base"
22950
22991
  }
22951
22992
  ),
22952
- query && /* @__PURE__ */ jsx(ButtonBase, { variant: "ghost", size: "icon", onClick: handleClearQuery, className: "h-8 w-8", children: /* @__PURE__ */ jsx(XIcon, { className: "w-4 h-4" }) })
22993
+ query && /* @__PURE__ */ jsx(
22994
+ ButtonBase,
22995
+ {
22996
+ variant: "ghost",
22997
+ size: "icon",
22998
+ onClick: handleClearQuery,
22999
+ className: "h-8 w-8",
23000
+ children: /* @__PURE__ */ jsx(XIcon, { className: "w-4 h-4" })
23001
+ }
23002
+ )
22953
23003
  ]
22954
23004
  }
22955
23005
  ),
@@ -23002,7 +23052,13 @@ function CommandPalette(props) {
23002
23052
  style: { maxHeight: "min(600px, 80vh)" },
23003
23053
  children: [
23004
23054
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-2 border-b border-border", children: [
23005
- /* @__PURE__ */ jsx(MagnifyingGlassIcon, { className: "w-4 h-4 text-muted-foreground flex-shrink-0", weight: "bold" }),
23055
+ /* @__PURE__ */ jsx(
23056
+ MagnifyingGlassIcon,
23057
+ {
23058
+ className: "w-4 h-4 text-muted-foreground flex-shrink-0",
23059
+ weight: "bold"
23060
+ }
23061
+ ),
23006
23062
  /* @__PURE__ */ jsx(
23007
23063
  DebouncedInput,
23008
23064
  {
@@ -23033,7 +23089,8 @@ function CommandPalette(props) {
23033
23089
  footer,
23034
23090
  totalItems,
23035
23091
  selectedCount: selectedItemIds.size,
23036
- executeBulkAction
23092
+ executeBulkAction,
23093
+ multiSelect
23037
23094
  }
23038
23095
  )
23039
23096
  ]
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "access": "public",
6
6
  "type": "module"
7
7
  },
8
- "version": "1.10.27",
8
+ "version": "1.10.29",
9
9
  "homepage": "https://main--68e80310a069c2f10b546ef3.chromatic.com/",
10
10
  "repository": {
11
11
  "type": "git",