import * as React from "react"; import { FieldValues, UseFormReturn, useFieldArray } from "react-hook-form"; type UseItemsTableNavigationOptions = { name: string; // Ruta del array, p.ej. "items" createEmpty: () => Record; firstEditableField?: string; // Primer campo editable para enfocar al crear/ir a la fila siguiente (p.ej. "description") }; export function useItemsTableNavigation( form: UseFormReturn, { name, createEmpty, firstEditableField = "description" }: UseItemsTableNavigationOptions ) { const { control, getValues, setFocus } = form; const fa = useFieldArray({ control, name }); const length = React.useCallback(() => { const arr = getValues(name) as unknown[]; return Array.isArray(arr) ? arr.length : 0; }, [getValues, name]); const focusRowFirstField = React.useCallback( (rowIndex: number) => { queueMicrotask(() => { setFocus(`${name}.${rowIndex}.${firstEditableField}` as any, { shouldSelect: true }); }); }, [name, firstEditableField, setFocus] ); const addEmpty = React.useCallback( (atEnd = true, index?: number, initial?: Record) => { const row = { ...createEmpty(), ...(initial ?? {}) }; if (!atEnd && typeof index === "number") fa.insert(index, row); else fa.append(row); }, [fa, createEmpty] ); const duplicate = React.useCallback( (i: number) => { const curr = getValues(`${name}.${i}`) as Record | undefined; if (!curr) return; const clone = typeof structuredClone === "function" ? structuredClone(curr) : JSON.parse(JSON.stringify(curr)); // RHF aƱade un id interno en fields; por si acaso: crear un objeto sin la propiedad id const { id: _id, ...sanitized } = clone as Record; fa.insert(i + 1, sanitized); }, [fa, getValues, name] ); const remove = React.useCallback( (i: number) => { if (i < 0 || i >= length()) return; fa.remove(i); }, [fa, length] ); const moveUp = React.useCallback( (i: number) => { if (i <= 0) return; fa.move(i, i - 1); }, [fa] ); const moveDown = React.useCallback( (i: number) => { const len = length(); if (i < 0 || i >= len - 1) return; fa.move(i, i + 1); }, [fa, length] ); const onTabFromLastCell = React.useCallback( (rowIndex: number) => { const len = length(); if (rowIndex === len - 1) { addEmpty(true); focusRowFirstField(len); } else { focusRowFirstField(rowIndex + 1); } }, [length, addEmpty, focusRowFirstField] ); const onShiftTabFromFirstCell = React.useCallback( (rowIndex: number) => { if (rowIndex <= 0) return; focusRowFirstField(rowIndex - 1); }, [focusRowFirstField] ); return { fa, // { fields, append, remove, insert, move, ... } addEmpty, duplicate, remove, moveUp, moveDown, onTabFromLastCell, onShiftTabFromFirstCell, focusRowFirstField, }; }