turash/bugulma/frontend/components/ui/MultiSelect.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

83 lines
2.5 KiB
TypeScript

import { useMultiSelect } from '@/hooks/ui/useMultiSelect.ts';
import { useTranslation } from '@/hooks/useI18n.tsx';
import { ChevronDown } from 'lucide-react';
import { Card } from '@/components/ui/Card.tsx';
import Checkbox from '@/components/ui/Checkbox.tsx';
import Input from '@/components/ui/Input.tsx';
interface MultiSelectProps {
options: readonly string[];
selected: string[];
onChange: (selected: string[]) => void;
placeholder: string;
className?: string;
}
const MultiSelect = ({
options,
selected,
onChange,
placeholder,
className = '',
}: MultiSelectProps) => {
const { t } = useTranslation();
const {
isOpen,
setIsOpen,
searchTerm,
setSearchTerm,
wrapperRef,
toggleOption,
filteredOptions,
handleKeyDown,
} = useMultiSelect(options as string[], selected, onChange);
return (
<div ref={wrapperRef} className={`relative ${className}`} onKeyDown={handleKeyDown}>
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className="flex h-10 w-full items-center justify-between rounded-md border bg-background px-3 py-2 text-sm text-left"
aria-haspopup="listbox"
aria-expanded={isOpen}
>
<span className={selected.length > 0 ? 'text-foreground' : 'text-muted-foreground'}>
{selected.length > 0
? t('multiSelect.selected', { count: selected.length })
: placeholder}
</span>
<ChevronDown className={`h-4 w-4 transition-transform ${isOpen ? 'rotate-180' : ''}`} />
</button>
{isOpen && (
<Card className="absolute z-10 mt-1 w-full max-h-60 overflow-y-auto">
<div className="p-2">
<Input
type="search"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="h-9"
/>
</div>
<ul className="p-1" role="listbox">
{filteredOptions.map((option) => (
<li key={option}>
<label className="flex items-center gap-2 p-2 rounded-md hover:bg-muted cursor-pointer text-sm">
<Checkbox
checked={selected.includes(option)}
onChange={() => toggleOption(option)}
/>
{option}
</label>
</li>
))}
</ul>
</Card>
)}
</div>
);
};
export default MultiSelect;