ivt
Version:
Ivt Components Library
288 lines (285 loc) • 13.9 kB
JavaScript
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