Files
frp-panel/www/components/base/combobox.tsx
VaalaCat 373276633f feat: proxy list [新增代理列表实体]
refactor: change client manager structure [重构:更改客户端管理器结构适配影子客户端]

feat: add proxy config table and dao [添加代理配置独立数据表和DAO层]

feat: new proxy config entity [新建的代理配置实体]

feat: update and delete proxy config [更新和删除代理配置接口]

feat: get config api and beautify proxy item [更新获取配置API并美化代理项]

feat: change proxy form style [美化修改代理的表单样式]

fix: client edit [修复:编辑客户端的问题]

fix: shadow copy status error [修复:影子客户端复制状态错误]

fix: http proxy bug [修复:HTTP代理类型的错误]

fix: cannot update client [修复:无法更新客户端]

feat: record trigger refetch [自动重新获取表格内的数据]

fix: filter string length [修复:过滤字符串长度]

fix: add client error [修复:添加客户端错误]

fix: do not notify client when stopped [修复:停止时不通知客户端]

fix: delete when proxy duplicate [修复:代理重复时删除]

feat: add http proxy location [添加HTTP代理路由路径]

chore: edit style [编辑样式美化]

fix: remove expired client [修复:自动移除过期客户端]

feat: proxy status [新增代理状态提示]

fix: build [修复:构建]

fix: refetch trigger [修复:重新获取数据的问题]

fix: remove all expired client [修复:移除所有过期客户端]

feat: i18n for proxy [代理页面的国际化翻译]
2024-12-20 12:18:28 +00:00

115 lines
3.1 KiB
TypeScript

"use client"
import * as React from "react"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { useDebouncedCallback } from 'use-debounce'
import { useTranslation } from 'react-i18next'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export interface ComboboxProps {
value?: string
setValue: (value: string) => void
dataList: { value: string; label: string }[]
placeholder?: string
notFoundText?: string
onOpenChange?: () => void
className?: string
onKeyWordChange?: (keyword: string) => void
keyword?: string
isLoading?: boolean
}
export function Combobox({
value,
setValue,
dataList,
placeholder,
notFoundText,
onOpenChange,
className,
keyword,
onKeyWordChange,
isLoading
}: ComboboxProps) {
const { t } = useTranslation()
const [open, setOpen] = React.useState(false)
const debounced = useDebouncedCallback(
(v) => {
onKeyWordChange && onKeyWordChange(v as string);
},
500,
);
const defaultPlaceholder = t('selector.common.placeholder')
const defaultNotFoundText = t('selector.common.notFound')
const loadingText = t('selector.common.loading')
return (
<Popover open={open} onOpenChange={(open) => {
onOpenChange && onOpenChange()
setOpen(open)}}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className={cn("w-full justify-between font-normal", className,
!value && "text-muted-foreground"
)}
>
{value
? (dataList.find((item) => item.value === value)?.label || value)
: (placeholder || defaultPlaceholder)}
<ChevronsUpDown className="opacity-50 h-[12px] w-[12px]" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0" align="start">
<Command>
<CommandInput
value={keyword}
onValueChange={(v) => debounced(v)}
placeholder={placeholder || defaultPlaceholder}
/>
<CommandList>
<CommandEmpty>{isLoading ? loadingText : (notFoundText || defaultNotFoundText)}</CommandEmpty>
<CommandGroup>
{dataList.map((item) => (
<CommandItem
key={item.value}
value={item.value}
onSelect={(currentValue) => {
setValue(currentValue === value ? "" : currentValue)
setOpen(false)
}}
>
{item.label}
<Check
className={cn(
"ml-auto",
value === item.value ? "opacity-100" : "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}