Files
portfolio/src/components/admin/fields/ChipsField.tsx
Johan LEROY 5d05cd4c04 prod setup
2026-05-04 11:00:44 +02:00

96 lines
3.2 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 { useState, type KeyboardEvent } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { FieldError } from './FieldError';
type Props = {
name: string;
label: string;
hint?: string;
placeholder?: string;
};
export function ChipsField({ name, label, hint, placeholder = 'Ajouter puis Entrée…' }: Props) {
const { control } = useFormContext();
const [draft, setDraft] = useState('');
return (
<Controller
name={name}
control={control}
render={({ field }) => {
const items: string[] = Array.isArray(field.value) ? field.value : [];
const add = () => {
const value = draft.trim();
if (!value) return;
if (items.includes(value)) {
setDraft('');
return;
}
field.onChange([...items, value]);
setDraft('');
};
const remove = (idx: number) => {
field.onChange(items.filter((_, i) => i !== idx));
};
const onKey = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
add();
} else if (e.key === 'Backspace' && draft === '' && items.length > 0) {
field.onChange(items.slice(0, -1));
}
};
return (
<div>
<span className="block font-mono text-xs uppercase tracking-widest text-[color:var(--color-cyan)] mb-1.5">
{label}
</span>
{hint && (
<span className="block font-mono text-[10px] text-[color:var(--color-text-dim)] mb-1.5">
{hint}
</span>
)}
<div className="flex flex-wrap gap-2 mb-2">
{items.map((item, idx) => (
<span
key={`${item}-${idx}`}
className="chip group cursor-pointer"
onClick={() => remove(idx)}
title="Cliquer pour retirer"
>
{item}
<span className="ml-1 opacity-50 group-hover:opacity-100 group-hover:text-[color:var(--color-magenta)]">
×
</span>
</span>
))}
</div>
<div className="flex gap-2">
<input
type="text"
value={draft}
onChange={(e) => setDraft(e.target.value)}
onKeyDown={onKey}
placeholder={placeholder}
className="flex-1 bg-[color:var(--color-surface-2)] border border-[color:var(--color-border)] focus:border-[color:var(--color-cyan)] focus:outline-none p-2 rounded text-sm font-mono text-[color:var(--color-text)] transition-colors"
/>
<button
type="button"
onClick={add}
className="px-3 py-1.5 font-mono text-xs uppercase tracking-widest border border-[color:var(--color-cyan)] text-[color:var(--color-cyan)] rounded hover:bg-[color:var(--color-cyan)] hover:text-[color:var(--color-bg)] transition-colors"
>
+ add
</button>
</div>
<FieldError name={name} />
</div>
);
}}
/>
);
}