UNPKG

@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
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;