import { Fragment, useMemo, useState } from 'react'; import { MoreVertical, GripVertical, ChevronRight, ChevronDown } from 'lucide-react'; import { useUi } from '../context/ui-context.jsx'; import { useDragReorder } from '../hooks/useDragReorder.js'; import { Flyout, FlyoutItem } from './Flyout.jsx'; // ListView/DataGrid: columnas ordenables, buscador ligero sobre el haystack completo, clic en // fila abre detalle, boton "..." abre el menu de acciones (equivalente al clic derecho). // `onReorder(idsEnNuevoOrden)` es opcional: si se pasa, habilita arrastrar filas con el mouse para // definir un orden manual (columna con icono de agarre a la izquierda) — solo mientras no haya un // ordenamiento por columna activo, para no mezclar los dos mecanismos de orden a la vez. // `getChildren(fila)` es opcional: si devuelve filas, la fila se pliega/despliega con su boton o con // un clic y las hijas se muestran debajo. Al buscar, solo quedan las hijas que coinciden y se abren. export function ListView({ columns, rows, searchValue, getSearchHaystack, onRowClick, rowActions, getRowKey, initialSort, onReorder, getChildren }) { const { t } = useUi(); const [sort, setSort] = useState(initialSort || { key: null, dir: 1 }); const [expanded, setExpanded] = useState(() => new Set()); const haystack = (r) => (getSearchHaystack ? getSearchHaystack(r) : JSON.stringify(r)).toLowerCase(); const toggleRow = (key) => setExpanded((prev) => { const next = new Set(prev); if (!next.delete(key)) next.add(key); return next; }); const filtered = useMemo(() => { if (!searchValue) return rows; const q = searchValue.toLowerCase(); return rows.filter((r) => haystack(r).includes(q) || getChildren?.(r)?.some((c) => haystack(c).includes(q))); // eslint-disable-next-line react-hooks/exhaustive-deps }, [rows, searchValue, getSearchHaystack, getChildren]); const sorted = useMemo(() => { if (!sort.key) return filtered; const col = columns.find((c) => c.key === sort.key); return [...filtered].sort((a, b) => { const va = col.sortValue ? col.sortValue(a) : a[sort.key]; const vb = col.sortValue ? col.sortValue(b) : b[sort.key]; if (va === vb) return 0; return (va > vb ? 1 : -1) * sort.dir; }); }, [filtered, sort, columns]); const dragEnabled = !!onReorder && !sort.key && !searchValue; const { order, getHandleProps, getRowProps, isDragging } = useDragReorder(sorted, (ids) => onReorder?.(ids), getRowKey); const displayRows = dragEnabled ? order : sorted; function toggleSort(key) { setSort((prev) => (prev.key === key ? { key, dir: prev.dir * -1 } : { key, dir: 1 })); } function renderRow(row, { isParent, open, onToggle, child } = {}) { const actions = rowActions?.(row) || []; const activate = isParent ? onToggle : onRowClick ? () => onRowClick(row) : null; return (
| } {columns.map((col) => ( | {col.sortable === false ? col.label : } | ))} {rowActions &&} |
|---|