diff --git a/src/components/ColumnMenu/ColumnMenu.tsx b/src/components/ColumnMenu/ColumnMenu.tsx
index dec3f6fe..d84bd16d 100644
--- a/src/components/ColumnMenu/ColumnMenu.tsx
+++ b/src/components/ColumnMenu/ColumnMenu.tsx
@@ -192,9 +192,10 @@ export default function ColumnMenu({
setTableSorts(
isSorted && !isAsc ? [] : [{ key: sortKey, direction: "desc" }]
);
- if (!isSorted || isAsc) {
- triggerSaveTableSorts([{ key: sortKey, direction: "desc" }]);
- }
+
+ triggerSaveTableSorts(
+ isSorted && !isAsc ? [] : [{ key: sortKey, direction: "desc" }]
+ );
handleClose();
},
active: isSorted && !isAsc,
@@ -209,9 +210,9 @@ export default function ColumnMenu({
setTableSorts(
isSorted && isAsc ? [] : [{ key: sortKey, direction: "asc" }]
);
- if (!isSorted || !isAsc) {
- triggerSaveTableSorts([{ key: sortKey, direction: "asc" }]);
- }
+ triggerSaveTableSorts(
+ isSorted && isAsc ? [] : [{ key: sortKey, direction: "asc" }]
+ );
handleClose();
},
active: isSorted && isAsc,
diff --git a/src/components/Table/ColumnHeader/ColumnHeaderSort.tsx b/src/components/Table/ColumnHeader/ColumnHeaderSort.tsx
index 45e924f0..2478ccbd 100644
--- a/src/components/Table/ColumnHeader/ColumnHeaderSort.tsx
+++ b/src/components/Table/ColumnHeader/ColumnHeaderSort.tsx
@@ -38,14 +38,12 @@ export const ColumnHeaderSort = memo(function ColumnHeaderSort({
const triggerSaveTableSorts = useSaveTableSorts(canEditColumns);
const handleSortClick = () => {
- if (nextSort === "none") setTableSorts([]);
- else setTableSorts([{ key: sortKey, direction: nextSort }]);
- triggerSaveTableSorts([
- {
- key: sortKey,
- direction: nextSort === "none" ? "asc" : nextSort,
- },
- ]);
+ setTableSorts(
+ nextSort === "none" ? [] : [{ key: sortKey, direction: nextSort }]
+ );
+ triggerSaveTableSorts(
+ nextSort === "none" ? [] : [{ key: sortKey, direction: nextSort }]
+ );
};
return (
diff --git a/src/components/TableToolbar/Sort/Sort.tsx b/src/components/TableToolbar/Sort/Sort.tsx
new file mode 100644
index 00000000..9d6d362e
--- /dev/null
+++ b/src/components/TableToolbar/Sort/Sort.tsx
@@ -0,0 +1,135 @@
+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 (
+
+ {({ handleClose }) => (
+
+
+ {
+ setTableSorts(
+ value === null
+ ? []
+ : [{ key: value, direction: tableSorts[0].direction }]
+ );
+ triggerSaveTableSorts(
+ value === null
+ ? []
+ : [{ key: value, direction: tableSorts[0].direction }]
+ );
+ }}
+ />
+
+
+
+ {
+ setTableSorts([
+ {
+ key: tableSorts[0].key,
+ direction: e.target.value === "asc" ? "asc" : "desc",
+ },
+ ]);
+ triggerSaveTableSorts([
+ {
+ key: tableSorts[0].key,
+ direction: e.target.value === "asc" ? "asc" : "desc",
+ },
+ ]);
+ }}
+ >
+
+
+
+
+
+ {
+ setTableSorts([]);
+ triggerSaveTableSorts([]);
+ }}
+ sx={{
+ "&:hover, &:focus": {
+ color: "error.main",
+ backgroundColor: (theme) =>
+ alpha(
+ theme.palette.error.main,
+ theme.palette.action.hoverOpacity * 2
+ ),
+ },
+ }}
+ >
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/TableToolbar/Sort/SortPopover.tsx b/src/components/TableToolbar/Sort/SortPopover.tsx
new file mode 100644
index 00000000..11d86b44
--- /dev/null
+++ b/src/components/TableToolbar/Sort/SortPopover.tsx
@@ -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(null);
+ const popoverId = tableSortPopoverState ? "sort-popover" : undefined;
+ const handleClose = () => setTableSortPopoverState(false);
+
+ const [tableSorts] = useAtom(tableSortsAtom, tableScope);
+
+ return (
+ <>
+ setTableSortPopoverState(true)}
+ active={true}
+ startIcon={
+
+ theme.transitions.create("transform", {
+ duration: theme.transitions.duration.short,
+ }),
+
+ transform:
+ tableSorts[0].direction === "asc" ? "rotate(180deg)" : "none",
+ }}
+ />
+ }
+ aria-describedby={popoverId}
+ >
+ Sorted: {tableSorts[0].key}
+
+
+
+ {children({ handleClose })}
+
+ >
+ );
+}
diff --git a/src/components/TableToolbar/Sort/index.tsx b/src/components/TableToolbar/Sort/index.tsx
new file mode 100644
index 00000000..a67b91b2
--- /dev/null
+++ b/src/components/TableToolbar/Sort/index.tsx
@@ -0,0 +1,2 @@
+export * from "./Sort";
+export { default } from "./Sort";
diff --git a/src/components/TableToolbar/TableToolbar.tsx b/src/components/TableToolbar/TableToolbar.tsx
index abd448bd..5845e185 100644
--- a/src/components/TableToolbar/TableToolbar.tsx
+++ b/src/components/TableToolbar/TableToolbar.tsx
@@ -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({
)}
+ {tableSorts.length > 0 && tableSettings.isCollection !== false && (
+ }>
+
+
+ )}
{/* Spacer */}
@@ -134,22 +144,20 @@ export default function TableToolbar({
)
)}
-
{(!projectSettings.exporterRoles ||
projectSettings.exporterRoles.length === 0 ||
userRoles.some((role) =>
projectSettings.exporterRoles?.includes(role)
)) && (
- }>
- openTableModal("export")}
- icon={}
- disabled={disabledTools.includes("export")}
- />
+ }>
+ openTableModal("export")}
+ icon={}
+ disabled={disabledTools.includes("export")}
+ />
)}
-
{userRoles.includes("ADMIN") && (
<>
{/* Spacer */}