fix: improve expertise input handling to prevent character loss during comma-separated editing
This commit is contained in:
@@ -143,6 +143,48 @@ function emptyStatItem(): StatItem {
|
|||||||
const inputCls = 'w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]';
|
const inputCls = 'w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]';
|
||||||
const textareaCls = inputCls;
|
const textareaCls = inputCls;
|
||||||
|
|
||||||
|
// Expertise input keeps its own raw-text state so the user can type commas
|
||||||
|
// without the display being destructively re-derived from the parsed array
|
||||||
|
// (which was dropping trailing commas mid-typing and merging new tokens into
|
||||||
|
// the previous one).
|
||||||
|
function ExpertiseInput({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value: string[];
|
||||||
|
onChange: (next: string[]) => void;
|
||||||
|
}) {
|
||||||
|
const [raw, setRaw] = useState<string>(() => value.join(', '));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentParsed = raw.split(',').map((s) => s.trim()).filter(Boolean);
|
||||||
|
const matches =
|
||||||
|
currentParsed.length === value.length &&
|
||||||
|
currentParsed.every((v, i) => v === value[i]);
|
||||||
|
if (!matches) setRaw(value.join(', '));
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={inputCls}
|
||||||
|
value={raw}
|
||||||
|
onChange={(e) => {
|
||||||
|
const next = e.target.value;
|
||||||
|
setRaw(next);
|
||||||
|
onChange(next.split(',').map((s) => s.trim()).filter(Boolean));
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
const parsed = raw.split(',').map((s) => s.trim()).filter(Boolean);
|
||||||
|
setRaw(parsed.join(', '));
|
||||||
|
onChange(parsed);
|
||||||
|
}}
|
||||||
|
placeholder="Expertise (comma-separated)"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Page component
|
// Page component
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -828,7 +870,7 @@ export default function CmsPage() {
|
|||||||
<input type="text" className={inputCls} value={item.location} onChange={(e) => updateItem(idx, { location: e.target.value })} placeholder="Location" />
|
<input type="text" className={inputCls} value={item.location} onChange={(e) => updateItem(idx, { location: e.target.value })} placeholder="Location" />
|
||||||
<input type="text" className={inputCls} value={item.experience} onChange={(e) => updateItem(idx, { experience: e.target.value })} placeholder="Experience" />
|
<input type="text" className={inputCls} value={item.experience} onChange={(e) => updateItem(idx, { experience: e.target.value })} placeholder="Experience" />
|
||||||
</div>
|
</div>
|
||||||
<input type="text" className={inputCls} value={item.expertise.join(', ')} onChange={(e) => updateItem(idx, { expertise: e.target.value.split(',').map((s) => s.trim()).filter(Boolean) })} placeholder="Expertise (comma-separated)" />
|
<ExpertiseInput value={item.expertise} onChange={(next) => updateItem(idx, { expertise: next })} />
|
||||||
<input type="text" className={inputCls} value={item.imageUrl} onChange={(e) => updateItem(idx, { imageUrl: e.target.value })} placeholder="Image URL" />
|
<input type="text" className={inputCls} value={item.imageUrl} onChange={(e) => updateItem(idx, { imageUrl: e.target.value })} placeholder="Image URL" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user