92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
|
|
import { Input } from '../input';
|
||
|
|
import { SelectOptionItem } from './SelectOptionItem';
|
||
|
|
import type { GroupedOptions } from './types';
|
||
|
|
|
||
|
|
interface SelectDropdownContentProps {
|
||
|
|
searchable: boolean;
|
||
|
|
search: string;
|
||
|
|
onSearchChange: (v: string) => void;
|
||
|
|
searchInputRef: React.RefObject<HTMLInputElement | null>;
|
||
|
|
filteredOptions: GroupedOptions;
|
||
|
|
multiple: boolean;
|
||
|
|
isSelected: (value: string) => boolean;
|
||
|
|
onSelect: (value: string) => void;
|
||
|
|
ariaLabel?: string;
|
||
|
|
name?: string;
|
||
|
|
placeholder?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function SelectDropdownContent({
|
||
|
|
searchable,
|
||
|
|
search,
|
||
|
|
onSearchChange,
|
||
|
|
searchInputRef,
|
||
|
|
filteredOptions,
|
||
|
|
multiple,
|
||
|
|
isSelected,
|
||
|
|
onSelect,
|
||
|
|
ariaLabel,
|
||
|
|
name,
|
||
|
|
placeholder,
|
||
|
|
}: SelectDropdownContentProps) {
|
||
|
|
const hasOptions =
|
||
|
|
filteredOptions.ungrouped.length > 0 ||
|
||
|
|
Object.keys(filteredOptions.groups).length > 0;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className="w-full min-w-48 max-h-72 overflow-y-auto"
|
||
|
|
role="listbox"
|
||
|
|
aria-label={ariaLabel || name || placeholder}
|
||
|
|
>
|
||
|
|
{searchable && (
|
||
|
|
<div className="p-2 border-b">
|
||
|
|
<Input
|
||
|
|
ref={searchInputRef}
|
||
|
|
type="text"
|
||
|
|
placeholder="Search..."
|
||
|
|
value={search}
|
||
|
|
onChange={(e) => onSearchChange(e.target.value)}
|
||
|
|
onClick={(e) => e.stopPropagation()}
|
||
|
|
className="w-full"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
{filteredOptions.ungrouped.length > 0 && (
|
||
|
|
<div className="py-1">
|
||
|
|
{filteredOptions.ungrouped.map((option) => (
|
||
|
|
<SelectOptionItem
|
||
|
|
key={option.value}
|
||
|
|
option={option}
|
||
|
|
isSelected={isSelected(option.value)}
|
||
|
|
multiple={multiple}
|
||
|
|
onSelect={onSelect}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
{Object.entries(filteredOptions.groups).map(([groupLabel, groupOptions]) => (
|
||
|
|
<div key={groupLabel} className="py-1">
|
||
|
|
<div className="px-4 py-1.5 text-xs font-semibold text-kodo-content-dim uppercase">
|
||
|
|
{groupLabel}
|
||
|
|
</div>
|
||
|
|
{groupOptions.map((option) => (
|
||
|
|
<SelectOptionItem
|
||
|
|
key={option.value}
|
||
|
|
option={option}
|
||
|
|
isSelected={isSelected(option.value)}
|
||
|
|
multiple={multiple}
|
||
|
|
onSelect={onSelect}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
{!hasOptions && (
|
||
|
|
<div className="px-4 py-2 text-sm text-kodo-content-dim text-center">
|
||
|
|
No options found
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|