import { useCallback, useRef, useState, type KeyboardEvent } from "react"; import { useFieldArray, type Control, type FieldErrors, type UseFormRegister, } from "react-hook-form"; import AddIcon from "@mui/icons-material/Add"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutlined"; import DragIndicatorIcon from "@mui/icons-material/DragIndicator"; import { Box, Button, Chip, IconButton, Paper, Stack, TextField, Tooltip, Typography, } from "@mui/material"; import type { LocationContactFormValues, LocationFormValues, } from "@/domain/locations/schemas/location-schema"; interface ContactRowProps { canRemove: boolean; dragIndex: number | null; errors: FieldErrors; fieldId: string; index: number; isLast: boolean; register: UseFormRegister; onDragEnd: () => void; onDragStart: (index: number) => void; onDrop: (index: number) => void; onRemove: (index: number) => void; onKeyboardMove: (from: number, to: number) => void; } function ContactRow({ canRemove, dragIndex, errors, fieldId, index, isLast, register, onDragEnd, onDragStart, onDrop, onRemove, onKeyboardMove, }: ContactRowProps) { const handleRef = useRef(null); const isDragging = dragIndex === index; const isDropTarget = dragIndex !== null && dragIndex !== index; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "ArrowUp" && index > 0) { event.preventDefault(); onKeyboardMove(index, index - 1); } if (event.key === "ArrowDown" && !isLast) { event.preventDefault(); onKeyboardMove(index, index + 1); } }; return ( { if (dragIndex !== null) { event.preventDefault(); } }} onDrop={(event) => { event.preventDefault(); onDrop(index); }} className="transition-opacity" sx={{ opacity: isDragging ? 0.5 : 1, borderStyle: isDropTarget ? "dashed" : "solid" }} > { event.dataTransfer.effectAllowed = "move"; event.dataTransfer.setData("text/plain", String(index)); onDragStart(index); }} onDragEnd={onDragEnd} onKeyDown={handleKeyDown} sx={{ cursor: "grab", display: "inline-flex", color: "text.secondary" }} > {index === 0 && } onRemove(index)} > ); } interface LocationContactsFieldArrayProps { control: Control; errors: FieldErrors; register: UseFormRegister; } export function LocationContactsFieldArray({ control, errors, register, }: LocationContactsFieldArrayProps) { const { fields, append, remove, move } = useFieldArray({ control, name: "contacts", keyName: "fieldKey", }); const [dragIndex, setDragIndex] = useState(null); const rootRef = useRef(null); const focusHandle = useCallback((fieldId: string) => { requestAnimationFrame(() => { const handle = rootRef.current?.querySelector(`[data-field-id="${fieldId}"]`); handle?.focus(); }); }, []); const handleKeyboardMove = useCallback( (from: number, to: number) => { if (to < 0 || to >= fields.length) { return; } const fieldId = fields[from]?.fieldKey; move(from, to); if (fieldId) { focusHandle(fieldId); } }, [fields, move, focusHandle], ); const handleDrop = useCallback( (index: number) => { if (dragIndex !== null && dragIndex !== index) { move(dragIndex, index); } setDragIndex(null); }, [dragIndex, move], ); return ( Contacts {fields.map((field, index) => ( 1} dragIndex={dragIndex} errors={errors.contacts?.[index] ?? {}} fieldId={field.fieldKey} index={index} isLast={index === fields.length - 1} register={register} onDragEnd={() => setDragIndex(null)} onDragStart={setDragIndex} onDrop={handleDrop} onRemove={remove} onKeyboardMove={handleKeyboardMove} /> ))} ); }