import { CodeRounded, ViewModuleRounded } from '@mui/icons-material' import { Box, Button, Chip, FormHelperText, IconButton, TextField, Tooltip, Typography, } from '@mui/material' import type { ReactNode } from 'react' import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' type BaseSplitChipEditorMode = 'visual' | 'advanced' interface BaseSplitChipEditorProps { value?: string onChange: (value: string) => void disabled?: boolean error?: boolean helperText?: ReactNode placeholder?: string separator?: string ariaLabel?: string renderHeader?: (modeToggle: ReactNode) => ReactNode } const DEFAULT_SPLIT_PATTERN = /[,\n;\r]+/ const splitValue = (value: string) => value .split(DEFAULT_SPLIT_PATTERN) .map((item) => item.trim()) .filter(Boolean) export const BaseSplitChipEditor = ({ value = '', onChange, disabled = false, error = false, helperText, placeholder, separator = ',', ariaLabel, renderHeader, }: BaseSplitChipEditorProps) => { const { t } = useTranslation() const [mode, setMode] = useState('visual') const [draft, setDraft] = useState('') const resolvedLabels = useMemo( () => ({ visual: t('shared.editorModes.visualization'), advanced: t('shared.editorModes.advanced'), add: t('shared.actions.new'), empty: t('shared.statuses.empty'), }), [t], ) const values = useMemo(() => splitValue(value), [value]) const items = useMemo(() => { const counts = new Map() return values.map((item) => { const nextCount = (counts.get(item) ?? 0) + 1 counts.set(item, nextCount) return { key: `${item}-${nextCount}`, value: item, } }) }, [values]) const handleAddDraft = () => { const nextValues = splitValue(draft) if (!nextValues.length) { return } const nextValue = [...values, ...nextValues].join(separator) onChange(nextValue) setDraft('') } const handleRemoveItem = (index: number) => { const nextValue = values.filter((_, itemIndex) => itemIndex !== index) onChange(nextValue.join(separator)) } const nextMode = mode === 'visual' ? 'advanced' : 'visual' const toggleLabel = nextMode === 'visual' ? resolvedLabels.visual : resolvedLabels.advanced const ToggleIcon = nextMode === 'visual' ? ViewModuleRounded : CodeRounded const resolvedAriaLabel = ariaLabel ?? (typeof toggleLabel === 'string' ? toggleLabel : undefined) const modeToggle = ( { setMode(nextMode) if (nextMode === 'visual') { setDraft('') } }} > ) return ( <> {renderHeader ? renderHeader(modeToggle) : modeToggle} {mode === 'visual' ? ( {items.length ? ( items.map((item, index) => ( handleRemoveItem(index) } /> )) ) : ( {resolvedLabels.empty} )} setDraft(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault() handleAddDraft() } }} /> {helperText && ( {helperText} )} ) : ( { onChange(event.target.value) }} /> )} ) }