@pagamio/frontend-commons-lib
Version:
Pagamio library for Frontend reusable components like the form engine and table container
60 lines (59 loc) • 5.52 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import * as Checkbox from '@radix-ui/react-checkbox';
import { CheckIcon, ChevronDownIcon, Cross2Icon } from '@radix-ui/react-icons';
import * as Popover from '@radix-ui/react-popover';
import { forwardRef, useEffect, useState } from 'react';
import { cn } from '../../helpers';
const TagsList = ({ selectedOptions, placeholder, removeTag }) => {
if (selectedOptions.length === 0) {
return _jsx("span", { className: "truncate", children: placeholder ?? 'Select options' });
}
return (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedOptions.map((option) => (_jsxs("div", { className: "flex items-center gap-1 px-2 py-1 text-sm bg-gray-100 rounded-md", children: [option.label, _jsx("button", { type: "button", onClick: (e) => removeTag(option.value, e), className: "p-0.5 hover:bg-gray-200 rounded-full", children: _jsx(Cross2Icon, { className: "w-3 h-3" }) })] }, option.value))) }));
};
const MultiSelect = forwardRef(({ options, value, defaultValue = [], disabled, onChange, placeholder, className, onSearch, field, tagPosition = 'inside', }, ref) => {
const [selectedValues, setSelectedValues] = useState(defaultValue);
const [searchTerm, setSearchTerm] = useState('');
const [filteredOptions, setFilteredOptions] = useState(options);
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
if (onSearch) {
onSearch(searchTerm);
}
else {
setFilteredOptions(options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase())));
}
}, [searchTerm, options, onSearch]);
const internalValues = value || selectedValues;
const handleToggle = (option) => {
const updatedValues = internalValues.includes(option)
? internalValues.filter((val) => val !== option)
: [...internalValues, option];
if (!value) {
setSelectedValues(updatedValues);
}
onChange?.(updatedValues);
};
const removeTag = (optionValue, e) => {
e.stopPropagation();
handleToggle(optionValue);
};
const selectedOptions = options.filter((option) => internalValues.includes(option.value));
return (_jsx("div", { ref: ref, className: cn('relative w-full', className), children: _jsxs(Popover.Root, { open: isOpen, onOpenChange: setIsOpen, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: disabled, className: "w-full cursor-pointer disabled:text-gray-400 disabled:bg-gray-50 disabled:cursor-not-allowed", onClick: (e) => {
e.preventDefault();
setIsOpen(!isOpen);
}, onKeyDown: (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(!isOpen);
}
}, children: [_jsxs("div", { id: field?.name, className: "flex items-center w-full px-4 py-2 border rounded-md", style: { minHeight: '41px' }, children: [_jsx("div", { className: "flex-1 text-left", children: tagPosition === 'inside' ? (_jsx(TagsList, { selectedOptions: selectedOptions, removeTag: removeTag, placeholder: field?.placeholder })) : (_jsx("span", { className: "truncate", children: selectedOptions.length === 0
? (field?.placeholder ?? placeholder ?? 'Select options')
: `${selectedOptions.length} selected` })) }), _jsx(ChevronDownIcon, { className: "ml-2 flex-shrink-0" })] }), tagPosition === 'bottom' && selectedOptions.length > 0 && (_jsx("div", { className: "mt-2", children: _jsx(TagsList, { selectedOptions: selectedOptions, removeTag: removeTag, placeholder: field?.placeholder }) }))] }) }), _jsxs(Popover.Content, { className: "p-2 bg-white border rounded shadow-md w-full z-50", align: "start", sideOffset: 5, onOpenAutoFocus: (e) => e.preventDefault(), children: [_jsx("input", { type: "text", placeholder: "Search...", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), className: "w-full px-2 py-1 mb-2 border rounded", onClick: (e) => e.stopPropagation() }), _jsx("div", { className: "max-h-60 overflow-auto", children: filteredOptions.length > 0 ? (filteredOptions.map((option) => (_jsxs("div", { className: "flex items-center gap-2 px-2 py-1 cursor-pointer hover:bg-gray-100", children: [_jsx(Checkbox.Root, { type: "button", checked: internalValues.includes(option.value), onCheckedChange: () => handleToggle(option.value), className: "w-4 h-4 border rounded", children: _jsx(Checkbox.Indicator, { children: _jsx(CheckIcon, { className: "w-4 h-4 text-blue-500" }) }) }), _jsx("button", { type: "button", onClick: () => handleToggle(option.value), onKeyDown: (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleToggle(option.value);
}
}, className: "text-left w-full", children: option.label })] }, option.value)))) : (_jsx("div", { className: "px-2 py-1 text-gray-500", children: "No options found" })) })] })] }) }));
});
MultiSelect.displayName = 'MultiSelect';
export default MultiSelect;