mirror of
https://github.com/rowyio/rowy.git
synced 2026-07-12 13:28:48 +02:00
Merge branch 'canary-react-data-grid' of https://github.com/AntlerVC/firetable into canary-react-data-grid
This commit is contained in:
@@ -37,10 +37,9 @@ const useStyles = makeStyles((theme) =>
|
||||
|
||||
// divider: { margin: theme.spacing(1, 2) },
|
||||
|
||||
secondaryAction: { pointerEvents: "none" },
|
||||
secondaryIcon: {
|
||||
display: "block",
|
||||
pointerEvents: "none",
|
||||
|
||||
color: theme.palette.action.active,
|
||||
},
|
||||
})
|
||||
@@ -114,7 +113,7 @@ export default function UserMenu(props: IconButtonProps) {
|
||||
|
||||
<MenuItem onClick={handleToggleTheme}>
|
||||
Dark Theme
|
||||
<ListItemSecondaryAction>
|
||||
<ListItemSecondaryAction className={classes.secondaryAction}>
|
||||
{theme === "light" ? (
|
||||
<CheckBoxOutlineBlankIcon className={classes.secondaryIcon} />
|
||||
) : (
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useStyles } from "./useStyles";
|
||||
import { useFiretableContext } from "contexts/firetableContext";
|
||||
import { FieldType } from "constants/fields";
|
||||
import useDoc from "hooks/useDoc";
|
||||
|
||||
export const DRAWER_WIDTH = 600;
|
||||
export const DRAWER_COLLAPSED_WIDTH = 36;
|
||||
|
||||
|
||||
@@ -1,84 +1,118 @@
|
||||
import React from "react";
|
||||
import { Theme, createStyles, makeStyles } from "@material-ui/core/styles";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Tooltip from "@material-ui/core/Tooltip";
|
||||
import _find from "lodash/find";
|
||||
|
||||
import {
|
||||
createStyles,
|
||||
makeStyles,
|
||||
fade,
|
||||
Grow,
|
||||
Paper,
|
||||
Grid,
|
||||
Tooltip,
|
||||
IconButton,
|
||||
Typography,
|
||||
TextField,
|
||||
MenuItem,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
||||
import Grow from "@material-ui/core/Grow";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Popper from "@material-ui/core/Popper";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import MenuList from "@material-ui/core/MenuList";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import CopyCellsIcon from "assets/icons/CopyCells";
|
||||
import ClearSelectionIcon from "@material-ui/icons/IndeterminateCheckBox";
|
||||
import DeleteIcon from "@material-ui/icons/DeleteForever";
|
||||
import DropdownIcon from "@material-ui/icons/ArrowDropDown";
|
||||
import ArrowDropUpIcon from "@material-ui/icons/ArrowDropUp";
|
||||
|
||||
import { useConfirmation } from "components/ConfirmationDialog/Context";
|
||||
import { useFiretableContext } from "contexts/firetableContext";
|
||||
import { useSnackContext } from "contexts/snackContext";
|
||||
import { sanitiseRowData } from "utils/fns";
|
||||
import { formatPath, asyncForEach } from "../../../utils/fns";
|
||||
import { sanitiseRowData, formatPath, asyncForEach } from "utils/fns";
|
||||
import { cloudFunction } from "firebase/callables";
|
||||
import _find from "lodash/find";
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
|
||||
const useStyles = makeStyles((theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
position: "absolute",
|
||||
bottom: 10,
|
||||
right: 30,
|
||||
position: "fixed",
|
||||
bottom: theme.spacing(2),
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
},
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
|
||||
cornerRadius: 32,
|
||||
paper: {
|
||||
height: 64,
|
||||
borderRadius: 32,
|
||||
padding: theme.spacing(0, 1),
|
||||
[theme.breakpoints.up("lg")]: { paddingRight: theme.spacing(2) },
|
||||
|
||||
zIndex: theme.zIndex.modal,
|
||||
|
||||
backgroundColor:
|
||||
theme.palette.type === "light"
|
||||
? theme.palette.background.default
|
||||
: theme.palette.background.elevation?.[12] ??
|
||||
theme.palette.background.default,
|
||||
|
||||
width: 470,
|
||||
maxWidth: "100vw",
|
||||
overflowX: "auto",
|
||||
},
|
||||
|
||||
grid: {
|
||||
height: "100%",
|
||||
marginTop: 0,
|
||||
marginBottom: 0,
|
||||
},
|
||||
spacer: { width: theme.spacing(2) },
|
||||
|
||||
selectedContainer: {
|
||||
flexBasis: 206,
|
||||
flexShrink: 0,
|
||||
},
|
||||
selected: {
|
||||
color: theme.palette.text.disabled,
|
||||
fontFeatureSettings: '"tnum"',
|
||||
userSelect: "none",
|
||||
|
||||
display: "inline-block",
|
||||
marginRight: theme.spacing(1),
|
||||
minWidth: 150,
|
||||
},
|
||||
|
||||
dropdown: {
|
||||
minWidth: 120,
|
||||
margin: 0,
|
||||
},
|
||||
inputBaseRoot: {
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor:
|
||||
theme.palette.type === "dark"
|
||||
? fade(theme.palette.text.primary, 0.06)
|
||||
: undefined,
|
||||
},
|
||||
dropdownLabel: {
|
||||
left: theme.spacing(1.5),
|
||||
top: "50%",
|
||||
transform: "translateY(-50%) !important",
|
||||
|
||||
...theme.typography.body1,
|
||||
},
|
||||
dropdownLabelFocused: {
|
||||
"$dropdownLabel&": { color: theme.palette.text.primary },
|
||||
},
|
||||
select: {
|
||||
paddingTop: "6px !important",
|
||||
paddingBottom: "7px !important",
|
||||
},
|
||||
dropdownMenu: { marginTop: theme.spacing(-3) },
|
||||
})
|
||||
);
|
||||
|
||||
export default function BulkActions({ selectedRows, columns, clearSelection }) {
|
||||
const { requestConfirmation } = useConfirmation();
|
||||
const { tableActions, tableState } = useFiretableContext();
|
||||
const snack = useSnackContext();
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef<HTMLButtonElement>(null);
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
const { tableActions, tableState } = useFiretableContext();
|
||||
|
||||
const handleClose = (event: React.MouseEvent<EventTarget>) => {
|
||||
if (
|
||||
anchorRef.current &&
|
||||
anchorRef.current.contains(event.target as HTMLElement)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const { requestConfirmation } = useConfirmation();
|
||||
const snack = useSnackContext();
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
function handleListKeyDown(event: React.KeyboardEvent) {
|
||||
if (event.key === "Tab") {
|
||||
event.preventDefault();
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
// return focus to the button when we transitioned from !open -> open
|
||||
const prevOpen = React.useRef(open);
|
||||
React.useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current!.focus();
|
||||
}
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
const actionColumns = columns
|
||||
const actionColumns: { name: string; key: string; config: any }[] = columns
|
||||
.filter((column) => column.type === "ACTION")
|
||||
.map((column) => ({
|
||||
name: column.name,
|
||||
@@ -109,7 +143,10 @@ export default function BulkActions({ selectedRows, columns, clearSelection }) {
|
||||
clearSelection();
|
||||
};
|
||||
|
||||
const executeAction = async (actionColumn, actionType) => {
|
||||
const executeAction = async (key: string, actionType: string) => {
|
||||
const actionColumn = _find(actionColumns, { key });
|
||||
if (!actionColumn) return;
|
||||
|
||||
console.log({ actionColumn, selectedRows, actionType });
|
||||
const callableName = actionColumn.config.callableName ?? "actionScript";
|
||||
|
||||
@@ -148,109 +185,121 @@ export default function BulkActions({ selectedRows, columns, clearSelection }) {
|
||||
);
|
||||
});
|
||||
};
|
||||
const numberOfSelectedRows = selectedRows.length;
|
||||
if (numberOfSelectedRows === 0) return null;
|
||||
|
||||
const numSelected = selectedRows.length;
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Paper elevation={5} className={classes.paper}>
|
||||
<Grid direction="row" container alignItems="center">
|
||||
<IconButton
|
||||
size="medium"
|
||||
color="inherit"
|
||||
onClick={clearSelection}
|
||||
aria-label="Duplicate selected rows"
|
||||
<Grow in={numSelected > 0}>
|
||||
<Paper elevation={8} className={classes.paper}>
|
||||
<Grid
|
||||
container
|
||||
alignItems="center"
|
||||
wrap="nowrap"
|
||||
className={classes.grid}
|
||||
>
|
||||
<ClearSelectionIcon />
|
||||
</IconButton>
|
||||
<Typography variant="overline">
|
||||
{numberOfSelectedRows} rows selected
|
||||
</Typography>
|
||||
<Grid item className={classes.selectedContainer}>
|
||||
<Tooltip title="Clear selection">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
onClick={clearSelection}
|
||||
aria-label="Clear selection"
|
||||
>
|
||||
<ClearSelectionIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Grid item>
|
||||
<div>
|
||||
<Button
|
||||
color="inherit"
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? "menu-list-grow" : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
<Typography variant="overline" className={classes.selected}>
|
||||
{numSelected} row{numSelected !== 1 && "s"} selected
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item className={classes.spacer} />
|
||||
|
||||
<Grid item>
|
||||
<TextField
|
||||
select
|
||||
variant="filled"
|
||||
className={classes.dropdown}
|
||||
onChange={(event) => executeAction(event.target.value, "run")}
|
||||
margin="dense"
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
classes: { root: classes.inputBaseRoot },
|
||||
}}
|
||||
InputLabelProps={{
|
||||
classes: {
|
||||
root: classes.dropdownLabel,
|
||||
focused: classes.dropdownLabelFocused,
|
||||
},
|
||||
}}
|
||||
SelectProps={{
|
||||
classes: { root: classes.select },
|
||||
displayEmpty: true,
|
||||
MenuProps: {
|
||||
getContentAnchorEl: null,
|
||||
anchorOrigin: { vertical: "top", horizontal: "left" },
|
||||
transformOrigin: { vertical: "bottom", horizontal: "left" },
|
||||
classes: { paper: classes.dropdownMenu },
|
||||
},
|
||||
IconComponent: ArrowDropUpIcon,
|
||||
}}
|
||||
label={`${actionColumns.length} action${
|
||||
actionColumns.length !== 1 ? "s" : ""
|
||||
}`}
|
||||
>
|
||||
{actionColumns.length} actions <DropdownIcon />
|
||||
</Button>
|
||||
<Popper
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin:
|
||||
placement === "bottom" ? "center top" : "center bottom",
|
||||
}}
|
||||
>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList
|
||||
autoFocusItem={open}
|
||||
id="menu-list-grow"
|
||||
onKeyDown={handleListKeyDown}
|
||||
>
|
||||
{actionColumns.map((action) => (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
executeAction(action, "run");
|
||||
handleClose(e);
|
||||
}}
|
||||
>
|
||||
{action.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</div>
|
||||
{actionColumns.map((action) => (
|
||||
<MenuItem value={action.key}>{action.name}</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
|
||||
<Grid item className={classes.spacer} />
|
||||
|
||||
<Grid item>
|
||||
<Tooltip title="Duplicate rows">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
requestConfirmation({
|
||||
title: "Duplicate Rows?",
|
||||
body: `Are you sure you want to duplicate the ${numSelected} selected row${
|
||||
numSelected !== 1 ? "s" : ""
|
||||
}?`,
|
||||
confirm: "Duplicate Rows",
|
||||
handleConfirm: handleDuplicate,
|
||||
});
|
||||
}}
|
||||
aria-label="Duplicate selected rows"
|
||||
>
|
||||
<CopyCellsIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<Tooltip title="Delete rows">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
requestConfirmation({
|
||||
title: "Delete Rows?",
|
||||
body: `Are you sure you want to delete the ${numSelected} select row${
|
||||
numSelected !== 1 ? "s" : ""
|
||||
}?`,
|
||||
confirm: "Delete Rows",
|
||||
handleConfirm: handleDelete,
|
||||
});
|
||||
}}
|
||||
aria-label="Delete selected rows"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<IconButton
|
||||
size="medium"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
requestConfirmation({
|
||||
title: "Duplicate Rows",
|
||||
body: `Are you sure you want to create duplicate of the ${numberOfSelectedRows} select rows?`,
|
||||
confirm: "Duplicate",
|
||||
handleConfirm: handleDuplicate,
|
||||
});
|
||||
}}
|
||||
aria-label="Duplicate selected rows"
|
||||
>
|
||||
<CopyCellsIcon />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
size="medium"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
requestConfirmation({
|
||||
title: "Delete Rows",
|
||||
body: `Are you sure you want to delete the ${numberOfSelectedRows} select rows?`,
|
||||
confirm: "Delete",
|
||||
handleConfirm: handleDelete,
|
||||
});
|
||||
}}
|
||||
aria-label="Delete selected rows"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Grow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ const useStyles = makeStyles((theme) =>
|
||||
|
||||
spacer: { minWidth: theme.spacing(8) },
|
||||
|
||||
formControl: {
|
||||
dropdown: {
|
||||
minWidth: 120,
|
||||
margin: theme.spacing(0, 0, 0, -1),
|
||||
},
|
||||
@@ -139,7 +139,7 @@ export default function TableHeader({
|
||||
<TextField
|
||||
select
|
||||
variant="filled"
|
||||
className={classes.formControl}
|
||||
className={classes.dropdown}
|
||||
value={rowHeight ?? 43}
|
||||
onChange={(event) => {
|
||||
updateConfig("rowHeight", event.target.value);
|
||||
|
||||
@@ -11,6 +11,8 @@ export const useStyles = makeStyles((theme) =>
|
||||
height: `calc(100vh - ${APP_BAR_HEIGHT}px)`,
|
||||
|
||||
"& > .rdg": { flex: 1 },
|
||||
|
||||
[theme.breakpoints.down("sm")]: { width: "100%" },
|
||||
},
|
||||
|
||||
loadingContainer: {
|
||||
|
||||
Reference in New Issue
Block a user