From cdc310223c831d1b214e431ed577f230514011ab Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Fri, 6 Mar 2026 15:27:18 +0100 Subject: [PATCH] feat: implement virtualized heatmap grid component using TanStack Table and a new Zustand store for product data. --- src/features/grid/components/heatmap-grid.tsx | 22 +++++++++++++++---- src/features/grid/store/use-grid-store.ts | 12 +++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/features/grid/components/heatmap-grid.tsx b/src/features/grid/components/heatmap-grid.tsx index bd5eaaa..2a43bb0 100644 --- a/src/features/grid/components/heatmap-grid.tsx +++ b/src/features/grid/components/heatmap-grid.tsx @@ -120,12 +120,14 @@ interface VirtualRowProps { rowHeight: number; isSelected: boolean; columnVisibility: Record; + columnSizing: Record; } -const GridRow = React.memo(({ virtualRow, row, rowHeight, isSelected, columnVisibility }: VirtualRowProps) => { +const GridRow = React.memo(({ virtualRow, row, rowHeight, isSelected, columnVisibility, columnSizing }: VirtualRowProps) => { // Uniquement la ligne concernée écoute son propre changement pour l'effet visuel const effectiveGamme = useGridStore((s) => s.draftChanges[row.original.codein] ?? row.original.codeGamme); void columnVisibility; // Force re-render via React.memo when visibility changes + void columnSizing; // Force re-render via React.memo when resizing columns return ( ([]); const [rowSelection, setRowSelection] = useState({}); - const { columnVisibility, setColumnVisibility } = useGridStore(); + const { columnVisibility, setColumnVisibility, columnSizing, setColumnSizing } = useGridStore(); const [isMounted, setIsMounted] = useState(false); const [portalContainer, setPortalContainer] = useState(null); const tableContainerRef = useRef(null); @@ -452,10 +454,12 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) { const table = useReactTable({ data: rows, columns, - state: { sorting, globalFilter: filters.search, rowSelection, columnVisibility }, + state: { sorting, globalFilter: filters.search, rowSelection, columnVisibility, columnSizing }, onSortingChange: setSorting, onRowSelectionChange: handleRowSelectionChange, onColumnVisibilityChange: setColumnVisibility, + onColumnSizingChange: setColumnSizing, + columnResizeMode: "onChange", getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), @@ -560,7 +564,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) { return ( )} + {/* Handle de redimensionnement de la colonne */} + {header.column.getCanResize() && ( +
e.stopPropagation()} + className={`absolute right-0 top-0 h-full w-1.5 cursor-col-resize user-select-none touch-none hover:bg-emerald-500/50 ${header.column.getIsResizing() ? "bg-emerald-500" : ""}`} + /> + )} ); })} @@ -601,6 +614,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) { rowHeight={rowHeight} isSelected={isSelected} columnVisibility={columnVisibility} + columnSizing={columnSizing} />
); diff --git a/src/features/grid/store/use-grid-store.ts b/src/features/grid/store/use-grid-store.ts index 34bfcf1..43ccdfa 100644 --- a/src/features/grid/store/use-grid-store.ts +++ b/src/features/grid/store/use-grid-store.ts @@ -16,6 +16,8 @@ interface GridState { activeGridQuery: string; /** Persisted column visibility state */ columnVisibility: Record; + /** Persisted column sizing state */ + columnSizing: Record; // Actions setRows: (rows: ProductRow[]) => void; @@ -29,6 +31,7 @@ interface GridState { restoreSnapshot: (changes: Record) => void; batchSetDraftGamme: (changes: Record) => void; setColumnVisibility: (updater: Record | ((old: Record) => Record)) => void; + setColumnSizing: (updater: Record | ((old: Record) => Record)) => void; } function computeSummary(rows: ProductRow[], drafts: Record): GridSummary { @@ -71,6 +74,7 @@ export const useGridStore = create()( displayDensity: "normal", activeGridQuery: "", columnVisibility: {}, + columnSizing: {}, setRows: (rows) => { set({ rows, summary: computeSummary(rows, get().draftChanges) }); @@ -134,16 +138,22 @@ export const useGridStore = create()( columnVisibility: typeof updater === 'function' ? updater(state.columnVisibility) : updater })); }, + setColumnSizing: (updater) => { + set((state) => ({ + columnSizing: typeof updater === 'function' ? updater(state.columnSizing) : updater + })); + }, }), { name: "collectflow-grid-storage", - // Only persist filters, display density, drafts, active grid query, and column visibility + // Only persist filters, display density, drafts, active grid query, and column visibility/sizing partialize: (state) => ({ filters: state.filters, displayDensity: state.displayDensity, draftChanges: state.draftChanges, activeGridQuery: state.activeGridQuery, columnVisibility: state.columnVisibility, + columnSizing: state.columnSizing, }), } )