BC-12 feat: store accounts table columns resizing to local storage.

This commit is contained in:
a.bouhuolia
2021-09-08 15:58:29 +02:00
parent 97c3fd9e0c
commit 7b3d310eab
5 changed files with 99 additions and 16 deletions

View File

@@ -32,6 +32,8 @@ import TableWrapper from './Datatable/TableWrapper';
import TableIndeterminateCheckboxRow from './Datatable/TableIndeterminateCheckboxRow'; import TableIndeterminateCheckboxRow from './Datatable/TableIndeterminateCheckboxRow';
import TableIndeterminateCheckboxHeader from './Datatable/TableIndeterminateCheckboxHeader'; import TableIndeterminateCheckboxHeader from './Datatable/TableIndeterminateCheckboxHeader';
import { useResizeObserver } from './Datatable/utils';
/** /**
* Datatable component. * Datatable component.
*/ */
@@ -78,6 +80,9 @@ export default function DataTable(props) {
TablePaginationRenderer, TablePaginationRenderer,
TableFooterRenderer, TableFooterRenderer,
onColumnResizing,
initialColumnsWidths,
...restProps ...restProps
} = props; } = props;
@@ -106,6 +111,9 @@ export default function DataTable(props) {
pageIndex: initialPageIndex, pageIndex: initialPageIndex,
pageSize: initialPageSize, pageSize: initialPageSize,
expanded, expanded,
columnResizing: {
columnWidths: initialColumnsWidths || {},
},
}, },
manualPagination, manualPagination,
pageCount: controlledPageCount, pageCount: controlledPageCount,
@@ -164,6 +172,11 @@ export default function DataTable(props) {
saveInvoke(onSelectedRowsChange, selectedFlatRows); saveInvoke(onSelectedRowsChange, selectedFlatRows);
}, [selectedRowIds, onSelectedRowsChange]); }, [selectedRowIds, onSelectedRowsChange]);
// Column resizing observer.
useResizeObserver(table.state, (current, columnWidth, columnsResizing) => {
onColumnResizing && onColumnResizing(current, columnWidth, columnsResizing);
});
return ( return (
<TableContext.Provider value={{ table, props }}> <TableContext.Provider value={{ table, props }}>
<TableWrapperRenderer> <TableWrapperRenderer>

View File

@@ -1,3 +1,5 @@
import { React } from 'react';
export const isCellLoading = (loading, cellsCoords, rowIndex, columnId) => { export const isCellLoading = (loading, cellsCoords, rowIndex, columnId) => {
if (!loading) { if (!loading) {
return false; return false;
@@ -8,3 +10,26 @@ export const isCellLoading = (loading, cellsCoords, rowIndex, columnId) => {
(cellCoord) => cellCoord[0] === rowIndex && cellCoord[1] === columnId, (cellCoord) => cellCoord[0] === rowIndex && cellCoord[1] === columnId,
); );
}; };
export const useResizeObserver = (state, callback) => {
// This Ref will contain the id of the column being resized or undefined
const columnResizeRef = React.useRef();
React.useEffect(() => {
// We are interested in calling the resize event only when "state.columnResizing?.isResizingColumn" changes from
// a string to undefined, because it indicates that it WAS resizing but it no longer is.
if (
state.columnResizing &&
!state.columnResizing?.isResizingColumn &&
columnResizeRef.current
) {
// Trigger resize event
callback(
columnResizeRef.current,
state.columnResizing.columnWidths[columnResizeRef.current],
state.columnResizing,
);
}
columnResizeRef.current = state.columnResizing?.isResizingColumn;
}, [callback, state.columnResizing]);
};

View File

@@ -15,6 +15,8 @@ import withAlertsActions from 'containers/Alert/withAlertActions';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { useMemorizedColumnsWidths } from '../../hooks';
/** /**
* Accounts data-table. * Accounts data-table.
*/ */
@@ -28,11 +30,8 @@ function AccountsDataTable({
// #withDrawerActions // #withDrawerActions
openDrawer, openDrawer,
}) { }) {
const { const { isAccountsLoading, isAccountsFetching, accounts } =
isAccountsLoading, useAccountsChartContext();
isAccountsFetching,
accounts,
} = useAccountsChartContext();
// Retrieve accounts table columns. // Retrieve accounts table columns.
const columns = useAccountsTableColumns(); const columns = useAccountsTableColumns();
@@ -80,6 +79,9 @@ function AccountsDataTable({
const handleCellClick = (cell, event) => { const handleCellClick = (cell, event) => {
openDrawer('account-drawer', { accountId: cell.row.original.id }); openDrawer('account-drawer', { accountId: cell.row.original.id });
}; };
// Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths('accounts');
return ( return (
<DataTable <DataTable
@@ -98,7 +100,7 @@ function AccountsDataTable({
autoResetSelectedRows={false} autoResetSelectedRows={false}
expandColumnSpace={1} expandColumnSpace={1}
expandToggleColumn={2} expandToggleColumn={2}
selectionColumnWidth={50} selectionColumnWidth={45}
TableCellRenderer={TableFastCell} TableCellRenderer={TableFastCell}
TableRowsRenderer={TableVirtualizedListRows} TableRowsRenderer={TableVirtualizedListRows}
TableLoadingRenderer={TableSkeletonRows} TableLoadingRenderer={TableSkeletonRows}
@@ -108,6 +110,8 @@ function AccountsDataTable({
vListrowHeight={42} vListrowHeight={42}
vListOverscanRowCount={0} vListOverscanRowCount={0}
onCellClick={handleCellClick} onCellClick={handleCellClick}
initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing}
payload={{ payload={{
onEdit: handleEditAccount, onEdit: handleEditAccount,
onDelete: handleDeleteAccount, onDelete: handleDeleteAccount,

View File

@@ -1,10 +1,9 @@
import { useRef, useEffect, useMemo } from 'react'; import React, { useRef, useEffect, useMemo } from 'react';
import useAsync from './async'; import useAsync from './async';
import useAutofocus from './useAutofocus'; import useAutofocus from './useAutofocus';
// import use from 'async'; // import use from 'async';
/** /**
* A custom useEffect hook that only triggers on updates, not on initial mount * A custom useEffect hook that only triggers on updates, not on initial mount
* Idea stolen from: https://stackoverflow.com/a/55075818/1526448 * Idea stolen from: https://stackoverflow.com/a/55075818/1526448
@@ -49,11 +48,10 @@ const isCurrentFocus = (autoFocus, columnId, rowIndex) => {
}; };
export function useCellAutoFocus(ref, autoFocus, columnId, rowIndex) { export function useCellAutoFocus(ref, autoFocus, columnId, rowIndex) {
const focus = useMemo(() => isCurrentFocus(autoFocus, columnId, rowIndex), [ const focus = useMemo(
autoFocus, () => isCurrentFocus(autoFocus, columnId, rowIndex),
columnId, [autoFocus, columnId, rowIndex],
rowIndex, );
]);
useEffect(() => { useEffect(() => {
if (ref.current && focus) { if (ref.current && focus) {
ref.current.focus(); ref.current.focus();
@@ -65,3 +63,46 @@ export function useCellAutoFocus(ref, autoFocus, columnId, rowIndex) {
export * from './useRequestPdf'; export * from './useRequestPdf';
export { useAsync, useAutofocus }; export { useAsync, useAutofocus };
// Hook
export function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = React.useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
return [storedValue, setValue];
}
export function useMemorizedColumnsWidths(tableName) {
const [get, save] = useLocalStorage(`${tableName}.columns_widths`, {});
const handleColumnResizing = (current, columnWidth, columnsResizing) => {
save(columnsResizing.columnWidths);
};
return [get, save, handleColumnResizing];
}

View File

@@ -6,12 +6,12 @@ import {
useSetGlobalErrors, useSetGlobalErrors,
useAuthToken, useAuthToken,
} from './state'; } from './state';
import { useAppIntlContext } from '../components/AppIntlProvider'; import { getCookie } from '../utils';
export default function useApiRequest() { export default function useApiRequest() {
const setGlobalErrors = useSetGlobalErrors(); const setGlobalErrors = useSetGlobalErrors();
const { setLogout } = useAuthActions(); const { setLogout } = useAuthActions();
const { currentLocale } = useAppIntlContext(); const currentLocale = getCookie('locale');
// Authentication token. // Authentication token.
const token = useAuthToken(); const token = useAuthToken();