Files
frp-panel/www/components/base/list-input.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

70 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { Badge } from '../ui/badge';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
import { useTranslation } from 'react-i18next';
interface StringListInputProps {
value: string[];
onChange: React.Dispatch<React.SetStateAction<string[]>>;
placeholder?: string;
}
const StringListInput: React.FC<StringListInputProps> = ({ value, onChange, placeholder }) => {
const { t } = useTranslation();
const [inputValue, setInputValue] = useState('');
const handleAdd = () => {
if (inputValue.trim()) {
if (value && value.includes(inputValue)) {
return;
}
if (value) {
onChange([...value, inputValue]);
} else {
onChange([inputValue]);
}
setInputValue('');
}
};
const handleRemove = (itemToRemove: string) => {
onChange(value.filter(item => item !== itemToRemove));
};
return (
<div className="mx-auto">
<div className="flex items-center mb-4">
<Input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
className="flex-1 px-4 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
placeholder={placeholder || t('input.list.placeholder')}
/>
<Button
disabled={!inputValue || value && value.includes(inputValue)}
onClick={handleAdd}
className="ml-2 px-4 py-2"
>
{t('input.list.add')}
</Button>
</div>
<div className="flex flex-wrap gap-2">
{value && value.map((item, index) => (
<Badge key={index} className='flex flex-row items-center justify-start'>{item}
<div
onClick={() => handleRemove(item)}
className="ml-1 h-4 w-4 text-center rounded-full hover:text-red-500 cursor-pointer"
>
×
</div>
</Badge>
))}
</div>
</div>
);
};
export default StringListInput;