mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-28 16:06:41 +01:00
add sort button
This commit is contained in:
133
src/components/TableToolbar/Sort/Sort.tsx
Normal file
133
src/components/TableToolbar/Sort/Sort.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
import {
|
||||
Grid,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Stack,
|
||||
TextField,
|
||||
Typography,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
|
||||
|
||||
import {
|
||||
tableColumnsOrderedAtom,
|
||||
tableScope,
|
||||
tableSettingsAtom,
|
||||
tableSortsAtom,
|
||||
} from "@src/atoms/tableScope";
|
||||
import SortPopover from "./SortPopover";
|
||||
import ColumnSelect from "@src/components/Table/ColumnSelect";
|
||||
|
||||
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
||||
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
|
||||
import { projectScope, userRolesAtom } from "@src/atoms/projectScope";
|
||||
import useSaveTableSorts from "@src/components/Table/ColumnHeader/useSaveTableSorts";
|
||||
|
||||
export default function Sort() {
|
||||
const [userRoles] = useAtom(userRolesAtom, projectScope);
|
||||
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
|
||||
|
||||
const canEditColumns = Boolean(
|
||||
userRoles.includes("ADMIN") ||
|
||||
tableSettings.modifiableBy?.some((r) => userRoles.includes(r))
|
||||
);
|
||||
|
||||
const [tableSorts, setTableSorts] = useAtom(tableSortsAtom, tableScope);
|
||||
const triggerSaveTableSorts = useSaveTableSorts(canEditColumns);
|
||||
|
||||
const [tableColumnsOrdered] = useAtom(tableColumnsOrderedAtom, tableScope);
|
||||
|
||||
const sortColumns = tableColumnsOrdered.map(({ key, name, type, index }) => ({
|
||||
value: key,
|
||||
label: name,
|
||||
type,
|
||||
index,
|
||||
}));
|
||||
|
||||
return (
|
||||
<SortPopover>
|
||||
{({ handleClose }) => (
|
||||
<Grid container spacing={2} sx={{ p: 3 }}>
|
||||
<Grid item xs={5.5}>
|
||||
<ColumnSelect
|
||||
multiple={false}
|
||||
label="Column"
|
||||
options={sortColumns}
|
||||
value={tableSorts[0].key}
|
||||
onChange={(value: string | null) => {
|
||||
if (value) {
|
||||
setTableSorts([
|
||||
{ key: value, direction: tableSorts[0].direction },
|
||||
]);
|
||||
|
||||
triggerSaveTableSorts([
|
||||
{ key: value, direction: tableSorts[0].direction },
|
||||
]);
|
||||
} else {
|
||||
setTableSorts([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={5.5}>
|
||||
<TextField
|
||||
label="Sort"
|
||||
select
|
||||
variant="filled"
|
||||
fullWidth
|
||||
value={tableSorts[0].direction}
|
||||
onChange={(e) => {
|
||||
setTableSorts([
|
||||
{
|
||||
key: tableSorts[0].key,
|
||||
direction: e.target.value === "asc" ? "asc" : "desc",
|
||||
},
|
||||
]);
|
||||
triggerSaveTableSorts([
|
||||
{
|
||||
key: tableSorts[0].key,
|
||||
direction: e.target.value === "asc" ? "asc" : "desc",
|
||||
},
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<MenuItem key="asc" value="asc">
|
||||
<Stack direction="row" gap={1} alignItems="center">
|
||||
<ArrowUpwardIcon />
|
||||
<Typography>Sort ascending</Typography>
|
||||
</Stack>
|
||||
</MenuItem>
|
||||
<MenuItem key="desc" value="desc">
|
||||
<Stack direction="row" gap={1} alignItems="center">
|
||||
<ArrowDownwardIcon />
|
||||
<Typography>Sort descending</Typography>
|
||||
</Stack>
|
||||
</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={1} alignSelf="flex-end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setTableSorts([])}
|
||||
sx={{
|
||||
"&:hover, &:focus": {
|
||||
color: "error.main",
|
||||
backgroundColor: (theme) =>
|
||||
alpha(
|
||||
theme.palette.error.main,
|
||||
theme.palette.action.hoverOpacity * 2
|
||||
),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</SortPopover>
|
||||
);
|
||||
}
|
||||
67
src/components/TableToolbar/Sort/SortPopover.tsx
Normal file
67
src/components/TableToolbar/Sort/SortPopover.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
import { Popover } from "@mui/material";
|
||||
|
||||
import ButtonWithStatus from "@src/components/ButtonWithStatus";
|
||||
|
||||
import { tableScope, tableSortsAtom } from "@src/atoms/tableScope";
|
||||
|
||||
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
||||
|
||||
export interface ISortPopoverProps {
|
||||
children: (props: { handleClose: () => void }) => React.ReactNode;
|
||||
}
|
||||
|
||||
export default function SortPopover({ children }: ISortPopoverProps) {
|
||||
const [tableSortPopoverState, setTableSortPopoverState] = useState(false);
|
||||
|
||||
const anchorEl = useRef<HTMLButtonElement>(null);
|
||||
const popoverId = tableSortPopoverState ? "sort-popover" : undefined;
|
||||
const handleClose = () => setTableSortPopoverState(false);
|
||||
|
||||
const [tableSorts] = useAtom(tableSortsAtom, tableScope);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ButtonWithStatus
|
||||
ref={anchorEl}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => setTableSortPopoverState(true)}
|
||||
active={true}
|
||||
startIcon={
|
||||
<ArrowDownwardIcon
|
||||
sx={{
|
||||
transition: (theme) =>
|
||||
theme.transitions.create("transform", {
|
||||
duration: theme.transitions.duration.short,
|
||||
}),
|
||||
|
||||
transform:
|
||||
tableSorts[0].direction === "asc" ? "rotate(180deg)" : "none",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
aria-describedby={popoverId}
|
||||
>
|
||||
Sorted: {tableSorts[0].key}
|
||||
</ButtonWithStatus>
|
||||
|
||||
<Popover
|
||||
id={popoverId}
|
||||
open={tableSortPopoverState}
|
||||
anchorEl={anchorEl.current}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
||||
sx={{
|
||||
"& .MuiPaper-root": { width: 640 },
|
||||
"& .content": { p: 3 },
|
||||
}}
|
||||
>
|
||||
{children({ handleClose })}
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
2
src/components/TableToolbar/Sort/index.tsx
Normal file
2
src/components/TableToolbar/Sort/index.tsx
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./Sort";
|
||||
export { default } from "./Sort";
|
||||
@@ -32,11 +32,15 @@ import {
|
||||
tableSettingsAtom,
|
||||
tableSchemaAtom,
|
||||
tableModalAtom,
|
||||
tableSortsAtom,
|
||||
} from "@src/atoms/tableScope";
|
||||
import { FieldType } from "@src/constants/fields";
|
||||
import { TableToolsType } from "@src/types/table";
|
||||
import FilterIcon from "@mui/icons-material/FilterList";
|
||||
|
||||
// prettier-ignore
|
||||
const Sort = lazy(() => import("./Sort" /* webpackChunkName: "Filters" */));
|
||||
|
||||
// prettier-ignore
|
||||
const Filters = lazy(() => import("./Filters" /* webpackChunkName: "Filters" */));
|
||||
// prettier-ignore
|
||||
@@ -62,6 +66,7 @@ export default function TableToolbar({
|
||||
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
|
||||
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
|
||||
const openTableModal = useSetAtom(tableModalAtom, tableScope);
|
||||
const [tableSorts] = useAtom(tableSortsAtom, tableScope);
|
||||
const hasDerivatives =
|
||||
Object.values(tableSchema.columns ?? {}).filter(
|
||||
(column) => column.type === FieldType.derivative
|
||||
@@ -116,6 +121,11 @@ export default function TableToolbar({
|
||||
<Filters />
|
||||
</Suspense>
|
||||
)}
|
||||
{tableSorts.length > 0 && tableSettings.isCollection !== false && (
|
||||
<Suspense fallback={<ButtonSkeleton />}>
|
||||
<Sort />
|
||||
</Suspense>
|
||||
)}
|
||||
<div /> {/* Spacer */}
|
||||
<LoadedRowsStatus />
|
||||
<div style={{ flexGrow: 1, minWidth: 64 }} />
|
||||
@@ -134,22 +144,20 @@ export default function TableToolbar({
|
||||
</Suspense>
|
||||
)
|
||||
)}
|
||||
|
||||
{(!projectSettings.exporterRoles ||
|
||||
projectSettings.exporterRoles.length === 0 ||
|
||||
userRoles.some((role) =>
|
||||
projectSettings.exporterRoles?.includes(role)
|
||||
)) && (
|
||||
<Suspense fallback={<ButtonSkeleton />}>
|
||||
<TableToolbarButton
|
||||
title="Export/Download"
|
||||
onClick={() => openTableModal("export")}
|
||||
icon={<ExportIcon />}
|
||||
disabled={disabledTools.includes("export")}
|
||||
/>
|
||||
<Suspense fallback={<ButtonSkeleton />}>
|
||||
<TableToolbarButton
|
||||
title="Export/Download"
|
||||
onClick={() => openTableModal("export")}
|
||||
icon={<ExportIcon />}
|
||||
disabled={disabledTools.includes("export")}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<>
|
||||
<div /> {/* Spacer */}
|
||||
|
||||
Reference in New Issue
Block a user