UNPKG

@linzjs/step-ag-grid

Version:

[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) > Reusable [ag-grid](https://www.ag-grid.com/) component for LINZ / Toitū te whenua.

192 lines (162 loc) 7.84 kB
import { IRowNode } from 'ag-grid-community'; import { ColumnState } from 'ag-grid-community'; import { PostSortRowsParams } from 'ag-grid-community'; import { isEmpty } from 'lodash-es'; import { useCallback, useContext, useRef } from 'react'; import { GridContext } from '../contexts/GridContext'; interface PostSortRowsHookProps { setStaleGrid: (stale: boolean) => void; } /** * Retains last sort order from via <AgGrid postRowSort>. * Then applies this sort until next sort column change. * Handles stale sort, when you edit a row but don't want to re-sort. */ export const usePostSortRowsHook = ({ setStaleGrid }: PostSortRowsHookProps) => { const { redrawRows } = useContext(GridContext); // On first run we need to init the first backed up sort order const initialised = useRef(false); // Used to detect sort order has changed since last run const lastSortOrderHash = useRef<string>(''); // Used to detect if sort order was the same, has the direction changed const previousRowSortIndexRef = useRef<Record<string, { index: number; hash: string } | undefined>>({}); // stale sort is when there's a sort and user edits a row // this applies a class to the div wrapping the grid which in turn adds a * beside the sort arrow const sortWasStale = useRef(false); // Previous col sort is stored and reapplied if the sort direction changes on the same column and the sort was stale // As a sort on a stale row should just resort, not change sort direction const previousColSort = useRef<ColumnState[]>([]); const previousQuickFilter = useRef(''); return useCallback( ({ api, nodes }: PostSortRowsParams) => { // Grid is destroyed if (!api) return; const previousRowSortIndex = previousRowSortIndexRef.current; const hashNode = (node: IRowNode | undefined) => { return node ? JSON.stringify(node.data) : ''; }; const copyCurrentSortSettings = (): ColumnState[] => api.getColumnState().map((row) => ({ colId: row.colId, sort: row.sort, sortIndex: row.sortIndex })); const backupSortOrder = () => { for (const x in previousRowSortIndex) delete previousRowSortIndex[x]; nodes.forEach((node, index) => (previousRowSortIndex[`${node.data.id}`] = { index, hash: hashNode(node) })); }; // Check if column is the first sorted column. Note: column is preconfigured to sort its sortIndex is null not 1 const isFirstSortColumn = (row: ColumnState) => row.sort && [0, null].includes(row.sortIndex ?? null); const isSameColumnAndDifferentSort = (col1: ColumnState | undefined, col2: ColumnState | undefined) => col1 && col2 && col1.colId === col2.colId && col1.sort != col2.sort; const restorePreviousSortColumnState = () => api.applyColumnState({ state: previousColSort.current }); const sortNodesByPreviousSort = () => { nodes.sort( (a, b) => (previousRowSortIndex[`${a.data.id}`]?.index ?? Number.MAX_SAFE_INTEGER) - (previousRowSortIndex[`${b.data.id}`]?.index ?? Number.MAX_SAFE_INTEGER), ); }; // On first load copy the current sort if (!initialised.current) { initialised.current = true; previousColSort.current = copyCurrentSortSettings(); } const newSortOrder = JSON.stringify(copyCurrentSortSettings()); let sortOrderChanged = newSortOrder != lastSortOrderHash.current; const quickFilter = api.getQuickFilter(); if (previousQuickFilter.current != quickFilter) { previousQuickFilter.current = quickFilter ?? ''; sortOrderChanged = true; } if (isEmpty(previousRowSortIndex)) { backupSortOrder(); } if (sortOrderChanged) { const thisFirstCol = copyCurrentSortSettings().find(isFirstSortColumn); const previousFirstCol = previousColSort.current.find(isFirstSortColumn); // Change to sort can be: (in the context of stale sort do...) // - no sort to sorted (do nothing) // - asc to desc (resort as asc, and remove stale sort) // - desc to no sort (resort as desc, and remove stale sort) const wasSortChangedOnSameColumn = previousFirstCol && (!thisFirstCol || isSameColumnAndDifferentSort(previousFirstCol, thisFirstCol)); // if sort was stale, and we're here, someone has clicked a stale sort order (^*) in the column header if (sortWasStale.current && wasSortChangedOnSameColumn) { // Sort was an existing priority 1 column // We want to re-sort with the old sort direction and clear stale sort sortWasStale.current = false; setStaleGrid(false); // trigger a recapture of sort order lastSortOrderHash.current = ''; restorePreviousSortColumnState(); } else { // Sort was on a different column so clear sort and sort like normal if (sortWasStale.current) { sortWasStale.current = false; setStaleGrid(false); } // Columns were sorted so retain the sort backupSortOrder(); lastSortOrderHash.current = newSortOrder; } } else { let firstChangedNodeIndex = -1; let lastNewNode: IRowNode | undefined = undefined; let changedRowCount = 0; let newRowCount = 0; let index = 0; for (const node of nodes) { const psr = previousRowSortIndex[`${node.data.id}`]; if (psr) { if (psr.hash != hashNode(node)) { if (firstChangedNodeIndex === -1) firstChangedNodeIndex = index; changedRowCount++; } } else { lastNewNode = node; newRowCount++; } index++; } let wasStale = false; if (changedRowCount === 0 && newRowCount === 1) { // insert new row at end const newIndex = index - 1; previousRowSortIndex[`${lastNewNode?.data.id}`] = { index: newIndex, hash: hashNode(lastNewNode) }; wasStale = true; } else if (changedRowCount === 2 && newRowCount === 0) { // This must be a swap rows (sometimes it's not, needs further attention) backupSortOrder(); wasStale = false; } else if (changedRowCount > 1 && newRowCount === 1) { // This must be a insert so, insert new row near the row that changed previousRowSortIndex[`${lastNewNode?.data.id}`] = { index: firstChangedNodeIndex + 0.5, hash: hashNode(lastNewNode), }; wasStale = true; // For some reason AgGrid mis-positions the inserted row. lastNewNode && redrawRows(); } else if (changedRowCount == 1 && newRowCount === 0) { // User edited one row so, do nothing, retain sort wasStale = true; } else if (changedRowCount !== 0 || newRowCount != 0) { // too many rows changed, resort backupSortOrder(); } if (wasStale) { // Check if the sort order the aggrid passed matches our stale sort order const stillStale = Object.keys(previousRowSortIndex).length != nodes.length || nodes.some((node, index) => previousRowSortIndex[`${node.data.id}`]?.index !== index); // If we haven't already processed a stale sort then... if (stillStale && !sortWasStale.current) { // backup sort state, so we can restore it when sort is clicked on a stale column previousColSort.current = copyCurrentSortSettings(); sortWasStale.current = true; setStaleGrid(true); } } sortNodesByPreviousSort(); } }, [redrawRows, setStaleGrid], ); };