Files
protoc-frontend/src/component/BasicElements/SelectElement.tsx

204 lines
6.6 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 { Button } from '@/component/ui/button'
import { Input } from '@/component/ui/input'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/component/ui/select'
import { ElementDefinition } from '@/entitiy/element/model/interface'
import { useToast } from '@/lib/hooks/useToast'
import { ElementOption } from '@/type/template'
import { ChevronDown, Plus, Trash2 } from 'lucide-react'
interface SelectConfig {
placeholder?: string
required?: boolean
options: ElementOption[]
targetCells: any[]
}
export const selectDefinition: ElementDefinition<SelectConfig, string> = {
type: 'select',
label: 'Выпадающий список',
icon: <ChevronDown className="h-4 w-4" />,
version: 1,
defaultConfig: {
placeholder: '',
required: false,
options: [],
targetCells: [],
},
mapToCells: config => config.targetCells || [],
mapToCellValues: (config, value) => {
const cells = config.targetCells || []
return cells.map(target => ({ target, value: value || '' }))
},
Editor: ({ config, onChange }) => {
const { warning } = useToast()
const handleAddOption = () => {
onChange({
...config,
options: [...config.options, { value: '', label: '' }],
})
}
const handleUpdateOption = (
index: number,
field: 'value' | 'label',
value: string
) => {
// Проверяем дублирование только для подписей (label)
if (field === 'label' && value.trim() !== '') {
const isDuplicate = config.options.some(
(option, i) =>
i !== index &&
option.label.trim().toLowerCase() === value.trim().toLowerCase()
)
if (isDuplicate) {
warning(`Такая подпись уже существует: "${value.trim()}"`)
return
}
}
onChange({
...config,
options: config.options.map((option, i) =>
i === index ? { ...option, [field]: value } : option
),
})
}
const handleRemoveOption = (index: number) => {
onChange({
...config,
options: config.options.filter((_, i) => i !== index),
})
}
return (
<div className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<label className="text-sm font-medium">Варианты</label>
<Button variant="outline" size="sm" onClick={handleAddOption}>
<Plus className="mr-1 h-4 w-4" />
Добавить
</Button>
</div>
<div className="max-h-32 space-y-2 overflow-y-auto">
{config.options.map((option, index) => {
const isDuplicateLabel = config.options.some(
(otherOption, i) =>
i !== index &&
option.label.trim() !== '' &&
otherOption.label.trim().toLowerCase() ===
option.label.trim().toLowerCase()
)
// Убираем проверку дублирования для значений
const isDuplicateValue = false
return (
<div key={index} className="flex gap-2">
<div className="flex-1">
<Input
placeholder="Значение"
value={option.value}
onChange={e =>
handleUpdateOption(index, 'value', e.target.value)
}
className={isDuplicateValue ? 'border-red-500' : ''}
/>
{isDuplicateValue && (
<p className="mt-1 text-xs text-red-500">
Это значение уже используется
</p>
)}
</div>
<div className="flex-1">
<Input
placeholder="Подпись"
value={option.label}
onChange={e =>
handleUpdateOption(index, 'label', e.target.value)
}
className={isDuplicateLabel ? 'border-red-500' : ''}
/>
{isDuplicateLabel && (
<p className="mt-1 text-xs text-red-500">
Такая подпись уже существует
</p>
)}
</div>
<Button
variant="outline"
size="icon"
onClick={() => handleRemoveOption(index)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
)
})}
</div>
</div>
</div>
)
},
Preview: ({ config }) => (
<Select>
<SelectTrigger className="w-full min-w-32">
<SelectValue placeholder={config.placeholder || 'Выберите значение'} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{config.options
.filter(option => option.value.trim() !== '')
.map((option, index) => (
<SelectItem key={index} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
),
Render: ({ config, value, onChange }) => {
const elementId = (config as any).id || config.placeholder || 'default'
const storageKey = `select_${elementId}`
const handleValueChange = (newValue: string) => {
localStorage.setItem(storageKey, newValue)
onChange?.(newValue)
}
// Если значение не установлено, пытаемся загрузить из localStorage
const currentValue = value || localStorage.getItem(storageKey) || ''
return (
<Select value={currentValue} onValueChange={handleValueChange}>
<SelectTrigger className="w-full min-w-32">
<SelectValue
placeholder={config.placeholder || 'Выберите значение'}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
{config.options
.filter(option => option.value.trim() !== '')
.map((option, index) => (
<SelectItem key={index} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
)
},
}