diff --git a/apps/docs/components/ExampleUseDataGrid.tsx b/apps/docs/components/ExampleUseDataGrid.tsx index f0645662..c8bbf426 100644 --- a/apps/docs/components/ExampleUseDataGrid.tsx +++ b/apps/docs/components/ExampleUseDataGrid.tsx @@ -40,6 +40,7 @@ export function ExampleUseDataGrid() { }; const dataGrid = useDataGrid({ + tableId: 'example-use-data-grid', columns, onPage: handleOnPage }); diff --git a/apps/docs/package.json b/apps/docs/package.json index 1f63df1a..1e37c2ab 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -43,7 +43,7 @@ "tailwindcss-animate": "1.0.7" }, "devDependencies": { - "@types/node": "18.19.5", + "@types/node": "18.19.6", "@types/react": "18.2.47", "@types/react-dom": "18.2.18", "autoprefixer": "10.4.16", diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index 20086e9b..26a79eb1 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@microsoft/api-extractor": "7.39.1", - "@types/node": "18.19.5", + "@types/node": "18.19.6", "@types/react": "18.2.47", "@types/react-dom": "18.2.18", "eslint-config-custom": "workspace:*", diff --git a/packages/react-mui-hooks/changes/Added [useDataGrid] Column ordering support b/packages/react-mui-hooks/changes/Added [useDataGrid] Column ordering support new file mode 100644 index 00000000..e69de29b diff --git a/packages/react-mui-hooks/changes/Added [useDataGrid] Column pinning support b/packages/react-mui-hooks/changes/Added [useDataGrid] Column pinning support new file mode 100644 index 00000000..e69de29b diff --git a/packages/react-mui-hooks/hooks/useDataGrid.tsx b/packages/react-mui-hooks/hooks/useDataGrid.tsx index 86758df1..a1efa001 100644 --- a/packages/react-mui-hooks/hooks/useDataGrid.tsx +++ b/packages/react-mui-hooks/hooks/useDataGrid.tsx @@ -38,6 +38,8 @@ import { type UncapitalizedGridProSlotsComponent, type GridFilterModel, type GridColumnResizeParams, + type GridPinnedColumns, + type GridColumnOrderChangeParams, } from '@mui/x-data-grid-pro'; import { useResizeObserver } from '@enterwell/react-hooks'; import { format } from 'date-fns'; @@ -48,6 +50,8 @@ const DISPLAY_DATETIME_FORMAT = "dd.MM.yyyy. HH:mm:ss"; const DISPLAY_DATE_FORMAT = "dd.MM.yyyy."; const columnVisibilityLocalStorageKey = 'muidatagrid-columnvisibility'; const columnSizeLocalStorageKey = 'muidatagrid-columnwidth'; +const columnPinningLocalStorageKey = 'muidatagrid-columnpinning'; +const columnOrderingLocalStorageKey = 'muidatagrid-columnordering'; const dataGridSx = (theme: Theme) => ({ '& .MuiDataGrid-columnHeader::after': { @@ -454,11 +458,12 @@ export function useDataGrid({ }; // Column visibility - const [columnVisibility, setColumnVisibility] = useState( - Boolean(tableId) && localStorage.getItem(`${columnVisibilityLocalStorageKey}-${tableId}`) !== null - ? JSON.parse(localStorage.getItem(`${columnVisibilityLocalStorageKey}-${tableId}`) ?? "") + const [columnVisibility, setColumnVisibility] = useState(() => { + const savedColumnVisibility = localStorage.getItem(`${columnVisibilityLocalStorageKey}-${tableId}`); + return Boolean(tableId) && savedColumnVisibility !== null + ? JSON.parse(savedColumnVisibility) : columnVisibilityModel - ); + }); /** * Handles the column visibility model changing. @@ -557,15 +562,15 @@ export function useDataGrid({ setIsMobile(window.innerWidth < theme.breakpoints.values.sm); }); - const columnsMemo = useMemo(() => columns.map((c) => { + const columnsMemo = useMemo(() => { + const columnsAdjusted = columns.map((c) => { // Get column width from local storage let width: number | null | undefined; if (tableId) { - const storageValue = localStorage.getItem(`${columnSizeLocalStorageKey}-${tableId}-${c.field}`); - - if (storageValue) { - width = Number(storageValue); + const columnSizeStorageValue = localStorage.getItem(`${columnSizeLocalStorageKey}-${tableId}-${c.field}`); + if (columnSizeStorageValue) { + width = Number(columnSizeStorageValue); } } @@ -587,8 +592,27 @@ export function useDataGrid({ flex: undefined, width } + }; + }); + + // Reorder columns based on saves order (if provided) + if (tableId) { + columnsAdjusted.forEach((c) => { + const columnOrderingStorageValue = localStorage.getItem(`${columnOrderingLocalStorageKey}-${tableId}-${c.field}`); + if (!columnOrderingStorageValue) return; + + const order = JSON.parse(columnOrderingStorageValue); + if (order != null) { + // Move column to new index + const index = columnsAdjusted.findIndex((x) => x.field === c.field); + columnsAdjusted.splice(index, 1); + columnsAdjusted.splice(order, 0, c); + } + }); } - }), [tableId, columns, headerRenderer, rowHeight]); + + return columnsAdjusted; + }, [tableId, columns, headerRenderer, rowHeight]); /** * Handles rows scroll end action. @@ -611,6 +635,39 @@ export function useDataGrid({ } }; + // Column pinning + const [pinnedColumns, setPinnedColumns] = useState(() => { + const savedPinnedColumns = localStorage.getItem(`${columnPinningLocalStorageKey}-${tableId}`); + return Boolean(tableId) && savedPinnedColumns != null + ? JSON.parse(savedPinnedColumns) + : null + }); + + /** + * Handles pinned columns change action. + * + * @param newPinnedColumns The new pinned columns. + */ + const handlePinnedColumnsChange = (newPinnedColumns: GridPinnedColumns) => { + if (tableId) { + localStorage.setItem(`${columnPinningLocalStorageKey}-${tableId}`, JSON.stringify(newPinnedColumns)); + } + setPinnedColumns(newPinnedColumns); + }; + + /** + * Handles column order change. + * + * @param newColumnOrder The new column order. + */ + const handleColumnOrderChange = (newColumnOrder: GridColumnOrderChangeParams) => { + if (tableId) { + localStorage.setItem( + `${columnOrderingLocalStorageKey}-${tableId}-${newColumnOrder.column.field}`, + JSON.stringify(newColumnOrder.targetIndex)); + } + }; + return { props: { ref: resizeRef, @@ -628,6 +685,9 @@ export function useDataGrid({ columnVisibilityModel: columnVisibility, onColumnVisibilityModelChange: handleColumnVisibilityChange, onColumnWidthChange: handleOnColumnWidthChange, + onColumnOrderChange: handleColumnOrderChange, + pinnedColumns, + onPinnedColumnsChange: handlePinnedColumnsChange, pagination: !infiniteLoading && enablePagination, paginationMode: 'server', paginationModel: { diff --git a/packages/react-mui-hooks/package.json b/packages/react-mui-hooks/package.json index 11b9ba76..3998d2a9 100644 --- a/packages/react-mui-hooks/package.json +++ b/packages/react-mui-hooks/package.json @@ -28,7 +28,7 @@ "@microsoft/api-extractor": "7.39.1", "@mui/material": "5.15.3", "@mui/x-data-grid-pro": "6.18.7", - "@types/node": "18.19.5", + "@types/node": "18.19.6", "@types/react": "18.2.47", "@types/react-dom": "18.2.18", "date-fns": "2.30.0", diff --git a/packages/react-ui/package.json b/packages/react-ui/package.json index 1f294038..a1c6a15e 100644 --- a/packages/react-ui/package.json +++ b/packages/react-ui/package.json @@ -33,7 +33,7 @@ "@mui/system": "5.15.3", "@mui/x-date-pickers-pro": "5.0.20", "@mui/x-date-pickers": "5.0.20", - "@types/node": "18.19.5", + "@types/node": "18.19.6", "@types/react": "18.2.47", "@types/react-dom": "18.2.18", "date-fns": "2.30.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20632325..7a4cd4aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: devDependencies: '@turbo/gen': specifier: 1.11.3 - version: 1.11.3(@types/node@18.19.5)(typescript@5.3.3) + version: 1.11.3(@types/node@18.19.6)(typescript@5.3.3) eslint: specifier: 8.56.0 version: 8.56.0 @@ -91,8 +91,8 @@ importers: version: 1.0.7(tailwindcss@3.4.1) devDependencies: '@types/node': - specifier: 18.19.5 - version: 18.19.5 + specifier: 18.19.6 + version: 18.19.6 '@types/react': specifier: 18.2.47 version: 18.2.47 @@ -135,10 +135,10 @@ importers: devDependencies: '@microsoft/api-extractor': specifier: 7.39.1 - version: 7.39.1(@types/node@18.19.5) + version: 7.39.1(@types/node@18.19.6) '@types/node': - specifier: 18.19.5 - version: 18.19.5 + specifier: 18.19.6 + version: 18.19.6 '@types/react': specifier: 18.2.47 version: 18.2.47 @@ -178,7 +178,7 @@ importers: version: link:../react-ui '@microsoft/api-extractor': specifier: 7.39.1 - version: 7.39.1(@types/node@18.19.5) + version: 7.39.1(@types/node@18.19.6) '@mui/material': specifier: 5.15.3 version: 5.15.3(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0) @@ -186,8 +186,8 @@ importers: specifier: 6.18.7 version: 6.18.7(@mui/material@5.15.3)(@mui/system@5.15.3)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0) '@types/node': - specifier: 18.19.5 - version: 18.19.5 + specifier: 18.19.6 + version: 18.19.6 '@types/react': specifier: 18.2.47 version: 18.2.47 @@ -233,7 +233,7 @@ importers: version: link:../react-hooks '@microsoft/api-extractor': specifier: 7.39.1 - version: 7.39.1(@types/node@18.19.5) + version: 7.39.1(@types/node@18.19.6) '@mui/icons-material': specifier: 5.15.3 version: 5.15.3(@mui/material@5.15.3)(@types/react@18.2.47)(react@18.2.0) @@ -250,8 +250,8 @@ importers: specifier: 5.0.20 version: 5.0.20(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@mui/material@5.15.3)(@mui/system@5.15.3)(@types/react@18.2.47)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) '@types/node': - specifier: 18.19.5 - version: 18.19.5 + specifier: 18.19.6 + version: 18.19.6 '@types/react': specifier: 18.2.47 version: 18.2.47 @@ -1103,24 +1103,24 @@ packages: react: 18.2.0 dev: false - /@microsoft/api-extractor-model@7.28.4(@types/node@18.19.5): + /@microsoft/api-extractor-model@7.28.4(@types/node@18.19.6): resolution: {integrity: sha512-vucgyPmgHrJ/D4/xQywAmjTmSfxAx2/aDmD6TkIoLu51FdsAfuWRbijWA48AePy60OO+l+mmy9p2P/CEeBZqig==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.63.0(@types/node@18.19.5) + '@rushstack/node-core-library': 3.63.0(@types/node@18.19.6) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.39.1(@types/node@18.19.5): + /@microsoft/api-extractor@7.39.1(@types/node@18.19.6): resolution: {integrity: sha512-V0HtCufWa8hZZvSmlEzQZfINcJkHAU/bmpyJQj6w+zpI87EkR8DuBOW6RWrO9c7mUYFZoDaNgUTyKo83ytv+QQ==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.4(@types/node@18.19.5) + '@microsoft/api-extractor-model': 7.28.4(@types/node@18.19.6) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.63.0(@types/node@18.19.5) + '@rushstack/node-core-library': 3.63.0(@types/node@18.19.6) '@rushstack/rig-package': 0.5.1 '@rushstack/ts-command-line': 4.17.1 colors: 1.2.5 @@ -1875,7 +1875,7 @@ packages: resolution: {integrity: sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==} dev: true - /@rushstack/node-core-library@3.63.0(@types/node@18.19.5): + /@rushstack/node-core-library@3.63.0(@types/node@18.19.6): resolution: {integrity: sha512-Q7B3dVpBQF1v+mUfxNcNZh5uHVR8ntcnkN5GYjbBLrxUYHBGKbnCM+OdcN+hzCpFlLBH6Ob0dEHhZ0spQwf24A==} peerDependencies: '@types/node': '*' @@ -1883,7 +1883,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 18.19.5 + '@types/node': 18.19.6 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -1954,7 +1954,7 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@turbo/gen@1.11.3(@types/node@18.19.5)(typescript@5.3.3): + /@turbo/gen@1.11.3(@types/node@18.19.6)(typescript@5.3.3): resolution: {integrity: sha512-cHGRj7Jn7Hw1cA7NuwWYfYdhEliQX4LuSfEB9L1m8ifGkHalU3bbYXcehzLThmckpGpUQGnXYx0UtVudbQ42HA==} hasBin: true dependencies: @@ -1966,7 +1966,7 @@ packages: minimatch: 9.0.3 node-plop: 0.26.3 proxy-agent: 6.3.1 - ts-node: 10.9.1(@types/node@18.19.5)(typescript@5.3.3) + ts-node: 10.9.1(@types/node@18.19.6)(typescript@5.3.3) update-check: 1.5.4 validate-npm-package-name: 5.0.0 transitivePeerDependencies: @@ -2042,7 +2042,7 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.19.5 + '@types/node': 18.19.6 dev: true /@types/hast@2.3.5: @@ -2104,8 +2104,8 @@ packages: resolution: {integrity: sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==} dev: false - /@types/node@18.19.5: - resolution: {integrity: sha512-22MG6T02Hos2JWfa1o5jsIByn+bc5iOt1IS4xyg6OG68Bu+wMonVZzdrgCw693++rpLE9RUT/Bx15BeDzO0j+g==} + /@types/node@18.19.6: + resolution: {integrity: sha512-X36s5CXMrrJOs2lQCdDF68apW4Rfx9ixYMawlepwmE4Anezv/AV2LSpKD1Ub8DAc+urp5bk0BGZ6NtmBitfnsg==} dependencies: undici-types: 5.26.5 dev: true @@ -2153,7 +2153,7 @@ packages: /@types/through@0.0.31: resolution: {integrity: sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==} dependencies: - '@types/node': 18.19.5 + '@types/node': 18.19.6 dev: true /@types/tinycolor2@1.4.4: @@ -8554,7 +8554,7 @@ packages: /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - /ts-node@10.9.1(@types/node@18.19.5)(typescript@5.3.3): + /ts-node@10.9.1(@types/node@18.19.6)(typescript@5.3.3): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -8573,7 +8573,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.5 + '@types/node': 18.19.6 acorn: 8.11.2 acorn-walk: 8.2.0 arg: 4.1.3 @@ -8620,7 +8620,7 @@ packages: typescript: optional: true dependencies: - '@microsoft/api-extractor': 7.39.1(@types/node@18.19.5) + '@microsoft/api-extractor': 7.39.1(@types/node@18.19.6) bundle-require: 4.0.1(esbuild@0.19.7) cac: 6.7.14 chokidar: 3.5.3