periplo-ui 4.2.1 → 4.3.0

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.
@@ -51,6 +51,8 @@ type ComboboxBaseProps<T> = {
51
51
  container?: HTMLElement;
52
52
  /** Alignment of the dropdown relative to the trigger. Defaults to 'center'. */
53
53
  align?: 'start' | 'center' | 'end';
54
+ /** Auto-focus the search input when the dropdown opens. */
55
+ onOpenAutoFocus?: boolean;
54
56
  };
55
57
  export type ComboboxSingleProps<T> = ComboboxBaseProps<T> & {
56
58
  multiple?: false;
@@ -70,6 +72,8 @@ export type ComboboxSingleProps<T> = ComboboxBaseProps<T> & {
70
72
  hasNextPage: boolean;
71
73
  nextPage: number;
72
74
  }>;
75
+ /** Prefetch the first page on mount (virtualized only), so data is ready before opening. */
76
+ prefetch?: boolean;
73
77
  };
74
78
  export type ComboboxMultipleProps<T> = ComboboxBaseProps<T> & {
75
79
  multiple: true;
@@ -79,6 +83,8 @@ export type ComboboxMultipleProps<T> = ComboboxBaseProps<T> & {
79
83
  renderLabel?: (selectedOptions: Array<T>, onRemove: (value: string) => void) => React.ReactNode;
80
84
  /** fetchPage is not allowed with multiple selection */
81
85
  fetchPage?: never;
86
+ /** prefetch is not allowed with multiple selection */
87
+ prefetch?: never;
82
88
  };
83
89
  export type ComboboxProps<T> = ComboboxSingleProps<T> | ComboboxMultipleProps<T>;
84
90
  /**
@@ -1,6 +1,7 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { CaretDown } from '@phosphor-icons/react/dist/ssr/CaretDown';
3
3
  import { X } from '@phosphor-icons/react/dist/ssr/X';
4
+ import { useRef } from 'react';
4
5
  import { cn } from '../../lib/utils.js';
5
6
  import { Button, buttonVariants } from '../Button/Button.js';
6
7
  import { Command, CommandInput } from '../Command/Command.js';
@@ -33,8 +34,10 @@ const Combobox = (props) => {
33
34
  specialOptions,
34
35
  specialOptionsTitle,
35
36
  container,
36
- align
37
+ align,
38
+ onOpenAutoFocus = false
37
39
  } = props;
40
+ const searchInputRef = useRef(null);
38
41
  const normalizedMaxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
39
42
  const boundedListMaxHeight = `min(${normalizedMaxHeight}, max(120px, calc(var(--radix-popover-content-available-height, 100dvh) - 68px)))`;
40
43
  const {
@@ -137,10 +140,15 @@ const Combobox = (props) => {
137
140
  container,
138
141
  side: "bottom",
139
142
  align,
143
+ onOpenAutoFocus: onOpenAutoFocus ? (event) => {
144
+ event.preventDefault();
145
+ searchInputRef.current?.focus();
146
+ } : void 0,
140
147
  children: /* @__PURE__ */ jsxs(Command, { shouldFilter: false, children: [
141
148
  /* @__PURE__ */ jsx(
142
149
  CommandInput,
143
150
  {
151
+ ref: searchInputRef,
144
152
  placeholder: searchPlaceholder,
145
153
  disabled: loading,
146
154
  value: searchTerm,
@@ -162,7 +170,8 @@ const Combobox = (props) => {
162
170
  maxHeight: boundedListMaxHeight,
163
171
  hasNextPage,
164
172
  loadingMore,
165
- onLoadMore: loadNextPage
173
+ onLoadMore: loadNextPage,
174
+ open
166
175
  }
167
176
  ) : /* @__PURE__ */ jsx(
168
177
  StaticComboboxList,
@@ -1 +1 @@
1
- {"version":3,"file":"Combobox.js","sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import { CaretDown } from '@phosphor-icons/react/dist/ssr/CaretDown'\nimport { X } from '@phosphor-icons/react/dist/ssr/X'\n\nimport { cn } from '../../lib/utils'\nimport { Button, buttonVariants } from '../Button'\nimport { Command, CommandInput } from '../Command'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\n\nimport { StaticComboboxList } from './StaticComboboxList'\nimport { useCombobox } from './useCombobox'\nimport { VirtualizedComboboxList } from './VirtualizedComboboxList'\n\ntype ComboboxBaseProps<T> = {\n /** Unique identifier for the combobox */\n id?: string\n /** Array of options to display in the combobox */\n options: Array<T>\n /** Function to get the unique identifier from an option. */\n getOptionValue: (option: T) => string\n /** Function to get the display text from an option. */\n getOptionLabel: (option: T) => string\n /** Custom render function for options. If not provided, defaults to showing a checkmark and label */\n renderOption?: (option: T, isSelected: boolean) => React.ReactNode\n /** Placeholder text shown when no option is selected */\n placeholder?: string\n /** Placeholder text for the search input field */\n searchPlaceholder?: string\n /** Message shown when no options match the search query */\n emptyMessage?: string\n /** Additional CSS classes to apply to the combobox trigger */\n className?: string\n /** Additional CSS classes to apply to the popover content */\n contentClassName?: string\n /** Whether the combobox is disabled */\n disabled?: boolean\n /** Maximum height of the options list. Can be any valid CSS height value */\n maxHeight?: string | number\n /** Whether to close the dropdown when an option is selected. Defaults to `true` for single select and `false` for multi-select. */\n closeOnSelect?: boolean\n /** Whether the combobox is in a loading state */\n loading?: boolean\n /** Message to show when in loading state */\n loadingPlaceholder?: string\n /** Whether the combobox has an error */\n error?: boolean | string\n /** Custom function to filter options based on search term */\n filterOptions?: (options: Array<T>, searchTerm: string) => Array<T>\n /** Callback function executed when the clear button is clicked. When provided, an X button will appear on hover to clear the selection. */\n onClear?: boolean | (() => void)\n /** Whether the selection can be cleared */\n clearable?: boolean\n /** Whether the combobox is inside a modal */\n modal?: boolean\n /** Placeholder text for the selected multiple options */\n selectedMultiplePlaceholder?: string\n /** Placeholder text for the multiple options */\n multipleOptionsPlaceholder?: string\n /** Special options that appear at the top with their own title. When selected, the combobox switches to single-select mode. */\n specialOptions?: Array<T>\n /** Title for the special options group */\n specialOptionsTitle?: string\n /** Container element to position the combobox relative to. */\n container?: HTMLElement\n /** Alignment of the dropdown relative to the trigger. Defaults to 'center'. */\n align?: 'start' | 'center' | 'end'\n}\n\nexport type ComboboxSingleProps<T> = ComboboxBaseProps<T> & {\n multiple?: false\n value?: string\n onChange: (value: string) => void\n /** Custom render function for the selected value display. */\n renderLabel?: (selectedOption: T) => React.ReactNode\n /**\n * Async pagination function.\n * Note: Multiple selection is not supported with virtualization.\n */\n fetchPage?: (params: {\n page: number\n search?: string\n }) => Promise<{ items: Array<T>; hasNextPage: boolean; nextPage: number }>\n}\n\nexport type ComboboxMultipleProps<T> = ComboboxBaseProps<T> & {\n multiple: true\n value?: Array<string>\n onChange: (value: Array<string>) => void\n /** Custom render function for the selected value(s) display. */\n renderLabel?: (selectedOptions: Array<T>, onRemove: (value: string) => void) => React.ReactNode\n /** fetchPage is not allowed with multiple selection */\n fetchPage?: never\n}\n\nexport type ComboboxProps<T> = ComboboxSingleProps<T> | ComboboxMultipleProps<T>\n\n/**\n * A searchable combobox component with support for custom rendering, keyboard navigation, and search filtering.\n *\n * @example Basic usage\n * ```tsx\n * interface User {\n * id: string\n * name: string\n * email: string\n * }\n *\n * <Combobox<User>\n * options={users}\n * value={selectedUserId}\n * onChange={setSelectedUserId}\n * getOptionValue={(user) => user.id}\n * getOptionLabel={(user) => user.name}\n * />\n * ```\n *\n * @example Custom filtering\n * ```tsx\n * <Combobox<User>\n * options={users}\n * value={selectedUserId}\n * onChange={setSelectedUserId}\n * getOptionValue={(user) => user.id}\n * getOptionLabel={(user) => user.name}\n * filterOptions={(options, searchTerm) =>\n * options.filter(user =>\n * user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||\n * user.email.toLowerCase().includes(searchTerm.toLowerCase())\n * )\n * }\n * />\n * ```\n *\n * @example Virtualized infinite scrolling with pagination\n * ```tsx\n * <Combobox<User>\n * options={[]} // Initial options can be empty\n * value={selectedUserId}\n * onChange={setSelectedUserId}\n * getOptionValue={(user) => user.id}\n * getOptionLabel={(user) => user.name}\n * fetchPage={async ({ page, search }) => {\n * const response = await api.getUsers({ page, search })\n * return {\n * items: response.users,\n * hasNextPage: response.page < response.totalPages,\n * nextPage: page + 1\n * }\n * }}\n * />\n * ```\n */\nexport const Combobox = <T extends object>(props: ComboboxProps<T>) => {\n const {\n id,\n options,\n getOptionValue,\n getOptionLabel,\n searchPlaceholder = 'Search...',\n emptyMessage = 'No results found.',\n className = 'w-60',\n contentClassName,\n disabled = false,\n maxHeight = '300px',\n renderOption,\n loading = false,\n loadingPlaceholder = 'Cargando...',\n error = false,\n multiple,\n onClear,\n modal = false,\n selectedMultiplePlaceholder = 'Selected',\n multipleOptionsPlaceholder = 'Options',\n specialOptions,\n specialOptionsTitle,\n container,\n align,\n } = props\n\n const normalizedMaxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight\n const boundedListMaxHeight = `min(${normalizedMaxHeight}, max(120px, calc(var(--radix-popover-content-available-height, 100dvh) - 68px)))`\n\n const {\n open,\n setOpen,\n searchTerm,\n setSearchTerm,\n setIsHovered,\n hasNextPage,\n loadingMore,\n isVirtualized,\n filteredOptions,\n displayValue,\n hasValue,\n showClearButton,\n handleSelect,\n handleClear,\n loadNextPage,\n } = useCombobox(props)\n\n return (\n <div className=\"flex w-full flex-col gap-1\">\n <PopoverRoot open={open} onOpenChange={setOpen} modal={modal}>\n <PopoverTrigger\n asChild\n onPointerDown={(event) => {\n event.stopPropagation()\n if (open) {\n setOpen(false)\n event.preventDefault()\n } else {\n setOpen(true)\n }\n }}\n onClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n }}\n >\n <Button\n id={id}\n type=\"button\"\n disabled={disabled}\n variant=\"text\"\n className={cn(\n buttonVariants({ variant: 'input', size: 'lg' }),\n 'relative flex justify-between rounded-lg',\n multiple && 'renderLabel' in props && props.renderLabel ? 'h-auto min-h-12' : 'h-12',\n open && 'border-neutral-950',\n disabled && 'cursor-not-allowed',\n error && 'border-error-400 focus-visible:border-error-700',\n className,\n )}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <span\n className={cn(\n 'block',\n !hasValue && 'text-neutral-300',\n !(multiple && 'renderLabel' in props && props.renderLabel) && 'truncate',\n )}\n >\n {displayValue}\n </span>\n <CaretDown\n className={cn(\n 'h-4 w-4 shrink-0 opacity-50 transition-opacity duration-150',\n showClearButton ? 'opacity-0' : 'opacity-50',\n )}\n />\n {onClear && hasValue && (\n <X\n data-testid=\"clear-button\"\n className={cn(\n 'absolute right-4 z-10 h-4 w-4 shrink-0 cursor-pointer transition-opacity duration-150',\n showClearButton ? 'opacity-100 hover:opacity-70' : 'opacity-0',\n )}\n onClick={handleClear}\n />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn('overflow-hidden p-0', contentClassName)}\n container={container}\n side=\"bottom\"\n align={align}\n >\n <Command shouldFilter={false}>\n <CommandInput\n placeholder={searchPlaceholder}\n disabled={loading}\n value={searchTerm}\n onValueChange={setSearchTerm}\n />\n {isVirtualized ? (\n <VirtualizedComboboxList\n localOptions={filteredOptions}\n loading={loading}\n loadingPlaceholder={loadingPlaceholder}\n emptyMessage={emptyMessage}\n value={props.value as string}\n getOptionValue={getOptionValue}\n getOptionLabel={getOptionLabel}\n handleSelect={handleSelect}\n renderOption={renderOption}\n maxHeight={boundedListMaxHeight}\n hasNextPage={hasNextPage}\n loadingMore={loadingMore}\n onLoadMore={loadNextPage}\n />\n ) : (\n <StaticComboboxList\n filteredOptions={filteredOptions}\n loading={loading}\n loadingPlaceholder={loadingPlaceholder}\n emptyMessage={emptyMessage}\n multiple={!!multiple}\n value={props.value}\n getOptionValue={getOptionValue}\n getOptionLabel={getOptionLabel}\n handleSelect={handleSelect}\n renderOption={renderOption}\n maxHeight={boundedListMaxHeight}\n selectedMultiplePlaceholder={selectedMultiplePlaceholder}\n multipleOptionsPlaceholder={multipleOptionsPlaceholder}\n options={options}\n specialOptions={specialOptions}\n specialOptionsTitle={specialOptionsTitle}\n />\n )}\n </Command>\n </PopoverContent>\n </PopoverRoot>\n {typeof error === 'string' && <span className=\"text-error-500 text-sm\">{error}</span>}\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Combobox.js","sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import { CaretDown } from '@phosphor-icons/react/dist/ssr/CaretDown'\nimport { X } from '@phosphor-icons/react/dist/ssr/X'\nimport { useRef } from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { Button, buttonVariants } from '../Button'\nimport { Command, CommandInput } from '../Command'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\n\nimport { StaticComboboxList } from './StaticComboboxList'\nimport { useCombobox } from './useCombobox'\nimport { VirtualizedComboboxList } from './VirtualizedComboboxList'\n\ntype ComboboxBaseProps<T> = {\n /** Unique identifier for the combobox */\n id?: string\n /** Array of options to display in the combobox */\n options: Array<T>\n /** Function to get the unique identifier from an option. */\n getOptionValue: (option: T) => string\n /** Function to get the display text from an option. */\n getOptionLabel: (option: T) => string\n /** Custom render function for options. If not provided, defaults to showing a checkmark and label */\n renderOption?: (option: T, isSelected: boolean) => React.ReactNode\n /** Placeholder text shown when no option is selected */\n placeholder?: string\n /** Placeholder text for the search input field */\n searchPlaceholder?: string\n /** Message shown when no options match the search query */\n emptyMessage?: string\n /** Additional CSS classes to apply to the combobox trigger */\n className?: string\n /** Additional CSS classes to apply to the popover content */\n contentClassName?: string\n /** Whether the combobox is disabled */\n disabled?: boolean\n /** Maximum height of the options list. Can be any valid CSS height value */\n maxHeight?: string | number\n /** Whether to close the dropdown when an option is selected. Defaults to `true` for single select and `false` for multi-select. */\n closeOnSelect?: boolean\n /** Whether the combobox is in a loading state */\n loading?: boolean\n /** Message to show when in loading state */\n loadingPlaceholder?: string\n /** Whether the combobox has an error */\n error?: boolean | string\n /** Custom function to filter options based on search term */\n filterOptions?: (options: Array<T>, searchTerm: string) => Array<T>\n /** Callback function executed when the clear button is clicked. When provided, an X button will appear on hover to clear the selection. */\n onClear?: boolean | (() => void)\n /** Whether the selection can be cleared */\n clearable?: boolean\n /** Whether the combobox is inside a modal */\n modal?: boolean\n /** Placeholder text for the selected multiple options */\n selectedMultiplePlaceholder?: string\n /** Placeholder text for the multiple options */\n multipleOptionsPlaceholder?: string\n /** Special options that appear at the top with their own title. When selected, the combobox switches to single-select mode. */\n specialOptions?: Array<T>\n /** Title for the special options group */\n specialOptionsTitle?: string\n /** Container element to position the combobox relative to. */\n container?: HTMLElement\n /** Alignment of the dropdown relative to the trigger. Defaults to 'center'. */\n align?: 'start' | 'center' | 'end'\n /** Auto-focus the search input when the dropdown opens. */\n onOpenAutoFocus?: boolean\n}\n\nexport type ComboboxSingleProps<T> = ComboboxBaseProps<T> & {\n multiple?: false\n value?: string\n onChange: (value: string) => void\n /** Custom render function for the selected value display. */\n renderLabel?: (selectedOption: T) => React.ReactNode\n /**\n * Async pagination function.\n * Note: Multiple selection is not supported with virtualization.\n */\n fetchPage?: (params: {\n page: number\n search?: string\n }) => Promise<{ items: Array<T>; hasNextPage: boolean; nextPage: number }>\n /** Prefetch the first page on mount (virtualized only), so data is ready before opening. */\n prefetch?: boolean\n}\n\nexport type ComboboxMultipleProps<T> = ComboboxBaseProps<T> & {\n multiple: true\n value?: Array<string>\n onChange: (value: Array<string>) => void\n /** Custom render function for the selected value(s) display. */\n renderLabel?: (selectedOptions: Array<T>, onRemove: (value: string) => void) => React.ReactNode\n /** fetchPage is not allowed with multiple selection */\n fetchPage?: never\n /** prefetch is not allowed with multiple selection */\n prefetch?: never\n}\n\nexport type ComboboxProps<T> = ComboboxSingleProps<T> | ComboboxMultipleProps<T>\n\n/**\n * A searchable combobox component with support for custom rendering, keyboard navigation, and search filtering.\n *\n * @example Basic usage\n * ```tsx\n * interface User {\n * id: string\n * name: string\n * email: string\n * }\n *\n * <Combobox<User>\n * options={users}\n * value={selectedUserId}\n * onChange={setSelectedUserId}\n * getOptionValue={(user) => user.id}\n * getOptionLabel={(user) => user.name}\n * />\n * ```\n *\n * @example Custom filtering\n * ```tsx\n * <Combobox<User>\n * options={users}\n * value={selectedUserId}\n * onChange={setSelectedUserId}\n * getOptionValue={(user) => user.id}\n * getOptionLabel={(user) => user.name}\n * filterOptions={(options, searchTerm) =>\n * options.filter(user =>\n * user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||\n * user.email.toLowerCase().includes(searchTerm.toLowerCase())\n * )\n * }\n * />\n * ```\n *\n * @example Virtualized infinite scrolling with pagination\n * ```tsx\n * <Combobox<User>\n * options={[]} // Initial options can be empty\n * value={selectedUserId}\n * onChange={setSelectedUserId}\n * getOptionValue={(user) => user.id}\n * getOptionLabel={(user) => user.name}\n * fetchPage={async ({ page, search }) => {\n * const response = await api.getUsers({ page, search })\n * return {\n * items: response.users,\n * hasNextPage: response.page < response.totalPages,\n * nextPage: page + 1\n * }\n * }}\n * />\n * ```\n */\nexport const Combobox = <T extends object>(props: ComboboxProps<T>) => {\n const {\n id,\n options,\n getOptionValue,\n getOptionLabel,\n searchPlaceholder = 'Search...',\n emptyMessage = 'No results found.',\n className = 'w-60',\n contentClassName,\n disabled = false,\n maxHeight = '300px',\n renderOption,\n loading = false,\n loadingPlaceholder = 'Cargando...',\n error = false,\n multiple,\n onClear,\n modal = false,\n selectedMultiplePlaceholder = 'Selected',\n multipleOptionsPlaceholder = 'Options',\n specialOptions,\n specialOptionsTitle,\n container,\n align,\n onOpenAutoFocus = false,\n } = props\n\n const searchInputRef = useRef<HTMLInputElement>(null)\n\n const normalizedMaxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight\n const boundedListMaxHeight = `min(${normalizedMaxHeight}, max(120px, calc(var(--radix-popover-content-available-height, 100dvh) - 68px)))`\n\n const {\n open,\n setOpen,\n searchTerm,\n setSearchTerm,\n setIsHovered,\n hasNextPage,\n loadingMore,\n isVirtualized,\n filteredOptions,\n displayValue,\n hasValue,\n showClearButton,\n handleSelect,\n handleClear,\n loadNextPage,\n } = useCombobox(props)\n\n return (\n <div className=\"flex w-full flex-col gap-1\">\n <PopoverRoot open={open} onOpenChange={setOpen} modal={modal}>\n <PopoverTrigger\n asChild\n onPointerDown={(event) => {\n event.stopPropagation()\n if (open) {\n setOpen(false)\n event.preventDefault()\n } else {\n setOpen(true)\n }\n }}\n onClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n }}\n >\n <Button\n id={id}\n type=\"button\"\n disabled={disabled}\n variant=\"text\"\n className={cn(\n buttonVariants({ variant: 'input', size: 'lg' }),\n 'relative flex justify-between rounded-lg',\n multiple && 'renderLabel' in props && props.renderLabel ? 'h-auto min-h-12' : 'h-12',\n open && 'border-neutral-950',\n disabled && 'cursor-not-allowed',\n error && 'border-error-400 focus-visible:border-error-700',\n className,\n )}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <span\n className={cn(\n 'block',\n !hasValue && 'text-neutral-300',\n !(multiple && 'renderLabel' in props && props.renderLabel) && 'truncate',\n )}\n >\n {displayValue}\n </span>\n <CaretDown\n className={cn(\n 'h-4 w-4 shrink-0 opacity-50 transition-opacity duration-150',\n showClearButton ? 'opacity-0' : 'opacity-50',\n )}\n />\n {onClear && hasValue && (\n <X\n data-testid=\"clear-button\"\n className={cn(\n 'absolute right-4 z-10 h-4 w-4 shrink-0 cursor-pointer transition-opacity duration-150',\n showClearButton ? 'opacity-100 hover:opacity-70' : 'opacity-0',\n )}\n onClick={handleClear}\n />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn('overflow-hidden p-0', contentClassName)}\n container={container}\n side=\"bottom\"\n align={align}\n onOpenAutoFocus={\n onOpenAutoFocus\n ? (event) => {\n event.preventDefault()\n searchInputRef.current?.focus()\n }\n : undefined\n }\n >\n <Command shouldFilter={false}>\n <CommandInput\n ref={searchInputRef}\n placeholder={searchPlaceholder}\n disabled={loading}\n value={searchTerm}\n onValueChange={setSearchTerm}\n />\n {isVirtualized ? (\n <VirtualizedComboboxList\n localOptions={filteredOptions}\n loading={loading}\n loadingPlaceholder={loadingPlaceholder}\n emptyMessage={emptyMessage}\n value={props.value as string}\n getOptionValue={getOptionValue}\n getOptionLabel={getOptionLabel}\n handleSelect={handleSelect}\n renderOption={renderOption}\n maxHeight={boundedListMaxHeight}\n hasNextPage={hasNextPage}\n loadingMore={loadingMore}\n onLoadMore={loadNextPage}\n open={open}\n />\n ) : (\n <StaticComboboxList\n filteredOptions={filteredOptions}\n loading={loading}\n loadingPlaceholder={loadingPlaceholder}\n emptyMessage={emptyMessage}\n multiple={!!multiple}\n value={props.value}\n getOptionValue={getOptionValue}\n getOptionLabel={getOptionLabel}\n handleSelect={handleSelect}\n renderOption={renderOption}\n maxHeight={boundedListMaxHeight}\n selectedMultiplePlaceholder={selectedMultiplePlaceholder}\n multipleOptionsPlaceholder={multipleOptionsPlaceholder}\n options={options}\n specialOptions={specialOptions}\n specialOptionsTitle={specialOptionsTitle}\n />\n )}\n </Command>\n </PopoverContent>\n </PopoverRoot>\n {typeof error === 'string' && <span className=\"text-error-500 text-sm\">{error}</span>}\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA8JO,MAAM,QAAA,GAAW,CAAmB,KAAA,KAA4B;AACrE,EAAA,MAAM;AAAA,IACJ,EAAA;AAAA,IACA,OAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA,iBAAA,GAAoB,WAAA;AAAA,IACpB,YAAA,GAAe,mBAAA;AAAA,IACf,SAAA,GAAY,MAAA;AAAA,IACZ,gBAAA;AAAA,IACA,QAAA,GAAW,KAAA;AAAA,IACX,SAAA,GAAY,OAAA;AAAA,IACZ,YAAA;AAAA,IACA,OAAA,GAAU,KAAA;AAAA,IACV,kBAAA,GAAqB,aAAA;AAAA,IACrB,KAAA,GAAQ,KAAA;AAAA,IACR,QAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA,GAAQ,KAAA;AAAA,IACR,2BAAA,GAA8B,UAAA;AAAA,IAC9B,0BAAA,GAA6B,SAAA;AAAA,IAC7B,cAAA;AAAA,IACA,mBAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,eAAA,GAAkB;AAAA,GACpB,GAAI,KAAA;AAEJ,EAAA,MAAM,cAAA,GAAiB,OAAyB,IAAI,CAAA;AAEpD,EAAA,MAAM,sBAAsB,OAAO,SAAA,KAAc,QAAA,GAAW,CAAA,EAAG,SAAS,CAAA,EAAA,CAAA,GAAO,SAAA;AAC/E,EAAA,MAAM,oBAAA,GAAuB,OAAO,mBAAmB,CAAA,iFAAA,CAAA;AAEvD,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,aAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF,GAAI,YAAY,KAAK,CAAA;AAErB,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4BAAA,EACb,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAY,YAAA,EAAc,OAAA,EAAS,KAAA,EAC9C,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAO,IAAA;AAAA,UACP,aAAA,EAAe,CAAC,KAAA,KAAU;AACxB,YAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,YAAA,IAAI,IAAA,EAAM;AACR,cAAA,OAAA,CAAQ,KAAK,CAAA;AACb,cAAA,KAAA,CAAM,cAAA,EAAe;AAAA,YACvB,CAAA,MAAO;AACL,cAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,YACd;AAAA,UACF,CAAA;AAAA,UACA,OAAA,EAAS,CAAC,KAAA,KAAU;AAClB,YAAA,KAAA,CAAM,cAAA,EAAe;AACrB,YAAA,KAAA,CAAM,eAAA,EAAgB;AAAA,UACxB,CAAA;AAAA,UAEA,QAAA,kBAAA,IAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,EAAA;AAAA,cACA,IAAA,EAAK,QAAA;AAAA,cACL,QAAA;AAAA,cACA,OAAA,EAAQ,MAAA;AAAA,cACR,SAAA,EAAW,EAAA;AAAA,gBACT,eAAe,EAAE,OAAA,EAAS,OAAA,EAAS,IAAA,EAAM,MAAM,CAAA;AAAA,gBAC/C,0CAAA;AAAA,gBACA,QAAA,IAAY,aAAA,IAAiB,KAAA,IAAS,KAAA,CAAM,cAAc,iBAAA,GAAoB,MAAA;AAAA,gBAC9E,IAAA,IAAQ,oBAAA;AAAA,gBACR,QAAA,IAAY,oBAAA;AAAA,gBACZ,KAAA,IAAS,iDAAA;AAAA,gBACT;AAAA,eACF;AAAA,cACA,eAAA,EAAe,IAAA;AAAA,cACf,eAAA,EAAc,SAAA;AAAA,cACd,YAAA,EAAc,MAAM,YAAA,CAAa,IAAI,CAAA;AAAA,cACrC,YAAA,EAAc,MAAM,YAAA,CAAa,KAAK,CAAA;AAAA,cAEtC,QAAA,EAAA;AAAA,gCAAA,GAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA;AAAA,sBACT,OAAA;AAAA,sBACA,CAAC,QAAA,IAAY,kBAAA;AAAA,sBACb,EAAE,QAAA,IAAY,aAAA,IAAiB,KAAA,IAAS,MAAM,WAAA,CAAA,IAAgB;AAAA,qBAChE;AAAA,oBAEC,QAAA,EAAA;AAAA;AAAA,iBACH;AAAA,gCACA,GAAA;AAAA,kBAAC,SAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA;AAAA,sBACT,6DAAA;AAAA,sBACA,kBAAkB,WAAA,GAAc;AAAA;AAClC;AAAA,iBACF;AAAA,gBACC,WAAW,QAAA,oBACV,GAAA;AAAA,kBAAC,CAAA;AAAA,kBAAA;AAAA,oBACC,aAAA,EAAY,cAAA;AAAA,oBACZ,SAAA,EAAW,EAAA;AAAA,sBACT,uFAAA;AAAA,sBACA,kBAAkB,8BAAA,GAAiC;AAAA,qBACrD;AAAA,oBACA,OAAA,EAAS;AAAA;AAAA;AACX;AAAA;AAAA;AAEJ;AAAA,OACF;AAAA,sBACA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAW,EAAA,CAAG,qBAAA,EAAuB,gBAAgB,CAAA;AAAA,UACrD,SAAA;AAAA,UACA,IAAA,EAAK,QAAA;AAAA,UACL,KAAA;AAAA,UACA,eAAA,EACE,eAAA,GACI,CAAC,KAAA,KAAU;AACT,YAAA,KAAA,CAAM,cAAA,EAAe;AACrB,YAAA,cAAA,CAAe,SAAS,KAAA,EAAM;AAAA,UAChC,CAAA,GACA,MAAA;AAAA,UAGN,QAAA,kBAAA,IAAA,CAAC,OAAA,EAAA,EAAQ,YAAA,EAAc,KAAA,EACrB,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,YAAA;AAAA,cAAA;AAAA,gBACC,GAAA,EAAK,cAAA;AAAA,gBACL,WAAA,EAAa,iBAAA;AAAA,gBACb,QAAA,EAAU,OAAA;AAAA,gBACV,KAAA,EAAO,UAAA;AAAA,gBACP,aAAA,EAAe;AAAA;AAAA,aACjB;AAAA,YACC,aAAA,mBACC,GAAA;AAAA,cAAC,uBAAA;AAAA,cAAA;AAAA,gBACC,YAAA,EAAc,eAAA;AAAA,gBACd,OAAA;AAAA,gBACA,kBAAA;AAAA,gBACA,YAAA;AAAA,gBACA,OAAO,KAAA,CAAM,KAAA;AAAA,gBACb,cAAA;AAAA,gBACA,cAAA;AAAA,gBACA,YAAA;AAAA,gBACA,YAAA;AAAA,gBACA,SAAA,EAAW,oBAAA;AAAA,gBACX,WAAA;AAAA,gBACA,WAAA;AAAA,gBACA,UAAA,EAAY,YAAA;AAAA,gBACZ;AAAA;AAAA,aACF,mBAEA,GAAA;AAAA,cAAC,kBAAA;AAAA,cAAA;AAAA,gBACC,eAAA;AAAA,gBACA,OAAA;AAAA,gBACA,kBAAA;AAAA,gBACA,YAAA;AAAA,gBACA,QAAA,EAAU,CAAC,CAAC,QAAA;AAAA,gBACZ,OAAO,KAAA,CAAM,KAAA;AAAA,gBACb,cAAA;AAAA,gBACA,cAAA;AAAA,gBACA,YAAA;AAAA,gBACA,YAAA;AAAA,gBACA,SAAA,EAAW,oBAAA;AAAA,gBACX,2BAAA;AAAA,gBACA,0BAAA;AAAA,gBACA,OAAA;AAAA,gBACA,cAAA;AAAA,gBACA;AAAA;AAAA;AACF,WAAA,EAEJ;AAAA;AAAA;AACF,KAAA,EACF,CAAA;AAAA,IACC,OAAO,KAAA,KAAU,QAAA,wBAAa,MAAA,EAAA,EAAK,SAAA,EAAU,0BAA0B,QAAA,EAAA,KAAA,EAAM;AAAA,GAAA,EAChF,CAAA;AAEJ;;;;"}
@@ -64,7 +64,7 @@ const StaticComboboxList = ({
64
64
  /* @__PURE__ */ jsx(CommandSeparator, { className: "!mx-[10px]" })
65
65
  ] }) : null,
66
66
  !specialOptions && multiple && isArrayValue && regularSelectedOptions.length > 0 && /* @__PURE__ */ jsx(CommandGroup, { heading: selectedMultiplePlaceholder, children: regularSelectedOptions.map(renderListOption) }),
67
- /* @__PURE__ */ jsx(CommandGroup, { heading: groupHeading, children: optionsToShow.map(renderListOption) })
67
+ optionsToShow.length > 0 && /* @__PURE__ */ jsx(CommandGroup, { heading: groupHeading, children: optionsToShow.map(renderListOption) })
68
68
  ] })
69
69
  }
70
70
  );
