mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
294 lines
7.6 KiB
TypeScript
294 lines
7.6 KiB
TypeScript
import React from "react";
|
|
import clsx from "clsx";
|
|
import { HeaderRendererProps } from "react-data-grid";
|
|
import { useDrag, useDrop, DragObjectWithType } from "react-dnd";
|
|
import { useCombinedRefs } from "react-data-grid/lib/hooks";
|
|
|
|
import {
|
|
makeStyles,
|
|
createStyles,
|
|
fade,
|
|
Tooltip,
|
|
Fade,
|
|
Grid,
|
|
IconButton,
|
|
Typography,
|
|
} from "@material-ui/core";
|
|
import SortDescIcon from "@material-ui/icons/ArrowDownward";
|
|
import DropdownIcon from "@material-ui/icons/ArrowDropDownCircle";
|
|
|
|
import { FieldType } from "constants/fields";
|
|
import { getFieldProp } from "components/fields";
|
|
import { useFiretableContext } from "contexts/FiretableContext";
|
|
import { FiretableOrderBy } from "hooks/useFiretable";
|
|
|
|
const useStyles = makeStyles((theme) =>
|
|
createStyles({
|
|
root: {
|
|
height: "100%",
|
|
"& svg, & button": { display: "block" },
|
|
|
|
color: theme.palette.text.secondary,
|
|
transition: theme.transitions.create("color", {
|
|
duration: theme.transitions.duration.short,
|
|
}),
|
|
"&:hover": { color: theme.palette.text.primary },
|
|
|
|
cursor: "move",
|
|
|
|
margin: theme.spacing(0, -1.5),
|
|
padding: theme.spacing(0, 1.5),
|
|
width: `calc(100% + ${theme.spacing(1.5) * 2}px)`,
|
|
},
|
|
isDragging: { opacity: 0.5 },
|
|
isOver: {
|
|
backgroundColor: fade(
|
|
theme.palette.primary.main,
|
|
theme.palette.action.focusOpacity
|
|
),
|
|
color: theme.palette.primary.main,
|
|
},
|
|
|
|
columnNameContainer: {
|
|
flexShrink: 1,
|
|
overflow: "hidden",
|
|
margin: theme.spacing(0, 0.5),
|
|
marginRight: -26,
|
|
},
|
|
columnName: { lineHeight: "44px" },
|
|
|
|
columnNameTooltip: {
|
|
background: theme.palette.background.default,
|
|
color: theme.palette.text.primary,
|
|
|
|
margin: "-43px 0 0",
|
|
padding: theme.spacing(0, 1.5, 0, 0),
|
|
|
|
"& *": { lineHeight: "42px" },
|
|
},
|
|
|
|
sortIconContainer: {
|
|
backgroundColor: theme.palette.background.default,
|
|
width: 30,
|
|
height: 30,
|
|
|
|
opacity: 0,
|
|
transition: theme.transitions.create("opacity", {
|
|
duration: theme.transitions.duration.shortest,
|
|
}),
|
|
"$root:hover &": { opacity: 1 },
|
|
},
|
|
sortIconContainerSorted: { opacity: 1 },
|
|
|
|
sortIcon: {
|
|
transition: theme.transitions.create(["background-color", "transform"], {
|
|
duration: theme.transitions.duration.short,
|
|
}),
|
|
},
|
|
sortIconAsc: {
|
|
transform: "rotate(180deg)",
|
|
},
|
|
|
|
dropdownButton: {
|
|
opacity: 0.5,
|
|
transition: theme.transitions.create("opacity", {
|
|
duration: theme.transitions.duration.short,
|
|
}),
|
|
"&:hover": {
|
|
backgroundColor: "transparent",
|
|
opacity: 1,
|
|
},
|
|
|
|
padding: 0,
|
|
},
|
|
})
|
|
);
|
|
|
|
interface ColumnDragObject extends DragObjectWithType {
|
|
key: string;
|
|
}
|
|
|
|
export default function DraggableHeaderRenderer<R>({
|
|
column,
|
|
}: HeaderRendererProps<R> & {
|
|
onColumnsReorder: (sourceKey: string, targetKey: string) => void;
|
|
}) {
|
|
const classes = useStyles();
|
|
|
|
const {
|
|
tableState,
|
|
tableActions,
|
|
userClaims,
|
|
columnMenuRef,
|
|
} = useFiretableContext();
|
|
const [{ isDragging }, drag] = useDrag({
|
|
item: { key: column.key, type: "COLUMN_DRAG" },
|
|
collect: (monitor) => ({
|
|
isDragging: !!monitor.isDragging(),
|
|
}),
|
|
});
|
|
|
|
const [{ isOver }, drop] = useDrop({
|
|
accept: "COLUMN_DRAG",
|
|
drop({ key, type }: ColumnDragObject) {
|
|
if (type === "COLUMN_DRAG") {
|
|
// onColumnsReorder(key, props.column.key);
|
|
tableActions?.column.reorder(key, column.key);
|
|
}
|
|
},
|
|
collect: (monitor) => ({
|
|
isOver: !!monitor.isOver(),
|
|
canDrop: !!monitor.canDrop(),
|
|
}),
|
|
});
|
|
|
|
const headerRef = useCombinedRefs(drag, drop);
|
|
if (!columnMenuRef || !tableState || !tableActions) return null;
|
|
const { orderBy } = tableState;
|
|
|
|
const handleClick = (
|
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
|
) =>
|
|
columnMenuRef?.current?.setSelectedColumnHeader({
|
|
column,
|
|
anchorEl: event.currentTarget,
|
|
});
|
|
|
|
const isSorted = orderBy?.[0]?.key === (column.key as string);
|
|
const isAsc = isSorted && orderBy?.[0]?.direction === "asc";
|
|
|
|
const handleSortClick = () => {
|
|
if (isAsc) {
|
|
const ordering: FiretableOrderBy = [
|
|
{ key: column.key as string, direction: "desc" },
|
|
];
|
|
|
|
tableActions.table.orderBy(ordering);
|
|
} else {
|
|
const ordering: FiretableOrderBy = [
|
|
{ key: column.key as string, direction: "asc" },
|
|
];
|
|
tableActions.table.orderBy(ordering);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Grid
|
|
ref={headerRef}
|
|
container
|
|
className={clsx(
|
|
classes.root,
|
|
isDragging && classes.isDragging,
|
|
isOver && !isDragging && classes.isOver
|
|
)}
|
|
alignItems="center"
|
|
wrap="nowrap"
|
|
>
|
|
<Tooltip
|
|
title={
|
|
<>
|
|
<Typography variant="caption" component="p">
|
|
{column.key as string}
|
|
</Typography>
|
|
<Typography variant="body2" component="p">
|
|
<small>(Click to copy)</small>
|
|
</Typography>
|
|
</>
|
|
}
|
|
enterDelay={1000}
|
|
placement="bottom-start"
|
|
>
|
|
<Grid
|
|
item
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(column.key as string);
|
|
}}
|
|
>
|
|
{getFieldProp("icon", (column as any).type)}
|
|
</Grid>
|
|
</Tooltip>
|
|
|
|
<Grid item xs className={classes.columnNameContainer}>
|
|
<Tooltip
|
|
title={<Typography variant="caption">{column.name}</Typography>}
|
|
enterDelay={1000}
|
|
placement="bottom-start"
|
|
PopperProps={{
|
|
modifiers: {
|
|
flip: { enabled: false },
|
|
preventOverflow: {
|
|
enabled: false,
|
|
boundariesElement: "scrollParent",
|
|
},
|
|
hide: { enabled: false },
|
|
},
|
|
}}
|
|
TransitionComponent={Fade}
|
|
classes={{ tooltip: classes.columnNameTooltip }}
|
|
>
|
|
<Typography
|
|
variant="caption"
|
|
noWrap
|
|
className={classes.columnName}
|
|
component="div"
|
|
color="inherit"
|
|
>
|
|
{column.name}
|
|
</Typography>
|
|
</Tooltip>
|
|
</Grid>
|
|
|
|
{(column as any).type !== FieldType.id && (
|
|
<Grid
|
|
item
|
|
className={clsx(
|
|
classes.sortIconContainer,
|
|
isSorted && classes.sortIconContainerSorted
|
|
)}
|
|
>
|
|
<IconButton
|
|
disableFocusRipple={true}
|
|
size="small"
|
|
onClick={handleSortClick}
|
|
color="inherit"
|
|
aria-label={`Sort by ${isAsc ? "descending" : "ascending"}`}
|
|
className={clsx(classes.sortIcon, isAsc && classes.sortIconAsc)}
|
|
>
|
|
<SortDescIcon />
|
|
</IconButton>
|
|
</Grid>
|
|
)}
|
|
|
|
{(userClaims?.roles?.includes("ADMIN") ||
|
|
(userClaims?.roles?.includes("OPS") &&
|
|
[FieldType.multiSelect, FieldType.singleSelect].includes(
|
|
(column as any).type
|
|
))) && (
|
|
<Grid item>
|
|
<IconButton
|
|
size="small"
|
|
className={classes.dropdownButton}
|
|
aria-label={`Show ${column.name} column dropdown`}
|
|
color="inherit"
|
|
onClick={handleClick}
|
|
>
|
|
<DropdownIcon />
|
|
</IconButton>
|
|
</Grid>
|
|
)}
|
|
</Grid>
|
|
);
|
|
// return (
|
|
// <div
|
|
// ref={useCombinedRefs(drag, drop)}
|
|
// style={{
|
|
// opacity: isDragging ? 0.5 : 1,
|
|
// backgroundColor: isOver ? '#ececec' : 'inherit',
|
|
// cursor: 'move'
|
|
// }}
|
|
// >
|
|
// {props.column.name}
|
|
// </div>
|
|
// );
|
|
}
|