Files
CapaKraken/apps/web/src/hooks/useRowOrder.ts
T

66 lines
2.3 KiB
TypeScript

import { useMemo, useCallback } from "react";
import type { ViewPrefsHandle } from "./useViewPrefs.js";
/**
* Manages manual drag-to-reorder row ordering for a table.
*
* - When a column sort is active (sortField !== null), row order is suppressed —
* the sorted rows are returned unchanged and dragging is disabled.
* - When no column sort is active and a saved rowOrder exists, rows are sorted
* by their position in rowOrder; rows without a saved position go to the end.
* - Calling reorder() saves the new order and clears any active column sort.
*
* Each user's rowOrder is persisted independently via useViewPrefs.
*/
export function useRowOrder<T extends { id: string }>(
rows: T[],
prefs: Pick<ViewPrefsHandle, "rowOrder" | "setRowOrder">,
activeSortField: string | null,
resetSort: () => void,
) {
const { rowOrder, setRowOrder } = prefs;
const orderedRows = useMemo((): T[] => {
// Column sort takes precedence — ignore manual order while sorting
if (activeSortField !== null) return rows;
if (rowOrder.length === 0) return rows;
const indexMap = new Map(rowOrder.map((id, i) => [id, i]));
return [...rows].sort((a, b) => {
const ai = indexMap.get(a.id) ?? Infinity;
const bi = indexMap.get(b.id) ?? Infinity;
return ai - bi;
});
}, [rows, rowOrder, activeSortField]);
const reorder = useCallback(
(draggedId: string, targetId: string) => {
if (draggedId === targetId) return;
// Build current ordered ID list from the current orderedRows snapshot
// (works correctly even when rowOrder is empty / partial)
const currentIds = orderedRows.map((r) => r.id);
const fromIdx = currentIds.indexOf(draggedId);
const toIdx = currentIds.indexOf(targetId);
if (fromIdx === -1 || toIdx === -1) return;
const next = [...currentIds];
next.splice(fromIdx, 1);
next.splice(toIdx, 0, draggedId);
// Clear any active column sort (mutual exclusion)
resetSort();
setRowOrder(next);
},
[orderedRows, resetSort, setRowOrder],
);
const resetOrder = useCallback(() => {
setRowOrder([]);
}, [setRowOrder]);
const isCustomOrder = rowOrder.length > 0 && activeSortField === null;
return { orderedRows, reorder, isCustomOrder, resetOrder };
}