mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-28 16:06:41 +01:00
add resize columns & ask admins if they want to save to scheam
This commit is contained in:
@@ -11,6 +11,7 @@ export interface IColumnProps extends Partial<GridProps> {
|
||||
label: string;
|
||||
type?: FieldType;
|
||||
secondaryItem?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
|
||||
active?: boolean;
|
||||
}
|
||||
@@ -19,6 +20,7 @@ export default function Column({
|
||||
label,
|
||||
type,
|
||||
secondaryItem,
|
||||
children,
|
||||
|
||||
active,
|
||||
...props
|
||||
@@ -35,6 +37,7 @@ export default function Column({
|
||||
height: COLUMN_HEADER_HEIGHT,
|
||||
border: (theme) => `1px solid ${theme.palette.divider}`,
|
||||
backgroundColor: "background.default",
|
||||
position: "relative",
|
||||
|
||||
py: 0,
|
||||
px: 1,
|
||||
@@ -106,6 +109,8 @@ export default function Column({
|
||||
{secondaryItem}
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
53
src/components/Table/Styled/StyledResizer.tsx
Normal file
53
src/components/Table/Styled/StyledResizer.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { styled } from "@mui/material";
|
||||
|
||||
export interface IStyledResizerProps {
|
||||
isResizing: boolean;
|
||||
}
|
||||
|
||||
export const StyledResizer = styled("div", {
|
||||
name: "StyledResizer",
|
||||
shouldForwardProp: (prop) => prop !== "isResizing",
|
||||
})<IStyledResizerProps>(({ theme, isResizing }) => ({
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
height: "100%",
|
||||
width: 10,
|
||||
|
||||
cursor: "col-resize",
|
||||
userSelect: "none",
|
||||
touchAction: "none",
|
||||
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
|
||||
transition: theme.transitions.create("opacity", {
|
||||
duration: theme.transitions.duration.shortest,
|
||||
}),
|
||||
opacity: isResizing ? 1 : 0,
|
||||
"[role='columnheader']:hover &": { opacity: 0.33 },
|
||||
"[role='columnheader'] &:hover, [role='columnheader'] &:active": {
|
||||
opacity: 1,
|
||||
"&::before": { transform: "scaleY(1.25)" },
|
||||
},
|
||||
|
||||
"&::before": {
|
||||
content: "''",
|
||||
display: "block",
|
||||
|
||||
height: "50%",
|
||||
width: 4,
|
||||
borderRadius: 2,
|
||||
marginRight: 2,
|
||||
|
||||
background: isResizing
|
||||
? theme.palette.primary.main
|
||||
: theme.palette.action.active,
|
||||
transition: theme.transitions.create("transform", {
|
||||
duration: theme.transitions.duration.shortest,
|
||||
}),
|
||||
transform: isResizing ? "scaleY(1.5) !important" : undefined,
|
||||
},
|
||||
}));
|
||||
StyledResizer.displayName = "StyledResizer";
|
||||
@@ -1,9 +1,15 @@
|
||||
import { useMemo, useRef, useCallback, Suspense, useEffect } from "react";
|
||||
import { useMemo, useRef, useCallback, useState, useEffect } from "react";
|
||||
import { useAtom, useSetAtom } from "jotai";
|
||||
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
|
||||
import {
|
||||
useDebounce,
|
||||
useDebouncedCallback,
|
||||
useThrottledCallback,
|
||||
} from "use-debounce";
|
||||
import { useSnackbar } from "notistack";
|
||||
import useMemoValue from "use-memo-value";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { findIndex } from "lodash-es";
|
||||
import { isEmpty, isEqual } from "lodash-es";
|
||||
|
||||
import {
|
||||
createColumnHelper,
|
||||
@@ -17,9 +23,11 @@ import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
import { TABLE_TOOLBAR_HEIGHT } from "@src/components/TableToolbar";
|
||||
import { StyledTable } from "./Styled/StyledTable";
|
||||
import { StyledRow } from "./Styled/StyledRow";
|
||||
import { StyledResizer } from "./Styled/StyledResizer";
|
||||
import ColumnHeaderComponent from "./Column";
|
||||
|
||||
import { IconButton, LinearProgress } from "@mui/material";
|
||||
import { IconButton, LinearProgress, Button } from "@mui/material";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
|
||||
import TableContainer, { OUT_OF_ORDER_MARGIN } from "./TableContainer";
|
||||
import ColumnHeader, { COLUMN_HEADER_HEIGHT } from "./ColumnHeader";
|
||||
@@ -63,8 +71,10 @@ import { useKeyboardNavigation } from "./useKeyboardNavigation";
|
||||
|
||||
export const DEFAULT_ROW_HEIGHT = 41;
|
||||
export const DEFAULT_COL_WIDTH = 150;
|
||||
export const MIN_COL_WIDTH = 32;
|
||||
export const TABLE_PADDING = 16;
|
||||
export const TABLE_GUTTER = 8;
|
||||
export const DEBOUNCE_DELAY = 500;
|
||||
|
||||
declare module "@tanstack/table-core" {
|
||||
interface ColumnMeta<TData, TValue> extends ColumnConfig {}
|
||||
@@ -74,6 +84,8 @@ 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);
|
||||
|
||||
@@ -105,6 +117,9 @@ export default function TableComponent() {
|
||||
columnHelper.accessor(columnConfig.fieldName, {
|
||||
id: columnConfig.fieldName,
|
||||
meta: columnConfig,
|
||||
size: columnConfig.width,
|
||||
enableResizing: columnConfig.resizable !== false,
|
||||
minSize: MIN_COL_WIDTH,
|
||||
// draggable: true,
|
||||
// resizable: true,
|
||||
// frozen: columnConfig.fixed,
|
||||
@@ -166,15 +181,62 @@ export default function TableComponent() {
|
||||
const lastFrozen: string | undefined =
|
||||
columnPinning.left[columnPinning.left.length - 1];
|
||||
|
||||
// Call TanStack Table
|
||||
const table = useReactTable({
|
||||
data: tableRows,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getRowId,
|
||||
columnResizeMode: "onChange",
|
||||
state: { columnVisibility, columnPinning },
|
||||
// debugRows: true,
|
||||
});
|
||||
|
||||
const [columnSizing, setColumnSizing] = useState(
|
||||
table.initialState.columnSizing
|
||||
);
|
||||
table.setOptions((prev) => ({
|
||||
...prev,
|
||||
state: { ...prev.state, columnVisibility, columnPinning, columnSizing },
|
||||
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,
|
||||
]);
|
||||
|
||||
const { rows } = table.getRowModel();
|
||||
const leafColumns = table.getVisibleLeafColumns();
|
||||
// console.log(table, selectedCell);
|
||||
@@ -260,7 +322,7 @@ export default function TableComponent() {
|
||||
setTablePage((p) => p + 1);
|
||||
}
|
||||
},
|
||||
250
|
||||
DEBOUNCE_DELAY
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -326,24 +388,13 @@ export default function TableComponent() {
|
||||
(e.target as HTMLDivElement).focus();
|
||||
}}
|
||||
>
|
||||
{/* <div
|
||||
{...{
|
||||
onMouseDown: header.getResizeHandler(),
|
||||
onTouchStart: header.getResizeHandler(),
|
||||
className: `resizer ${
|
||||
header.column.getIsResizing() ? "isResizing" : ""
|
||||
}`,
|
||||
// style: {
|
||||
// transform:
|
||||
// columnResizeMode === 'onEnd' &&
|
||||
// header.column.getIsResizing()
|
||||
// ? `translateX(${
|
||||
// table.getState().columnSizingInfo.deltaOffset
|
||||
// }px)`
|
||||
// : '',
|
||||
// },
|
||||
}}
|
||||
/> */}
|
||||
{header.column.getCanResize() && (
|
||||
<StyledResizer
|
||||
isResizing={header.column.getIsResizing()}
|
||||
onMouseDown={header.getResizeHandler()}
|
||||
onTouchStart={header.getResizeHandler()}
|
||||
/>
|
||||
)}
|
||||
</ColumnHeaderComponent>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user