UNPKG

ivt

Version:

Ivt Components Library

288 lines (285 loc) 13.9 kB
import { cva } from 'class-variance-authority'; import * as React from 'react'; import { c as cn } from './utils-BDcRwQMd.mjs'; import { r as removeSpecialCharacters } from './string-DRS4HkY8.mjs'; import { X } from './x-B6hDJA_l.mjs'; import { C as ChevronsUpDown } from './chevrons-up-down-CdBVNy4c.mjs'; import { C as Check } from './check-Ds358kZc.mjs'; import { W as WandSparkles } from './wand-sparkles-DqgQle7F.mjs'; import { L as Label } from './label-D0iRy8Wj.mjs'; import { P as Popover, a as PopoverTrigger, b as PopoverContent } from './popover-CdZgcLPo.mjs'; import { B as Button } from './button-CZpmCmNo.mjs'; import { B as Badge } from './badge-ZU62MOiS.mjs'; import { c as TooltipProvider, T as Tooltip, a as TooltipTrigger, b as TooltipContent } from './tooltip-njN_ok-r.mjs'; import { S as Separator } from './separator-DYyhdAi6.mjs'; import { C as Command, b as CommandInput, c as CommandList, d as CommandEmpty, e as CommandGroup, f as CommandItem, h as CommandSeparator } from './command-o67hOCgH.mjs'; /** * Variants for the multi-select component to handle different styles. * Uses class-variance-authority (cva) to define different styles based on "variant" prop. */ const multiSelectVariants = cva("m-1 transition ease-in-out delay-150 duration-300", { variants: { variant: { default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", positive: "border-transparent bg-positive text-positive-foreground shadow hover:bg-positive/80", warning: "border-transparent bg-warning text-warning-foreground shadow hover:bg-warning/80", info: "border-transparent bg-info text-info-foreground shadow hover:bg-info/80", outline: "text-foreground bg-transparent hover:bg-transparent border" }, subtle: { true: "", false: "" } }, defaultVariants: { variant: "default" } }); const MultiSelect = /*#__PURE__*/ React.forwardRef(({ options, onValueChange, variant, defaultValue = [], placeholder = "Selecione um item...", animation = 0, maxCount = 3, modalPopover = false, asChild = false, className, label, description, subtle = false, value: controlledValue, messageEmpty = "Nenhum resultado encontrado.", searchPlaceholder = "Buscar...", groupHeading, normalizeSearch = false, focusOnSelect = true, id, ...props }, ref)=>{ const inputRef = React.useRef(null); const generatedId = React.useId(); const inputId = id || generatedId; const [selectedValues, setSelectedValues] = React.useState(controlledValue || defaultValue); React.useEffect(()=>{ if (controlledValue) { setSelectedValues(controlledValue); } }, [ controlledValue ]); const [isPopoverOpen, setIsPopoverOpen] = React.useState(false); const [isAnimating, setIsAnimating] = React.useState(false); const [isShiftOn, setIsShiftOn] = React.useState(false); const [inputValue, setInputValue] = React.useState(""); const normalize = React.useCallback((text)=>normalizeSearch ? removeSpecialCharacters(text).toLowerCase() : text.toLowerCase(), [ normalizeSearch ]); const filteredOptions = React.useMemo(()=>{ const normalizedSearch = normalize(inputValue); return options.filter((option)=>normalize(option.label).includes(normalizedSearch)); }, [ options, inputValue, normalize ]); const handleInputKeyDown = (event)=>{ if (event.key === "Enter") { setIsPopoverOpen(true); } }; const toggleOption = (option)=>{ const newSelectedValues = selectedValues.includes(option) ? selectedValues.filter((value)=>value !== option) : [ ...selectedValues, option ]; setSelectedValues(newSelectedValues); onValueChange(newSelectedValues); }; const toggleInterval = (index)=>{ if (selectedValues.length < 1) return; const lastIndex = options.findIndex((o)=>o.value === selectedValues.at(-1)); const start = Math.min(lastIndex, index); const end = Math.max(lastIndex, index) + 1; const rangeValues = options.slice(start, end).map((o)=>o.value); const areAllSelected = rangeValues.every((v)=>selectedValues.includes(v)); const newSelectedValues = areAllSelected ? selectedValues.filter((v)=>!rangeValues.includes(v)) : [ ...new Set([ ...selectedValues, ...rangeValues ]) ]; setSelectedValues(newSelectedValues); onValueChange(newSelectedValues); }; const handleClear = ()=>{ setSelectedValues([]); onValueChange([]); }; const handleTogglePopover = ()=>{ setIsPopoverOpen((prev)=>!prev); }; const clearExtraOptions = ()=>{ const newSelectedValues = selectedValues.slice(0, maxCount); setSelectedValues(newSelectedValues); onValueChange(newSelectedValues); }; const toggleAll = ()=>{ if (selectedValues.length === options.length) { handleClear(); } else { const allValues = options.map((option)=>option.value); setSelectedValues(allValues); onValueChange(allValues); } }; const subtleStyles = (variant)=>{ if (variant === "default") { return "border-transparent bg-accent text-primary hover:bg-accent/80"; } if (variant === "destructive") { return "border-transparent bg-destructive-foreground text-destructive hover:bg-destructive-foreground/80"; } if (variant === "positive") { return "border-transparent bg-positive-foreground text-positive hover:bg-positive-foreground/80"; } if (variant === "warning") { return "border-transparent bg-warning-foreground text-warning hover:bg-warning-foreground/80"; } if (variant === "info") { return "border-transparent bg-info-foreground text-info hover:bg-info-foreground/80"; } return ""; }; const finalSubtleStyles = subtleStyles(variant); return /*#__PURE__*/ React.createElement("div", { className: "font-lato space-y-2" }, label && /*#__PURE__*/ React.createElement(Label, { htmlFor: inputId, className: "text-foreground text-sm font-medium gap-0" }, label), /*#__PURE__*/ React.createElement(Popover, { open: isPopoverOpen, onOpenChange: setIsPopoverOpen, modal: modalPopover }, /*#__PURE__*/ React.createElement(PopoverTrigger, { asChild: true }, /*#__PURE__*/ React.createElement(Button, { id: inputId, ref: ref, ...props, onClick: handleTogglePopover, variant: "outline", "aria-expanded": isPopoverOpen, className: cn("flex h-auto min-h-9 w-full items-center justify-between rounded-md border bg-inherit px-3 py-2 hover:bg-inherit [&_svg]:pointer-events-auto", className) }, selectedValues.length > 0 ? /*#__PURE__*/ React.createElement("div", { className: "flex w-full items-center justify-between" }, /*#__PURE__*/ React.createElement("div", { className: "flex flex-wrap items-center" }, selectedValues.slice(0, maxCount).map((value)=>{ const option = options.find((o)=>o.value === value); const IconComponent = option?.icon; return /*#__PURE__*/ React.createElement(Badge, { key: value, className: cn("px-1", isAnimating ? "animate-bounce" : "", multiSelectVariants({ variant }), subtle && finalSubtleStyles), style: { animationDuration: `${animation}s` } }, IconComponent && /*#__PURE__*/ React.createElement(IconComponent, { className: "mr-2 h-4 w-4" }), option?.label, /*#__PURE__*/ React.createElement("div", { onClick: (event)=>{ event.stopPropagation(); toggleOption(value); }, className: "cursor-pointer" }, /*#__PURE__*/ React.createElement(X, { className: "ml-2 size-3" }))); }), selectedValues.length > maxCount && /*#__PURE__*/ React.createElement(Badge, { className: cn("text-foreground border-foreground/1 bg-transparent px-1 hover:bg-transparent", isAnimating ? "animate-bounce" : "", multiSelectVariants({ variant }), subtle && finalSubtleStyles), style: { animationDuration: `${animation}s` } }, `+ ${selectedValues.length - maxCount} itens`, /*#__PURE__*/ React.createElement("div", { onClick: (event)=>{ event.stopPropagation(); clearExtraOptions(); }, className: "cursor-pointer" }, /*#__PURE__*/ React.createElement(X, { className: "ml-2 size-3" })))), /*#__PURE__*/ React.createElement("div", { className: "flex items-center justify-between" }, /*#__PURE__*/ React.createElement(TooltipProvider, null, /*#__PURE__*/ React.createElement(Tooltip, null, /*#__PURE__*/ React.createElement(TooltipTrigger, null, /*#__PURE__*/ React.createElement("div", { onClick: (event)=>{ event.stopPropagation(); handleClear(); }, className: "cursor-pointer" }, /*#__PURE__*/ React.createElement(X, { className: "text-muted-foreground h-4" }))), /*#__PURE__*/ React.createElement(TooltipContent, null, /*#__PURE__*/ React.createElement("span", { className: "text-xs" }, "Limpar tudo")))), /*#__PURE__*/ React.createElement(Separator, { orientation: "vertical", className: "flex h-full min-h-6" }), /*#__PURE__*/ React.createElement(ChevronsUpDown, { className: "text-muted-foreground h-4 cursor-pointer" }))) : /*#__PURE__*/ React.createElement("div", { className: "flex w-full items-center justify-between" }, /*#__PURE__*/ React.createElement("span", { className: "text-muted-foreground" }, placeholder), /*#__PURE__*/ React.createElement(ChevronsUpDown, { className: "text-muted-foreground h-4 cursor-pointer" })))), /*#__PURE__*/ React.createElement(PopoverContent, { className: "w-auto p-0", align: "start", onEscapeKeyDown: ()=>setIsPopoverOpen(false), onKeyDown: (event)=>{ event.key === "Shift" ? setIsShiftOn(true) : null; }, onKeyUp: (event)=>{ event.key === "Shift" ? setIsShiftOn(false) : null; } }, /*#__PURE__*/ React.createElement(Command, { shouldFilter: false }, /*#__PURE__*/ React.createElement(CommandInput, { ref: inputRef, placeholder: searchPlaceholder, value: inputValue, onValueChange: setInputValue, onKeyDown: handleInputKeyDown }), /*#__PURE__*/ React.createElement(CommandList, null, filteredOptions.length === 0 ? /*#__PURE__*/ React.createElement(CommandEmpty, null, messageEmpty) : /*#__PURE__*/ React.createElement(CommandGroup, { heading: groupHeading }, /*#__PURE__*/ React.createElement(CommandItem, { key: "all", onSelect: ()=>{ toggleAll(); if (focusOnSelect) { inputRef.current?.focus(); } }, className: "flex cursor-pointer items-center justify-between" }, /*#__PURE__*/ React.createElement("span", null, "(Selecionar tudo)"), /*#__PURE__*/ React.createElement("div", { className: cn(selectedValues.length === options.length ? "" : "opacity-50 [&_svg]:invisible") }, /*#__PURE__*/ React.createElement(Check, { className: "text-foreground h-4 w-4" }))), filteredOptions.map((option)=>{ const isSelected = selectedValues.includes(option.value); return /*#__PURE__*/ React.createElement(CommandItem, { key: option.value, onSelect: ()=>{ if (isShiftOn) { const originalIndex = options.findIndex((o)=>o.value === option.value); toggleInterval(originalIndex); } else { toggleOption(option.value); } if (focusOnSelect) { inputRef.current?.focus(); } }, className: "flex cursor-pointer items-center justify-between" }, /*#__PURE__*/ React.createElement("div", { className: "flex items-center" }, option.icon && /*#__PURE__*/ React.createElement(option.icon, { className: "text-muted-foreground mr-2 h-4 w-4" }), /*#__PURE__*/ React.createElement("span", null, option.label)), /*#__PURE__*/ React.createElement("div", { className: cn(isSelected ? "" : "opacity-50 [&_svg]:invisible") }, /*#__PURE__*/ React.createElement(Check, { className: "text-foreground h-4 w-4" }))); })), /*#__PURE__*/ React.createElement(CommandSeparator, null)))), animation > 0 && selectedValues.length > 0 && /*#__PURE__*/ React.createElement(WandSparkles, { className: cn("text-foreground bg-background my-2 h-3 w-3 cursor-pointer", isAnimating ? "" : "text-muted-foreground"), onClick: ()=>setIsAnimating(!isAnimating) })), description && /*#__PURE__*/ React.createElement("p", { className: "text-muted-foreground text-sm" }, description)); }); MultiSelect.displayName = "MultiSelect"; export { MultiSelect as M }; //# sourceMappingURL=multi-select-ILUAJeKL.mjs.map