Uecko_ERP/modules/customer-invoices/src/web/hooks/use-items-table-navigation.ts

132 lines
3.5 KiB
TypeScript
Raw Normal View History

2025-10-07 16:38:03 +00:00
import * as React from "react";
2025-10-13 17:41:21 +00:00
import { FieldValues, Path, UseFormReturn, useFieldArray } from "react-hook-form";
2025-10-07 16:38:03 +00:00
2025-10-13 17:41:21 +00:00
interface UseItemsTableNavigationOptions<TFieldValues extends FieldValues> {
/** Nombre del array de líneas en el formulario (tipo-safe) */
name: Path<TFieldValues>;
/** Creador de una línea vacía */
createEmpty: () => unknown; // ajusta el tipo del item si lo conoces
/** Primer campo editable de la fila */
2025-10-12 18:36:33 +00:00
firstEditableField?: string;
}
2025-10-07 16:38:03 +00:00
2025-10-13 17:41:21 +00:00
export function useItemsTableNavigation<TFieldValues extends FieldValues = FieldValues>(
form: UseFormReturn<TFieldValues>,
{
name,
createEmpty,
firstEditableField = "description",
}: UseItemsTableNavigationOptions<TFieldValues>
2025-10-07 16:38:03 +00:00
) {
const { control, getValues, setFocus } = form;
2025-10-13 17:41:21 +00:00
const fa = useFieldArray<TFieldValues>({ control, name });
2025-10-07 16:38:03 +00:00
2025-10-12 18:36:33 +00:00
// Desestructurar para evitar recreaciones
const { append, insert, remove: faRemove, move } = fa;
// Ref estable para getValues
const getValuesRef = React.useRef(getValues);
getValuesRef.current = getValues;
2025-10-07 16:38:03 +00:00
const length = React.useCallback(() => {
2025-10-12 18:36:33 +00:00
const arr = getValuesRef.current(name) as unknown[];
2025-10-07 16:38:03 +00:00
return Array.isArray(arr) ? arr.length : 0;
2025-10-12 18:36:33 +00:00
}, [name]);
2025-10-07 16:38:03 +00:00
const focusRowFirstField = React.useCallback(
(rowIndex: number) => {
queueMicrotask(() => {
2025-10-12 18:36:33 +00:00
try {
setFocus(`${name}.${rowIndex}.${firstEditableField}` as any, {
shouldSelect: true,
});
} catch {
// el campo aún no está montado
}
2025-10-07 16:38:03 +00:00
});
},
[name, firstEditableField, setFocus]
);
const addEmpty = React.useCallback(
(atEnd = true, index?: number, initial?: Record<string, unknown>) => {
const row = { ...createEmpty(), ...(initial ?? {}) };
2025-10-12 18:36:33 +00:00
if (!atEnd && typeof index === "number") insert(index, row);
else append(row);
2025-10-07 16:38:03 +00:00
},
2025-10-12 18:36:33 +00:00
[append, insert, createEmpty]
2025-10-07 16:38:03 +00:00
);
const duplicate = React.useCallback(
(i: number) => {
2025-10-12 18:36:33 +00:00
const curr = getValuesRef.current(`${name}.${i}`) as Record<string, unknown> | undefined;
2025-10-07 16:38:03 +00:00
if (!curr) return;
const clone =
typeof structuredClone === "function"
? structuredClone(curr)
: JSON.parse(JSON.stringify(curr));
2025-10-12 18:36:33 +00:00
const { id: _id, ...sanitized } = clone;
insert(i + 1, sanitized);
2025-10-07 16:38:03 +00:00
},
2025-10-12 18:36:33 +00:00
[insert, name]
2025-10-07 16:38:03 +00:00
);
const remove = React.useCallback(
(i: number) => {
if (i < 0 || i >= length()) return;
2025-10-12 18:36:33 +00:00
faRemove(i);
2025-10-07 16:38:03 +00:00
},
2025-10-12 18:36:33 +00:00
[faRemove, length]
2025-10-07 16:38:03 +00:00
);
const moveUp = React.useCallback(
(i: number) => {
if (i <= 0) return;
2025-10-12 18:36:33 +00:00
move(i, i - 1);
2025-10-07 16:38:03 +00:00
},
2025-10-12 18:36:33 +00:00
[move]
2025-10-07 16:38:03 +00:00
);
const moveDown = React.useCallback(
(i: number) => {
const len = length();
if (i < 0 || i >= len - 1) return;
2025-10-12 18:36:33 +00:00
move(i, i + 1);
2025-10-07 16:38:03 +00:00
},
2025-10-12 18:36:33 +00:00
[move, length]
2025-10-07 16:38:03 +00:00
);
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 {
2025-10-12 18:36:33 +00:00
fieldArray: fa, // { fields, append, remove, insert, move, ... }
2025-10-07 16:38:03 +00:00
addEmpty,
duplicate,
remove,
moveUp,
moveDown,
onTabFromLastCell,
onShiftTabFromFirstCell,
focusRowFirstField,
};
}