mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-20 04:39:00 +08:00
### What problem does this PR solve? Feat: Add the SelectWithSearch component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
163
web/src/components/originui/select-with-search.tsx
Normal file
163
web/src/components/originui/select-with-search.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
'use client';
|
||||
|
||||
import { CheckIcon, ChevronDownIcon } from 'lucide-react';
|
||||
import { Fragment, useCallback, useEffect, useId, useState } from 'react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@/components/ui/command';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { RAGFlowSelectOptionType } from '../ui/select';
|
||||
|
||||
const countries = [
|
||||
{
|
||||
label: 'America',
|
||||
options: [
|
||||
{ value: 'United States', label: '🇺🇸' },
|
||||
{ value: 'Canada', label: '🇨🇦' },
|
||||
{ value: 'Mexico', label: '🇲🇽' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Africa',
|
||||
options: [
|
||||
{ value: 'South Africa', label: '🇿🇦' },
|
||||
{ value: 'Nigeria', label: '🇳🇬' },
|
||||
{ value: 'Morocco', label: '🇲🇦' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Asia',
|
||||
options: [
|
||||
{ value: 'China', label: '🇨🇳' },
|
||||
{ value: 'Japan', label: '🇯🇵' },
|
||||
{ value: 'India', label: '🇮🇳' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Europe',
|
||||
options: [
|
||||
{ value: 'United Kingdom', label: '🇬🇧' },
|
||||
{ value: 'France', label: '🇫🇷' },
|
||||
{ value: 'Germany', label: '🇩🇪' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Oceania',
|
||||
options: [
|
||||
{ value: 'Australia', label: '🇦🇺' },
|
||||
{ value: 'New Zealand', label: '🇳🇿' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export type SelectWithSearchFlagOptionType = {
|
||||
label: string;
|
||||
options: RAGFlowSelectOptionType[];
|
||||
};
|
||||
|
||||
export type SelectWithSearchFlagProps = {
|
||||
options?: SelectWithSearchFlagOptionType[];
|
||||
value?: string;
|
||||
onChange?(value: string): void;
|
||||
};
|
||||
|
||||
export function SelectWithSearch({
|
||||
value: val = '',
|
||||
onChange,
|
||||
options = countries,
|
||||
}: SelectWithSearchFlagProps) {
|
||||
const id = useId();
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [value, setValue] = useState<string>('');
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(val: string) => {
|
||||
setValue(val);
|
||||
setOpen(false);
|
||||
onChange?.(val);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(val);
|
||||
}, [val]);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
id={id}
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]"
|
||||
>
|
||||
{value ? (
|
||||
<span className="flex min-w-0 options-center gap-2">
|
||||
<span className="text-lg leading-none truncate">
|
||||
{
|
||||
options
|
||||
.map((group) =>
|
||||
group.options.find((item) => item.value === value),
|
||||
)
|
||||
.filter(Boolean)[0]?.label
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">Select value</span>
|
||||
)}
|
||||
<ChevronDownIcon
|
||||
size={16}
|
||||
className="text-muted-foreground/80 shrink-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0"
|
||||
align="start"
|
||||
>
|
||||
<Command>
|
||||
<CommandInput placeholder="Search ..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No data found.</CommandEmpty>
|
||||
{options.map((group) => (
|
||||
<Fragment key={group.label}>
|
||||
<CommandGroup heading={group.label}>
|
||||
{group.options.map((option) => (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
onSelect={handleSelect}
|
||||
>
|
||||
<span className="text-lg leading-none">
|
||||
{option.label}
|
||||
</span>
|
||||
{option.value}
|
||||
{value === option.value && (
|
||||
<CheckIcon size={16} className="ml-auto" />
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</Fragment>
|
||||
))}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user