show save state for column sizing

This commit is contained in:
Sidney Alcantara
2022-10-24 14:59:29 +11:00
parent 6d1c6b0100
commit 918528fb6a
3 changed files with 107 additions and 40 deletions

View File

@@ -68,10 +68,11 @@ import { formatSubTableName } from "@src/utils/table";
import { TableRow, ColumnConfig } from "@src/types/table";
import { StyledCell } from "./Styled/StyledCell";
import { useKeyboardNavigation } from "./useKeyboardNavigation";
import { useSaveColumnSizing } from "./useSaveColumnSizing";
export const DEFAULT_ROW_HEIGHT = 41;
export const DEFAULT_COL_WIDTH = 150;
export const MIN_COL_WIDTH = 32;
export const MIN_COL_WIDTH = 80;
export const TABLE_PADDING = 16;
export const TABLE_GUTTER = 8;
export const DEBOUNCE_DELAY = 500;
@@ -84,8 +85,6 @@ const columnHelper = createColumnHelper<TableRow>();
const getRowId = (row: TableRow) => row._rowy_ref.path || row._rowy_ref.id;
export default function TableComponent() {
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const [userRoles] = useAtom(userRolesAtom, projectScope);
const [userSettings] = useAtom(userSettingsAtom, projectScope);
@@ -199,43 +198,7 @@ export default function TableComponent() {
onColumnSizingChange: setColumnSizing,
}));
// Debounce for saving to schema
const [debouncedColumnSizing] = useDebounce(columnSizing, DEBOUNCE_DELAY, {
equalityFn: isEqual,
});
// Offer to save when column sizing changes
useEffect(() => {
if (!canEditColumn || isEmpty(debouncedColumnSizing)) return;
const snackbarId = enqueueSnackbar("Save column sizes for all users?", {
action: (
<LoadingButton
variant="contained"
color="primary"
onClick={handleSaveToSchema}
>
Save
</LoadingButton>
),
anchorOrigin: { horizontal: "center", vertical: "top" },
});
async function handleSaveToSchema() {
const promises = Object.entries(debouncedColumnSizing).map(
([key, value]) => updateColumn({ key, config: { width: value } })
);
await Promise.all(promises);
closeSnackbar(snackbarId);
}
return () => closeSnackbar(snackbarId);
}, [
debouncedColumnSizing,
canEditColumn,
enqueueSnackbar,
closeSnackbar,
updateColumn,
]);
useSaveColumnSizing(columnSizing, canEditColumn);
const { rows } = table.getRowModel();
const leafColumns = table.getVisibleLeafColumns();

View File

@@ -138,3 +138,5 @@ export function useKeyboardNavigation({
return { handleKeyDown, focusInsideCell } as const;
}
export default useKeyboardNavigation;

View File

@@ -0,0 +1,102 @@
import { useEffect, useState } from "react";
import { useSetAtom } from "jotai";
import { useSnackbar } from "notistack";
import { useDebounce } from "use-debounce";
import { isEqual, isEmpty } from "lodash-es";
import LoadingButton from "@mui/lab/LoadingButton";
import CheckIcon from "@mui/icons-material/Check";
import CircularProgressOptical from "@src/components/CircularProgressOptical";
import {
tableScope,
updateColumnAtom,
IUpdateColumnOptions,
} from "@src/atoms/tableScope";
import { DEBOUNCE_DELAY } from "./Table";
import { ColumnSizingState } from "@tanstack/react-table";
/** Debounces columnSizing and asks admins if they want to save for all users */
export function useSaveColumnSizing(
columnSizing: ColumnSizingState,
canEditColumn: boolean
) {
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const updateColumn = useSetAtom(updateColumnAtom, tableScope);
// Debounce for saving to schema
const [debouncedColumnSizing] = useDebounce(columnSizing, DEBOUNCE_DELAY, {
equalityFn: isEqual,
});
// Offer to save when column sizing changes
useEffect(() => {
if (!canEditColumn || isEmpty(debouncedColumnSizing)) return;
const snackbarId = enqueueSnackbar("Save column sizes for all users?", {
action: (
<SaveColumnSizingButton
debouncedColumnSizing={debouncedColumnSizing}
updateColumn={updateColumn}
/>
),
anchorOrigin: { horizontal: "center", vertical: "top" },
});
return () => closeSnackbar(snackbarId);
}, [
debouncedColumnSizing,
canEditColumn,
enqueueSnackbar,
closeSnackbar,
updateColumn,
]);
return null;
}
interface ISaveColumnSizingButtonProps {
debouncedColumnSizing: ColumnSizingState;
updateColumn: (update: IUpdateColumnOptions) => Promise<void>;
}
/**
* Make the button a component so it can have its own state,
* so we can display the loading state without showing a new snackbar
*/
function SaveColumnSizingButton({
debouncedColumnSizing,
updateColumn,
}: ISaveColumnSizingButtonProps) {
const [state, setState] = useState<"" | "loading" | "success">("");
const handleSaveToSchema = async () => {
setState("loading");
// Do this one by one for now to prevent race conditions.
// Need to support updating multiple columns in updateColumnAtom
// in the future.
for (const [key, value] of Object.entries(debouncedColumnSizing)) {
await updateColumn({ key, config: { width: value } });
}
setState("success");
};
return (
<LoadingButton
variant="contained"
color="primary"
onClick={handleSaveToSchema}
loading={Boolean(state)}
loadingIndicator={
state === "success" ? (
<CheckIcon color="primary" />
) : (
<CircularProgressOptical size={20} color="primary" />
)
}
>
Save
</LoadingButton>
);
}
export default useSaveColumnSizing;