@@ -1 +1 @@
1
- {"version":3,"file":"StaticComboboxList.js","sources":["../../../src/components/Combobox/StaticComboboxList.tsx"],"sourcesContent":["import { CheckSquare } from '@phosphor-icons/react/dist/ssr/CheckSquare'\nimport { Square } from '@phosphor-icons/react/dist/ssr/Square'\nimport { useMemo } from 'react'\n\nimport { CommandEmpty, CommandGroup, CommandItem, CommandList, CommandSeparator } from '../Command'\n\nimport { ComboboxOptionWithTooltip } from './ComboboxOptionWithTooltip'\nimport type { StaticComboboxListProps } from './types'\n\nexport const StaticComboboxList = <T extends object>({\n filteredOptions,\n loading,\n loadingPlaceholder,\n emptyMessage,\n multiple,\n value,\n getOptionValue,\n getOptionLabel,\n handleSelect,\n renderOption,\n maxHeight,\n selectedMultiplePlaceholder,\n multipleOptionsPlaceholder,\n options,\n specialOptions,\n specialOptionsTitle,\n}: StaticComboboxListProps<T>) => {\n const isArrayValue = Array.isArray(value)\n const selectedValues = useMemo(() => (multiple && isArrayValue ? value : []), [multiple, isArrayValue, value])\n\n const isOptionSelected = (optionValue: string) =>\n multiple ? selectedValues.includes(optionValue) : value === optionValue\n\n const regularOptions = useMemo(() => {\n return filteredOptions.filter(\n (option) => !specialOptions?.some((special) => getOptionValue(special) === getOptionValue(option)),\n )\n }, [filteredOptions, specialOptions, getOptionValue])\n\n const regularSelectedOptions = useMemo(() => {\n if (!multiple || !isArrayValue) return []\n\n return options.filter(\n (option) =>\n selectedValues.includes(getOptionValue(option)) &&\n !specialOptions?.some((special) => getOptionValue(special) === getOptionValue(option)),\n )\n }, [options, selectedValues, specialOptions, getOptionValue, multiple, isArrayValue])\n\n const optionsToShow = useMemo(() => {\n if (specialOptions) return regularOptions\n\n if (!multiple || !isArrayValue) return regularOptions\n\n return regularOptions.filter((option) => !selectedValues.includes(getOptionValue(option)))\n }, [regularOptions, specialOptions, selectedValues, getOptionValue, multiple, isArrayValue])\n\n const groupHeading = !specialOptions && multiple ? multipleOptionsPlaceholder : undefined\n\n const renderListOption = (option: T) => {\n const optionValue = getOptionValue(option)\n const selected = isOptionSelected(optionValue)\n\n return (\n <CommandItem key={optionValue} value={optionValue} onSelect={handleSelect}>\n {renderOption ? (\n renderOption(option, selected)\n ) : (\n <>\n {selected ? <CheckSquare className=\"mr-2 !h-6 !w-6\" /> : <Square className=\"mr-2 !h-6 !w-6\" />}\n <ComboboxOptionWithTooltip label={getOptionLabel(option)} />\n </>\n )}\n </CommandItem>\n )\n }\n\n return (\n <CommandList\n style={{ maxHeight }}\n className=\"overflow-auto overscroll-contain\"\n onWheel={(event) => event.stopPropagation()}\n >\n {loading ? (\n <div className=\"text-muted-foreground flex items-center justify-center py-6 text-sm\">{loadingPlaceholder}</div>\n ) : (\n <>\n {filteredOptions.length === 0 &&\n regularOptions.length === 0 &&\n (!specialOptions || specialOptions.length === 0) && <CommandEmpty>{emptyMessage}</CommandEmpty>}\n\n {specialOptions?.length && specialOptionsTitle ? (\n <>\n <CommandGroup heading={specialOptionsTitle}>{specialOptions.map(renderListOption)}</CommandGroup>\n\n <CommandSeparator className=\"!mx-[10px]\" />\n </>\n ) : null}\n\n {!specialOptions && multiple && isArrayValue && regularSelectedOptions.length > 0 && (\n <CommandGroup heading={selectedMultiplePlaceholder}>\n {regularSelectedOptions.map(renderListOption)}\n </CommandGroup>\n )}\n\n <CommandGroup heading={groupHeading}>{optionsToShow.map(renderListOption)}</CommandGroup>\n </>\n )}\n </CommandList>\n )\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StaticComboboxList.js","sources":["../../../src/components/Combobox/StaticComboboxList.tsx"],"sourcesContent":["import { CheckSquare } from '@phosphor-icons/react/dist/ssr/CheckSquare'\nimport { Square } from '@phosphor-icons/react/dist/ssr/Square'\nimport { useMemo } from 'react'\n\nimport { CommandEmpty, CommandGroup, CommandItem, CommandList, CommandSeparator } from '../Command'\n\nimport { ComboboxOptionWithTooltip } from './ComboboxOptionWithTooltip'\nimport type { StaticComboboxListProps } from './types'\n\nexport const StaticComboboxList = <T extends object>({\n filteredOptions,\n loading,\n loadingPlaceholder,\n emptyMessage,\n multiple,\n value,\n getOptionValue,\n getOptionLabel,\n handleSelect,\n renderOption,\n maxHeight,\n selectedMultiplePlaceholder,\n multipleOptionsPlaceholder,\n options,\n specialOptions,\n specialOptionsTitle,\n}: StaticComboboxListProps<T>) => {\n const isArrayValue = Array.isArray(value)\n const selectedValues = useMemo(() => (multiple && isArrayValue ? value : []), [multiple, isArrayValue, value])\n\n const isOptionSelected = (optionValue: string) =>\n multiple ? selectedValues.includes(optionValue) : value === optionValue\n\n const regularOptions = useMemo(() => {\n return filteredOptions.filter(\n (option) => !specialOptions?.some((special) => getOptionValue(special) === getOptionValue(option)),\n )\n }, [filteredOptions, specialOptions, getOptionValue])\n\n const regularSelectedOptions = useMemo(() => {\n if (!multiple || !isArrayValue) return []\n\n return options.filter(\n (option) =>\n selectedValues.includes(getOptionValue(option)) &&\n !specialOptions?.some((special) => getOptionValue(special) === getOptionValue(option)),\n )\n }, [options, selectedValues, specialOptions, getOptionValue, multiple, isArrayValue])\n\n const optionsToShow = useMemo(() => {\n if (specialOptions) return regularOptions\n\n if (!multiple || !isArrayValue) return regularOptions\n\n return regularOptions.filter((option) => !selectedValues.includes(getOptionValue(option)))\n }, [regularOptions, specialOptions, selectedValues, getOptionValue, multiple, isArrayValue])\n\n const groupHeading = !specialOptions && multiple ? multipleOptionsPlaceholder : undefined\n\n const renderListOption = (option: T) => {\n const optionValue = getOptionValue(option)\n const selected = isOptionSelected(optionValue)\n\n return (\n <CommandItem key={optionValue} value={optionValue} onSelect={handleSelect}>\n {renderOption ? (\n renderOption(option, selected)\n ) : (\n <>\n {selected ? <CheckSquare className=\"mr-2 !h-6 !w-6\" /> : <Square className=\"mr-2 !h-6 !w-6\" />}\n <ComboboxOptionWithTooltip label={getOptionLabel(option)} />\n </>\n )}\n </CommandItem>\n )\n }\n\n return (\n <CommandList\n style={{ maxHeight }}\n className=\"overflow-auto overscroll-contain\"\n onWheel={(event) => event.stopPropagation()}\n >\n {loading ? (\n <div className=\"text-muted-foreground flex items-center justify-center py-6 text-sm\">{loadingPlaceholder}</div>\n ) : (\n <>\n {filteredOptions.length === 0 &&\n regularOptions.length === 0 &&\n (!specialOptions || specialOptions.length === 0) && <CommandEmpty>{emptyMessage}</CommandEmpty>}\n\n {specialOptions?.length && specialOptionsTitle ? (\n <>\n <CommandGroup heading={specialOptionsTitle}>{specialOptions.map(renderListOption)}</CommandGroup>\n\n <CommandSeparator className=\"!mx-[10px]\" />\n </>\n ) : null}\n\n {!specialOptions && multiple && isArrayValue && regularSelectedOptions.length > 0 && (\n <CommandGroup heading={selectedMultiplePlaceholder}>\n {regularSelectedOptions.map(renderListOption)}\n </CommandGroup>\n )}\n\n {optionsToShow.length > 0 && (\n <CommandGroup heading={groupHeading}>{optionsToShow.map(renderListOption)}</CommandGroup>\n )}\n </>\n )}\n </CommandList>\n )\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
1
  import { VirtualizedComboboxListProps } from './types';
2
- export declare const VirtualizedComboboxList: <T extends object>({ localOptions, loading, loadingPlaceholder, emptyMessage, value, getOptionValue, getOptionLabel, handleSelect, renderOption, maxHeight, hasNextPage, loadingMore, onLoadMore, }: VirtualizedComboboxListProps<T>) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const VirtualizedComboboxList: <T extends object>({ localOptions, loading, loadingPlaceholder, emptyMessage, value, getOptionValue, getOptionLabel, handleSelect, renderOption, maxHeight, hasNextPage, loadingMore, onLoadMore, open, }: VirtualizedComboboxListProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -18,7 +18,8 @@ const VirtualizedComboboxList = ({
18
18
  maxHeight,
19
19
  hasNextPage,
20
20
  loadingMore,
21
- onLoadMore
21
+ onLoadMore,
22
+ open
22
23
  }) => {
23
24
  const parentRef = useRef(null);
24
25
  const hasTriggeredRef = useRef(false);
@@ -33,6 +34,19 @@ const VirtualizedComboboxList = ({
33
34
  useEffect(() => {
34
35
  hasTriggeredRef.current = false;
35
36
  }, [totalCount]);
37
+ const hasScrolledToSelectedRef = useRef(false);
38
+ useEffect(() => {
39
+ if (!open) {
40
+ hasScrolledToSelectedRef.current = false;
41
+ return;
42
+ }
43
+ if (hasScrolledToSelectedRef.current || !value || totalCount === 0) return;
44
+ const selectedIndex = localOptions.findIndex((option) => getOptionValue(option) === value);
45
+ if (selectedIndex >= 0) {
46
+ hasScrolledToSelectedRef.current = true;
47
+ rowVirtualizer.scrollToIndex(selectedIndex, { align: "center" });
48
+ }
49
+ }, [open, value, totalCount, localOptions, getOptionValue, rowVirtualizer]);
36
50
  const virtualItems = rowVirtualizer.getVirtualItems();
37
51
  useEffect(() => {
38
52
  if (virtualItems.length === 0 || !hasNextPage || loadingMore || loading || hasTriggeredRef.current) {
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualizedComboboxList.js","sources":["../../../src/components/Combobox/VirtualizedComboboxList.tsx"],"sourcesContent":["import { Check } from '@phosphor-icons/react/dist/ssr/Check'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport { useRef, useEffect } from 'react'\n\nimport { CommandEmpty, CommandItem } from '../Command'\n\nimport { ComboboxOptionWithTooltip } from './ComboboxOptionWithTooltip'\nimport type { VirtualizedComboboxListProps } from './types'\n\nexport const VirtualizedComboboxList = <T extends object>({\n localOptions,\n loading,\n loadingPlaceholder,\n emptyMessage,\n value,\n getOptionValue,\n getOptionLabel,\n handleSelect,\n renderOption,\n maxHeight,\n hasNextPage,\n loadingMore,\n onLoadMore,\n}: VirtualizedComboboxListProps<T>) => {\n const parentRef = useRef<HTMLDivElement>(null)\n const hasTriggeredRef = useRef(false)\n\n const totalCount = localOptions.length\n const PREFETCH_THRESHOLD = 25\n\n const rowVirtualizer = useVirtualizer({\n count: totalCount,\n getScrollElement: () => parentRef.current,\n estimateSize: () => 40,\n overscan: 10,\n })\n\n useEffect(() => {\n hasTriggeredRef.current = false\n }, [totalCount])\n\n const virtualItems = rowVirtualizer.getVirtualItems()\n\n useEffect(() => {\n if (virtualItems.length === 0 || !hasNextPage || loadingMore || loading || hasTriggeredRef.current) {\n return\n }\n\n const lastVisibleItem = virtualItems.at(-1)\n const itemsFromEnd = totalCount - (lastVisibleItem?.index ?? 0)\n\n if (itemsFromEnd <= PREFETCH_THRESHOLD) {\n hasTriggeredRef.current = true\n onLoadMore()\n }\n }, [virtualItems, totalCount, hasNextPage, loadingMore, loading, onLoadMore])\n\n if (loading && localOptions.length === 0) {\n return (\n <div className=\"text-muted-foreground flex items-center justify-center py-6 text-sm\">{loadingPlaceholder}</div>\n )\n }\n\n if (localOptions.length === 0 && !loading && !loadingMore) {\n return <CommandEmpty>{emptyMessage}</CommandEmpty>\n }\n\n const getItemAtIndex = (index: number) => {\n const option = localOptions[index]\n const isSelected = value === getOptionValue(option)\n return { option, isSelected }\n }\n\n return (\n <div\n ref={parentRef}\n className=\"overflow-auto overscroll-contain\"\n style={{ maxHeight }}\n onWheel={(event) => event.stopPropagation()}\n >\n <div style={{ height: `${rowVirtualizer.getTotalSize()}px`, position: 'relative' }}>\n {rowVirtualizer.getVirtualItems().map((virtualRow) => {\n const { option, isSelected } = getItemAtIndex(virtualRow.index)\n const optionValue = getOptionValue(option)\n\n return (\n <div\n key={optionValue}\n data-index={virtualRow.index}\n ref={(el) => {\n if (el) rowVirtualizer.measureElement(el)\n }}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${virtualRow.start}px)`,\n }}\n >\n <CommandItem value={optionValue} onSelect={handleSelect}>\n {renderOption ? (\n renderOption(option, isSelected)\n ) : (\n <>\n <Check className={`mr-2 h-4 w-4 ${isSelected ? 'opacity-100' : 'opacity-0'}`} />\n <ComboboxOptionWithTooltip label={getOptionLabel(option)} />\n </>\n )}\n </CommandItem>\n </div>\n )\n })}\n </div>\n {loadingMore && (\n <div className=\"flex items-center justify-center py-2 text-sm text-neutral-500\">{loadingPlaceholder}</div>\n )}\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VirtualizedComboboxList.js","sources":["../../../src/components/Combobox/VirtualizedComboboxList.tsx"],"sourcesContent":["import { Check } from '@phosphor-icons/react/dist/ssr/Check'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport { useRef, useEffect } from 'react'\n\nimport { CommandEmpty, CommandItem } from '../Command'\n\nimport { ComboboxOptionWithTooltip } from './ComboboxOptionWithTooltip'\nimport type { VirtualizedComboboxListProps } from './types'\n\nexport const VirtualizedComboboxList = <T extends object>({\n localOptions,\n loading,\n loadingPlaceholder,\n emptyMessage,\n value,\n getOptionValue,\n getOptionLabel,\n handleSelect,\n renderOption,\n maxHeight,\n hasNextPage,\n loadingMore,\n onLoadMore,\n open,\n}: VirtualizedComboboxListProps<T>) => {\n const parentRef = useRef<HTMLDivElement>(null)\n const hasTriggeredRef = useRef(false)\n\n const totalCount = localOptions.length\n const PREFETCH_THRESHOLD = 25\n\n const rowVirtualizer = useVirtualizer({\n count: totalCount,\n getScrollElement: () => parentRef.current,\n estimateSize: () => 40,\n overscan: 10,\n })\n\n useEffect(() => {\n hasTriggeredRef.current = false\n }, [totalCount])\n\n const hasScrolledToSelectedRef = useRef(false)\n\n useEffect(() => {\n if (!open) {\n hasScrolledToSelectedRef.current = false\n return\n }\n if (hasScrolledToSelectedRef.current || !value || totalCount === 0) return\n\n const selectedIndex = localOptions.findIndex((option) => getOptionValue(option) === value)\n if (selectedIndex >= 0) {\n hasScrolledToSelectedRef.current = true\n rowVirtualizer.scrollToIndex(selectedIndex, { align: 'center' })\n }\n }, [open, value, totalCount, localOptions, getOptionValue, rowVirtualizer])\n\n const virtualItems = rowVirtualizer.getVirtualItems()\n\n useEffect(() => {\n if (virtualItems.length === 0 || !hasNextPage || loadingMore || loading || hasTriggeredRef.current) {\n return\n }\n\n const lastVisibleItem = virtualItems.at(-1)\n const itemsFromEnd = totalCount - (lastVisibleItem?.index ?? 0)\n\n if (itemsFromEnd <= PREFETCH_THRESHOLD) {\n hasTriggeredRef.current = true\n onLoadMore()\n }\n }, [virtualItems, totalCount, hasNextPage, loadingMore, loading, onLoadMore])\n\n if (loading && localOptions.length === 0) {\n return (\n <div className=\"text-muted-foreground flex items-center justify-center py-6 text-sm\">{loadingPlaceholder}</div>\n )\n }\n\n if (localOptions.length === 0 && !loading && !loadingMore) {\n return <CommandEmpty>{emptyMessage}</CommandEmpty>\n }\n\n const getItemAtIndex = (index: number) => {\n const option = localOptions[index]\n const isSelected = value === getOptionValue(option)\n return { option, isSelected }\n }\n\n return (\n <div\n ref={parentRef}\n className=\"overflow-auto overscroll-contain\"\n style={{ maxHeight }}\n onWheel={(event) => event.stopPropagation()}\n >\n <div style={{ height: `${rowVirtualizer.getTotalSize()}px`, position: 'relative' }}>\n {rowVirtualizer.getVirtualItems().map((virtualRow) => {\n const { option, isSelected } = getItemAtIndex(virtualRow.index)\n const optionValue = getOptionValue(option)\n\n return (\n <div\n key={optionValue}\n data-index={virtualRow.index}\n ref={(el) => {\n if (el) rowVirtualizer.measureElement(el)\n }}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${virtualRow.start}px)`,\n }}\n >\n <CommandItem value={optionValue} onSelect={handleSelect}>\n {renderOption ? (\n renderOption(option, isSelected)\n ) : (\n <>\n <Check className={`mr-2 h-4 w-4 ${isSelected ? 'opacity-100' : 'opacity-0'}`} />\n <ComboboxOptionWithTooltip label={getOptionLabel(option)} />\n </>\n )}\n </CommandItem>\n </div>\n )\n })}\n </div>\n {loadingMore && (\n <div className=\"flex items-center justify-center py-2 text-sm text-neutral-500\">{loadingPlaceholder}</div>\n )}\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ export type VirtualizedComboboxListProps<T> = ComboboxListBaseProps<T> & {
14
14
  hasNextPage: boolean;
15
15
  loadingMore: boolean;
16
16
  onLoadMore: () => void;
17
+ open: boolean;
17
18
  };
18
19
  export type StaticComboboxListProps<T> = ComboboxListBaseProps<T> & {
19
20
  filteredOptions: Array<T>;
@@ -12,7 +12,8 @@ const useCombobox = (props) => {
12
12
  clearable = true,
13
13
  onClear,
14
14
  fetchPage,
15
- specialOptions
15
+ specialOptions,
16
+ prefetch
16
17
  } = props;
17
18
  const [open, setOpen] = useState(false);
18
19
  const [searchTerm, setSearchTerm] = useState("");
@@ -56,10 +57,10 @@ const useCombobox = (props) => {
56
57
  }
57
58
  }, [fetchPage, loadingMore, hasNextPage, loading, page, debouncedSearchTerm]);
58
59
  useEffect(() => {
59
- if (isVirtualized && open && localOptions.length === 0 && !loading && !loadingMore) {
60
+ if (isVirtualized && (open || prefetch) && localOptions.length === 0 && !loading && !loadingMore) {
60
61
  loadNextPage();
61
62
  }
62
- }, [isVirtualized, open, localOptions.length, loading, loadingMore, loadNextPage]);
63
+ }, [isVirtualized, open, prefetch, localOptions.length, loading, loadingMore, loadNextPage]);
63
64
  const filteredOptions = useMemo(() => {
64
65
  if (isVirtualized) {
65
66
  return localOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"useCombobox.js","sources":["../../../src/components/Combobox/useCombobox.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { ComboboxProps } from './Combobox'\n\nexport const useCombobox = <T extends object>(props: ComboboxProps<T>) => {\n const {\n options,\n getOptionValue,\n getOptionLabel,\n placeholder = 'Select...',\n loading = false,\n filterOptions,\n multiple,\n clearable = true,\n onClear,\n fetchPage,\n specialOptions,\n } = props\n\n const [open, setOpen] = useState(false)\n const [searchTerm, setSearchTerm] = useState('')\n const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('')\n const [isHovered, setIsHovered] = useState(false)\n\n const [page, setPage] = useState(1)\n const [localOptions, setLocalOptions] = useState<Array<T>>(options)\n const [hasNextPage, setHasNextPage] = useState(true)\n const [loadingMore, setLoadingMore] = useState(false)\n\n const debounceTimerRef = useRef<NodeJS.Timeout | undefined>(undefined)\n\n const closeOnSelect = props.closeOnSelect ?? !props.multiple\n const isVirtualized = !!fetchPage\n\n useEffect(() => {\n if (!isVirtualized) return\n\n clearTimeout(debounceTimerRef.current)\n\n debounceTimerRef.current = setTimeout(() => {\n setDebouncedSearchTerm(searchTerm)\n }, 300)\n\n return () => {\n clearTimeout(debounceTimerRef.current)\n }\n }, [searchTerm, isVirtualized])\n\n useEffect(() => {\n if (isVirtualized) {\n setPage(1)\n setLocalOptions([])\n setHasNextPage(true)\n setLoadingMore(false)\n }\n }, [debouncedSearchTerm, isVirtualized])\n\n const loadNextPage = useCallback(async () => {\n if (!fetchPage || loadingMore || !hasNextPage || loading) return\n\n setLoadingMore(true)\n try {\n const result = await fetchPage({ page, search: debouncedSearchTerm || undefined })\n setLocalOptions((prev) => [...prev, ...result.items])\n setPage(result.nextPage)\n setHasNextPage(result.hasNextPage)\n } finally {\n setLoadingMore(false)\n }\n }, [fetchPage, loadingMore, hasNextPage, loading, page, debouncedSearchTerm])\n\n // Load initial page when combobox opens\n useEffect(() => {\n if (isVirtualized && open && localOptions.length === 0 && !loading && !loadingMore) {\n loadNextPage()\n }\n }, [isVirtualized, open, localOptions.length, loading, loadingMore, loadNextPage])\n\n const filteredOptions = useMemo(() => {\n if (isVirtualized) {\n return localOptions\n }\n\n if (!filterOptions) {\n return options.filter((option) => getOptionLabel(option).toLowerCase().includes(searchTerm.toLowerCase()))\n }\n\n return searchTerm ? filterOptions(options, searchTerm) : options\n }, [isVirtualized, localOptions, filterOptions, options, searchTerm, getOptionLabel])\n\n const handleSelect = useCallback(\n (currentValue: string) => {\n if (multiple) {\n const { value = [], onChange } = props\n const isRemoving = value.includes(currentValue)\n\n if (specialOptions && specialOptions.length > 0) {\n if (isRemoving) {\n onChange([])\n } else {\n onChange([currentValue])\n }\n } else {\n if (isRemoving && !clearable && value.length === 1) {\n return\n }\n\n const newValues = isRemoving ? value.filter((val) => val !== currentValue) : [...value, currentValue]\n onChange(newValues)\n }\n } else {\n const { value, onChange } = props\n const newValue = clearable && currentValue === value ? '' : currentValue\n onChange(newValue)\n }\n\n if (closeOnSelect) {\n setOpen(false)\n }\n },\n [multiple, clearable, closeOnSelect, props, specialOptions],\n )\n\n const handleRemove = useCallback(\n (valueToRemove: string) => {\n if (props.multiple) {\n const { value = [], onChange } = props\n\n if (!clearable && value.length === 1) {\n return\n }\n\n const newValues = value.filter((val) => val !== valueToRemove)\n onChange(newValues)\n }\n },\n [props, clearable],\n )\n\n const handleClear = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n\n if (multiple) {\n const { onChange } = props\n\n if (clearable) {\n onChange([])\n }\n } else {\n const { onChange } = props\n onChange('')\n }\n\n if (typeof onClear === 'function') {\n onClear()\n }\n },\n [multiple, clearable, onClear, props],\n )\n\n const displayValue = useMemo(() => {\n const availableOptions = isVirtualized ? localOptions : options\n const allAvailableOptions = specialOptions ? [...specialOptions, ...availableOptions] : availableOptions\n\n if (multiple) {\n const { value = [], renderLabel } = props\n if (value.length === 0) return placeholder\n\n const selectedOptions = allAvailableOptions.filter((option) => value.includes(getOptionValue(option)))\n\n if (renderLabel) {\n return renderLabel(selectedOptions, handleRemove)\n }\n\n return selectedOptions.map(getOptionLabel).join(', ')\n }\n\n const { value, renderLabel } = props\n const selectedOption = allAvailableOptions.find((option) => getOptionValue(option) === value)\n if (renderLabel && selectedOption) {\n return renderLabel(selectedOption)\n }\n return selectedOption ? getOptionLabel(selectedOption) : placeholder\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n props,\n getOptionLabel,\n getOptionValue,\n options,\n localOptions,\n isVirtualized,\n placeholder,\n multiple,\n specialOptions,\n ])\n\n const hasValue = useMemo(() => {\n if (multiple) {\n return props.value && props.value.length > 0\n }\n return !!props.value\n }, [props.value, multiple])\n\n const showClearButton = onClear && hasValue && isHovered && clearable\n\n return {\n // State\n open,\n setOpen,\n searchTerm,\n setSearchTerm,\n isHovered,\n setIsHovered,\n localOptions,\n hasNextPage,\n loadingMore,\n\n // Computed values\n isVirtualized,\n filteredOptions,\n displayValue,\n hasValue,\n showClearButton,\n\n // Handlers\n handleSelect,\n handleRemove,\n handleClear,\n loadNextPage,\n }\n}\n"],"names":["value","renderLabel"],"mappings":";;AAIO,MAAM,WAAA,GAAc,CAAmB,KAAA,KAA4B;AACxE,EAAA,MAAM;AAAA,IACJ,OAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA,WAAA,GAAc,WAAA;AAAA,IACd,OAAA,GAAU,KAAA;AAAA,IACV,aAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA,GAAY,IAAA;AAAA,IACZ,OAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF,GAAI,KAAA;AAEJ,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAS,EAAE,CAAA;AAC/C,EAAA,MAAM,CAAC,mBAAA,EAAqB,sBAAsB,CAAA,GAAI,SAAS,EAAE,CAAA;AACjE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAEhD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,CAAC,CAAA;AAClC,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAmB,OAAO,CAAA;AAClE,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,IAAI,CAAA;AACnD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AAEpD,EAAA,MAAM,gBAAA,GAAmB,OAAmC,MAAS,CAAA;AAErE,EAAA,MAAM,aAAA,GAAgB,KAAA,CAAM,aAAA,IAAiB,CAAC,KAAA,CAAM,QAAA;AACpD,EAAA,MAAM,aAAA,GAAgB,CAAC,CAAC,SAAA;AAExB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,aAAA,EAAe;AAEpB,IAAA,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAErC,IAAA,gBAAA,CAAiB,OAAA,GAAU,WAAW,MAAM;AAC1C,MAAA,sBAAA,CAAuB,UAAU,CAAA;AAAA,IACnC,GAAG,GAAG,CAAA;AAEN,IAAA,OAAO,MAAM;AACX,MAAA,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAAA,IACvC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAA,EAAY,aAAa,CAAC,CAAA;AAE9B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,OAAA,CAAQ,CAAC,CAAA;AACT,MAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,MAAA,cAAA,CAAe,IAAI,CAAA;AACnB,MAAA,cAAA,CAAe,KAAK,CAAA;AAAA,IACtB;AAAA,EACF,CAAA,EAAG,CAAC,mBAAA,EAAqB,aAAa,CAAC,CAAA;AAEvC,EAAA,MAAM,YAAA,GAAe,YAAY,YAAY;AAC3C,IAAA,IAAI,CAAC,SAAA,IAAa,WAAA,IAAe,CAAC,eAAe,OAAA,EAAS;AAE1D,IAAA,cAAA,CAAe,IAAI,CAAA;AACnB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAU,EAAE,MAAM,MAAA,EAAQ,mBAAA,IAAuB,QAAW,CAAA;AACjF,MAAA,eAAA,CAAgB,CAAC,SAAS,CAAC,GAAG,MAAM,GAAG,MAAA,CAAO,KAAK,CAAC,CAAA;AACpD,MAAA,OAAA,CAAQ,OAAO,QAAQ,CAAA;AACvB,MAAA,cAAA,CAAe,OAAO,WAAW,CAAA;AAAA,IACnC,CAAA,SAAE;AACA,MAAA,cAAA,CAAe,KAAK,CAAA;AAAA,IACtB;AAAA,EACF,CAAA,EAAG,CAAC,SAAA,EAAW,WAAA,EAAa,aAAa,OAAA,EAAS,IAAA,EAAM,mBAAmB,CAAC,CAAA;AAG5E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,aAAA,IAAiB,QAAQ,YAAA,CAAa,MAAA,KAAW,KAAK,CAAC,OAAA,IAAW,CAAC,WAAA,EAAa;AAClF,MAAA,YAAA,EAAa;AAAA,IACf;AAAA,EACF,CAAA,EAAG,CAAC,aAAA,EAAe,IAAA,EAAM,aAAa,MAAA,EAAQ,OAAA,EAAS,WAAA,EAAa,YAAY,CAAC,CAAA;AAEjF,EAAA,MAAM,eAAA,GAAkB,QAAQ,MAAM;AACpC,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,OAAO,YAAA;AAAA,IACT;AAEA,IAAA,IAAI,CAAC,aAAA,EAAe;AAClB,MAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,CAAC,MAAA,KAAW,cAAA,CAAe,MAAM,CAAA,CAAE,WAAA,EAAY,CAAE,QAAA,CAAS,UAAA,CAAW,WAAA,EAAa,CAAC,CAAA;AAAA,IAC3G;AAEA,IAAA,OAAO,UAAA,GAAa,aAAA,CAAc,OAAA,EAAS,UAAU,CAAA,GAAI,OAAA;AAAA,EAC3D,CAAA,EAAG,CAAC,aAAA,EAAe,YAAA,EAAc,eAAe,OAAA,EAAS,UAAA,EAAY,cAAc,CAAC,CAAA;AAEpF,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,CAAC,YAAA,KAAyB;AACxB,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,MAAM,EAAE,KAAA,GAAQ,EAAC,EAAG,UAAS,GAAI,KAAA;AACjC,QAAA,MAAM,UAAA,GAAa,KAAA,CAAM,QAAA,CAAS,YAAY,CAAA;AAE9C,QAAA,IAAI,cAAA,IAAkB,cAAA,CAAe,MAAA,GAAS,CAAA,EAAG;AAC/C,UAAA,IAAI,UAAA,EAAY;AACd,YAAA,QAAA,CAAS,EAAE,CAAA;AAAA,UACb,CAAA,MAAO;AACL,YAAA,QAAA,CAAS,CAAC,YAAY,CAAC,CAAA;AAAA,UACzB;AAAA,QACF,CAAA,MAAO;AACL,UAAA,IAAI,UAAA,IAAc,CAAC,SAAA,IAAa,KAAA,CAAM,WAAW,CAAA,EAAG;AAClD,YAAA;AAAA,UACF;AAEA,UAAA,MAAM,SAAA,GAAY,UAAA,GAAa,KAAA,CAAM,MAAA,CAAO,CAAC,GAAA,KAAQ,GAAA,KAAQ,YAAY,CAAA,GAAI,CAAC,GAAG,KAAA,EAAO,YAAY,CAAA;AACpG,UAAA,QAAA,CAAS,SAAS,CAAA;AAAA,QACpB;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAS,GAAI,KAAA;AAC5B,QAAA,MAAM,QAAA,GAAW,SAAA,IAAa,YAAA,KAAiB,KAAA,GAAQ,EAAA,GAAK,YAAA;AAC5D,QAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,MACnB;AAEA,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAA,EAAU,SAAA,EAAW,aAAA,EAAe,OAAO,cAAc;AAAA,GAC5D;AAEA,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,CAAC,aAAA,KAA0B;AACzB,MAAA,IAAI,MAAM,QAAA,EAAU;AAClB,QAAA,MAAM,EAAE,KAAA,GAAQ,EAAC,EAAG,UAAS,GAAI,KAAA;AAEjC,QAAA,IAAI,CAAC,SAAA,IAAa,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AACpC,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,YAAY,KAAA,CAAM,MAAA,CAAO,CAAC,GAAA,KAAQ,QAAQ,aAAa,CAAA;AAC7D,QAAA,QAAA,CAAS,SAAS,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,OAAO,SAAS;AAAA,GACnB;AAEA,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,KAAA,KAA4B;AAC3B,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA,KAAA,CAAM,eAAA,EAAgB;AAEtB,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,MAAM,EAAE,UAAS,GAAI,KAAA;AAErB,QAAA,IAAI,SAAA,EAAW;AACb,UAAA,QAAA,CAAS,EAAE,CAAA;AAAA,QACb;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAM,EAAE,UAAS,GAAI,KAAA;AACrB,QAAA,QAAA,CAAS,EAAE,CAAA;AAAA,MACb;AAEA,MAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AACjC,QAAA,OAAA,EAAQ;AAAA,MACV;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAA,EAAU,SAAA,EAAW,OAAA,EAAS,KAAK;AAAA,GACtC;AAEA,EAAA,MAAM,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAA,MAAM,gBAAA,GAAmB,gBAAgB,YAAA,GAAe,OAAA;AACxD,IAAA,MAAM,sBAAsB,cAAA,GAAiB,CAAC,GAAG,cAAA,EAAgB,GAAG,gBAAgB,CAAA,GAAI,gBAAA;AAExF,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,EAAE,KAAA,EAAAA,MAAAA,GAAQ,EAAC,EAAG,WAAA,EAAAC,cAAY,GAAI,KAAA;AACpC,MAAA,IAAID,MAAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,WAAA;AAE/B,MAAA,MAAM,eAAA,GAAkB,mBAAA,CAAoB,MAAA,CAAO,CAAC,MAAA,KAAWA,OAAM,QAAA,CAAS,cAAA,CAAe,MAAM,CAAC,CAAC,CAAA;AAErG,MAAA,IAAIC,YAAAA,EAAa;AACf,QAAA,OAAOA,YAAAA,CAAY,iBAAiB,YAAY,CAAA;AAAA,MAClD;AAEA,MAAA,OAAO,eAAA,CAAgB,GAAA,CAAI,cAAc,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,IACtD;AAEA,IAAA,MAAM,EAAE,KAAA,EAAO,WAAA,EAAY,GAAI,KAAA;AAC/B,IAAA,MAAM,cAAA,GAAiB,oBAAoB,IAAA,CAAK,CAAC,WAAW,cAAA,CAAe,MAAM,MAAM,KAAK,CAAA;AAC5F,IAAA,IAAI,eAAe,cAAA,EAAgB;AACjC,MAAA,OAAO,YAAY,cAAc,CAAA;AAAA,IACnC;AACA,IAAA,OAAO,cAAA,GAAiB,cAAA,CAAe,cAAc,CAAA,GAAI,WAAA;AAAA,EAE3D,CAAA,EAAG;AAAA,IACD,KAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAM;AAC7B,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAO,KAAA,CAAM,KAAA,IAAS,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAA;AAAA,IAC7C;AACA,IAAA,OAAO,CAAC,CAAC,KAAA,CAAM,KAAA;AAAA,EACjB,CAAA,EAAG,CAAC,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAC,CAAA;AAE1B,EAAA,MAAM,eAAA,GAAkB,OAAA,IAAW,QAAA,IAAY,SAAA,IAAa,SAAA;AAE5D,EAAA,OAAO;AAAA;AAAA,IAEL,IAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,aAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA;AAAA,IAGA,aAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA,eAAA;AAAA;AAAA,IAGA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
1
+ {"version":3,"file":"useCombobox.js","sources":["../../../src/components/Combobox/useCombobox.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { ComboboxProps } from './Combobox'\n\nexport const useCombobox = <T extends object>(props: ComboboxProps<T>) => {\n const {\n options,\n getOptionValue,\n getOptionLabel,\n placeholder = 'Select...',\n loading = false,\n filterOptions,\n multiple,\n clearable = true,\n onClear,\n fetchPage,\n specialOptions,\n prefetch,\n } = props\n\n const [open, setOpen] = useState(false)\n const [searchTerm, setSearchTerm] = useState('')\n const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('')\n const [isHovered, setIsHovered] = useState(false)\n\n const [page, setPage] = useState(1)\n const [localOptions, setLocalOptions] = useState<Array<T>>(options)\n const [hasNextPage, setHasNextPage] = useState(true)\n const [loadingMore, setLoadingMore] = useState(false)\n\n const debounceTimerRef = useRef<NodeJS.Timeout | undefined>(undefined)\n\n const closeOnSelect = props.closeOnSelect ?? !props.multiple\n const isVirtualized = !!fetchPage\n\n useEffect(() => {\n if (!isVirtualized) return\n\n clearTimeout(debounceTimerRef.current)\n\n debounceTimerRef.current = setTimeout(() => {\n setDebouncedSearchTerm(searchTerm)\n }, 300)\n\n return () => {\n clearTimeout(debounceTimerRef.current)\n }\n }, [searchTerm, isVirtualized])\n\n useEffect(() => {\n if (isVirtualized) {\n setPage(1)\n setLocalOptions([])\n setHasNextPage(true)\n setLoadingMore(false)\n }\n }, [debouncedSearchTerm, isVirtualized])\n\n const loadNextPage = useCallback(async () => {\n if (!fetchPage || loadingMore || !hasNextPage || loading) return\n\n setLoadingMore(true)\n try {\n const result = await fetchPage({ page, search: debouncedSearchTerm || undefined })\n setLocalOptions((prev) => [...prev, ...result.items])\n setPage(result.nextPage)\n setHasNextPage(result.hasNextPage)\n } finally {\n setLoadingMore(false)\n }\n }, [fetchPage, loadingMore, hasNextPage, loading, page, debouncedSearchTerm])\n\n // Load initial page when combobox opens (or on mount when prefetch enabled)\n useEffect(() => {\n if (isVirtualized && (open || prefetch) && localOptions.length === 0 && !loading && !loadingMore) {\n loadNextPage()\n }\n }, [isVirtualized, open, prefetch, localOptions.length, loading, loadingMore, loadNextPage])\n\n const filteredOptions = useMemo(() => {\n if (isVirtualized) {\n return localOptions\n }\n\n if (!filterOptions) {\n return options.filter((option) => getOptionLabel(option).toLowerCase().includes(searchTerm.toLowerCase()))\n }\n\n return searchTerm ? filterOptions(options, searchTerm) : options\n }, [isVirtualized, localOptions, filterOptions, options, searchTerm, getOptionLabel])\n\n const handleSelect = useCallback(\n (currentValue: string) => {\n if (multiple) {\n const { value = [], onChange } = props\n const isRemoving = value.includes(currentValue)\n\n if (specialOptions && specialOptions.length > 0) {\n if (isRemoving) {\n onChange([])\n } else {\n onChange([currentValue])\n }\n } else {\n if (isRemoving && !clearable && value.length === 1) {\n return\n }\n\n const newValues = isRemoving ? value.filter((val) => val !== currentValue) : [...value, currentValue]\n onChange(newValues)\n }\n } else {\n const { value, onChange } = props\n const newValue = clearable && currentValue === value ? '' : currentValue\n onChange(newValue)\n }\n\n if (closeOnSelect) {\n setOpen(false)\n }\n },\n [multiple, clearable, closeOnSelect, props, specialOptions],\n )\n\n const handleRemove = useCallback(\n (valueToRemove: string) => {\n if (props.multiple) {\n const { value = [], onChange } = props\n\n if (!clearable && value.length === 1) {\n return\n }\n\n const newValues = value.filter((val) => val !== valueToRemove)\n onChange(newValues)\n }\n },\n [props, clearable],\n )\n\n const handleClear = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n\n if (multiple) {\n const { onChange } = props\n\n if (clearable) {\n onChange([])\n }\n } else {\n const { onChange } = props\n onChange('')\n }\n\n if (typeof onClear === 'function') {\n onClear()\n }\n },\n [multiple, clearable, onClear, props],\n )\n\n const displayValue = useMemo(() => {\n const availableOptions = isVirtualized ? localOptions : options\n const allAvailableOptions = specialOptions ? [...specialOptions, ...availableOptions] : availableOptions\n\n if (multiple) {\n const { value = [], renderLabel } = props\n if (value.length === 0) return placeholder\n\n const selectedOptions = allAvailableOptions.filter((option) => value.includes(getOptionValue(option)))\n\n if (renderLabel) {\n return renderLabel(selectedOptions, handleRemove)\n }\n\n return selectedOptions.map(getOptionLabel).join(', ')\n }\n\n const { value, renderLabel } = props\n const selectedOption = allAvailableOptions.find((option) => getOptionValue(option) === value)\n if (renderLabel && selectedOption) {\n return renderLabel(selectedOption)\n }\n return selectedOption ? getOptionLabel(selectedOption) : placeholder\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n props,\n getOptionLabel,\n getOptionValue,\n options,\n localOptions,\n isVirtualized,\n placeholder,\n multiple,\n specialOptions,\n ])\n\n const hasValue = useMemo(() => {\n if (multiple) {\n return props.value && props.value.length > 0\n }\n return !!props.value\n }, [props.value, multiple])\n\n const showClearButton = onClear && hasValue && isHovered && clearable\n\n return {\n // State\n open,\n setOpen,\n searchTerm,\n setSearchTerm,\n isHovered,\n setIsHovered,\n localOptions,\n hasNextPage,\n loadingMore,\n\n // Computed values\n isVirtualized,\n filteredOptions,\n displayValue,\n hasValue,\n showClearButton,\n\n // Handlers\n handleSelect,\n handleRemove,\n handleClear,\n loadNextPage,\n }\n}\n"],"names":["value","renderLabel"],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "periplo-ui",
3
3
  "description": "IATI UI library",
4
4
  "private": false,
5
- "version": "4.2.1",
5
+ "version": "4.3.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",