periplo-ui 3.9.0 → 3.9.1
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.
|
@@ -46,8 +46,8 @@ const MultiSelect = React.forwardRef(
|
|
|
46
46
|
}
|
|
47
47
|
}, []);
|
|
48
48
|
React.useEffect(() => {
|
|
49
|
-
onChange?.(selected.map((
|
|
50
|
-
}, [selected
|
|
49
|
+
onChange?.(selected.map((item) => item.value));
|
|
50
|
+
}, [selected]);
|
|
51
51
|
return /* @__PURE__ */ jsx(PopoverRoot, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(Command, { onKeyDown: handleKeyDown, children: [
|
|
52
52
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
53
53
|
Button,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiSelect.js","sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../Command'\nimport { CaretUpDown, Check } from '@phosphor-icons/react'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { Chip } from '../Chip'\nimport { Button } from '../Button'\nimport { ControllerFieldState } from 'react-hook-form'\n\n/**\n * Represents a selectable option in the MultiSelect component\n */\ntype Option = {\n /** Unique identifier for the option */\n value: string\n /** Display text for the option */\n label: string\n}\n\nexport interface MultiSelectProps {\n /**\n * Array of options to display in the select menu\n */\n options: Array<Option>\n\n /**\n * Props to be passed to the individual Chip components\n */\n chipProps?: React.ComponentProps<typeof Chip>\n\n /**\n * Callback fired when selected values change\n * @param value Array of selected option values\n */\n onChange?: (value: string[]) => void\n\n /**\n * Form field state from react-hook-form\n */\n fieldState?: ControllerFieldState\n\n /**\n * Initially selected option values\n */\n defaultValue?: Array<string>\n\n /**\n * Text shown in the select button when no option is selected\n * @default \"Select your options...\"\n */\n placeholder?: string\n\n /**\n * Text shown when no options match the search query\n * @default \"Options not found\"\n */\n notFoundText?: string\n\n /**\n * Placeholder text for the search input\n * @default \"Search...\"\n */\n commandInputPlaceholder?: string\n\n /**\n * Maximum number of items that can be selected\n * Shows a counter chip when specified\n */\n max?: number\n}\n\n/**\n * A multi-select component with search functionality and chip display for selected items.\n * Supports keyboard navigation, search filtering, and maximum selection limit.\n *\n * @example\n * // Basic usage\n * <MultiSelect\n * options={[\n * { value: '1', label: 'Option 1' },\n * { value: '2', label: 'Option 2' }\n * ]}\n * onChange={(values) => console.log(values)}\n * />\n *\n * // With maximum selection limit\n * <MultiSelect\n * options={options}\n * max={3}\n * placeholder=\"Select up to 3 options\"\n * />\n *\n * // With custom chip styling\n * <MultiSelect\n * options={options}\n * chipProps={{ variant: 'primary' }}\n * />\n */\nexport const MultiSelect = React.forwardRef<HTMLInputElement, MultiSelectProps>(\n (\n {\n options,\n onChange,\n chipProps,\n defaultValue,\n placeholder = 'Select your options...',\n notFoundText = 'Options not found',\n fieldState,\n commandInputPlaceholder = 'Search...',\n max,\n },\n _ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const [open, setOpen] = React.useState(false)\n const [selected, setSelected] = React.useState<MultiSelectProps['options']>(\n options.filter((option) => defaultValue?.includes(option.value) ?? []),\n )\n const [inputValue, setInputValue] = React.useState('')\n\n const handleUnselect = React.useCallback((item: Option) => {\n setSelected((prev) => prev.filter((s) => s.value !== item.value))\n }, [])\n\n const handleKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n const input = inputRef.current\n if (input) {\n if (e.key === 'Delete' || e.key === 'Backspace') {\n if (input.value === '') {\n setSelected((prev) => {\n const newSelected = [...prev]\n newSelected.pop()\n return newSelected\n })\n }\n }\n\n if (e.key === 'Escape') {\n input.blur()\n }\n }\n }, [])\n\n React.useEffect(() => {\n onChange?.(selected.map((
|
|
1
|
+
{"version":3,"file":"MultiSelect.js","sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../Command'\nimport { CaretUpDown, Check } from '@phosphor-icons/react'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { Chip } from '../Chip'\nimport { Button } from '../Button'\nimport { ControllerFieldState } from 'react-hook-form'\n\n/**\n * Represents a selectable option in the MultiSelect component\n */\ntype Option = {\n /** Unique identifier for the option */\n value: string\n /** Display text for the option */\n label: string\n}\n\nexport interface MultiSelectProps {\n /**\n * Array of options to display in the select menu\n */\n options: Array<Option>\n\n /**\n * Props to be passed to the individual Chip components\n */\n chipProps?: React.ComponentProps<typeof Chip>\n\n /**\n * Callback fired when selected values change\n * @param value Array of selected option values\n */\n onChange?: (value: string[]) => void\n\n /**\n * Form field state from react-hook-form\n */\n fieldState?: ControllerFieldState\n\n /**\n * Initially selected option values\n */\n defaultValue?: Array<string>\n\n /**\n * Text shown in the select button when no option is selected\n * @default \"Select your options...\"\n */\n placeholder?: string\n\n /**\n * Text shown when no options match the search query\n * @default \"Options not found\"\n */\n notFoundText?: string\n\n /**\n * Placeholder text for the search input\n * @default \"Search...\"\n */\n commandInputPlaceholder?: string\n\n /**\n * Maximum number of items that can be selected\n * Shows a counter chip when specified\n */\n max?: number\n}\n\n/**\n * A multi-select component with search functionality and chip display for selected items.\n * Supports keyboard navigation, search filtering, and maximum selection limit.\n *\n * @example\n * // Basic usage\n * <MultiSelect\n * options={[\n * { value: '1', label: 'Option 1' },\n * { value: '2', label: 'Option 2' }\n * ]}\n * onChange={(values) => console.log(values)}\n * />\n *\n * // With maximum selection limit\n * <MultiSelect\n * options={options}\n * max={3}\n * placeholder=\"Select up to 3 options\"\n * />\n *\n * // With custom chip styling\n * <MultiSelect\n * options={options}\n * chipProps={{ variant: 'primary' }}\n * />\n */\nexport const MultiSelect = React.forwardRef<HTMLInputElement, MultiSelectProps>(\n (\n {\n options,\n onChange,\n chipProps,\n defaultValue,\n placeholder = 'Select your options...',\n notFoundText = 'Options not found',\n fieldState,\n commandInputPlaceholder = 'Search...',\n max,\n },\n _ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const [open, setOpen] = React.useState(false)\n const [selected, setSelected] = React.useState<MultiSelectProps['options']>(\n options.filter((option) => defaultValue?.includes(option.value) ?? []),\n )\n const [inputValue, setInputValue] = React.useState('')\n\n const handleUnselect = React.useCallback((item: Option) => {\n setSelected((prev) => prev.filter((s) => s.value !== item.value))\n }, [])\n\n const handleKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n const input = inputRef.current\n if (input) {\n if (e.key === 'Delete' || e.key === 'Backspace') {\n if (input.value === '') {\n setSelected((prev) => {\n const newSelected = [...prev]\n newSelected.pop()\n return newSelected\n })\n }\n }\n\n if (e.key === 'Escape') {\n input.blur()\n }\n }\n }, [])\n\n React.useEffect(() => {\n onChange?.(selected.map((item) => item.value))\n }, [selected])\n\n return (\n <PopoverRoot open={open} onOpenChange={setOpen}>\n <Command onKeyDown={handleKeyDown}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n role=\"combobox\"\n aria-expanded={open}\n className={`w-full justify-between bg-white ${\n fieldState?.invalid ? 'border-error-400 focus-within:border-error-700' : ''\n }`}\n >\n {placeholder}\n <CaretUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"px-0 py-0\">\n <CommandInput\n placeholder={commandInputPlaceholder}\n ref={inputRef}\n value={inputValue}\n onValueChange={setInputValue}\n >\n {max && (\n <Chip\n variant={selected.length >= max ? 'primary' : 'default'}\n size=\"sm\"\n className=\"my-1 whitespace-nowrap\"\n >\n {selected.length} / {max}\n </Chip>\n )}\n </CommandInput>\n\n <CommandList>\n <CommandEmpty>{notFoundText}</CommandEmpty>\n {open && options.length > 0 && (\n <CommandGroup>\n {options.map((item) => (\n <CommandItem\n key={`item-${item.value}`}\n onMouseDown={(e) => {\n e.preventDefault()\n e.stopPropagation()\n }}\n onSelect={(value) => {\n const isSelected = selected.find((s) => s.label === value)\n if (isSelected) {\n handleUnselect(isSelected)\n } else if (!max || selected.length < max) {\n setInputValue('')\n setSelected((prev) => [...prev, item])\n }\n }}\n className=\"cursor-pointer\"\n >\n <div className=\"flex\">\n <div className=\"mr-2\">\n {selected.find((s) => s.value === item.value) ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <div className=\"h-4 w-4\" />\n )}\n </div>\n <div>{item.label}</div>\n </div>\n </CommandItem>\n ))}\n </CommandGroup>\n )}\n </CommandList>\n </PopoverContent>\n {selected.length > 0 && (\n <div className=\"flex flex-wrap gap-2 pt-2\">\n {selected.map((item) => (\n <Chip key={item.value} {...chipProps} className=\"gap-2 pr-1\" onRemove={() => handleUnselect(item)}>\n {item.label}\n </Chip>\n ))}\n </div>\n )}\n </Command>\n </PopoverRoot>\n )\n },\n)\n\nMultiSelect.displayName = 'MultiSelect'\n"],"names":[],"mappings":";;;;;;;;;AAmGO;AAA0B;AAE7B;AACE;AACA;AACA;AACA;AACc;AACC;AACf;AAC0B;AAC1B;AAIF;AACA;AACA;AAAsC;AACiC;AAEvE;AAEA;AACE;AAAgE;AAGlE;AACE;AACA;AACE;AACE;AACE;AACE;AACA;AACA;AAAO;AACR;AACH;AAGF;AACE;AAAW;AACb;AACF;AAGF;AACE;AAA6C;AAG/C;AAGM;AACE;AAAC;AAAA;AACS;AACH;AACU;AAGf;AAEC;AAAA;AACyD;AAAA;AAAA;AAE9D;AAEE;AAAA;AAAC;AAAA;AACc;AACR;AACE;AACQ;AAGb;AAAC;AAAA;AAC+C;AACzC;AACK;AAET;AAAS;AAAO;AAAI;AAAA;AAAA;AACvB;AAAA;AAEJ;AAGE;AAA4B;AAItB;AAAC;AAAA;AAGG;AACA;AAAkB;AACpB;AAEE;AACA;AACE;AAAyB;AAEzB;AACA;AAAqC;AACvC;AACF;AACU;AAGR;AAMA;AACiB;AACnB;AAAA;AAzBuB;AA4B7B;AAEJ;AACF;AAQE;AAGN;AAGN;AAEA;;"}
|