From d3ef807af5307246d9e8fd4f4aee53b1f2109f28 Mon Sep 17 00:00:00 2001 From: Eunjae Lee Date: Mon, 27 Jan 2025 11:20:59 +0100 Subject: [PATCH] feat: add command list to DataTable column header (#18867) * feat: add command list to DataTable column header * disable hiding and sorting for some columns * disable hiding & sorting --- apps/web/public/icons/sprite.svg | 4 + apps/web/public/static/locales/en/common.json | 3 + .../data-table/components/DataTable.tsx | 122 ++++++++++++++---- .../components/GroupTeamMappingTable.tsx | 4 + .../components/AvailabilitySliderTable.tsx | 6 + packages/ui/components/icon/icon-list.mjs | 1 + packages/ui/components/icon/icon-names.ts | 1 + 7 files changed, 119 insertions(+), 22 deletions(-) diff --git a/apps/web/public/icons/sprite.svg b/apps/web/public/icons/sprite.svg index 87b4c19153..4f881a9fe1 100644 --- a/apps/web/public/icons/sprite.svg +++ b/apps/web/public/icons/sprite.svg @@ -191,6 +191,10 @@ + + + + diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index a1e4a75a7f..1d43a5a987 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2941,6 +2941,9 @@ "add_preset" : "Add Preset", "authorize": "Authorize", "next": "Next", + "hide": "Hide", + "asc": "Asc", + "desc": "Desc", "verify_email": "Verify email", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" } diff --git a/packages/features/data-table/components/DataTable.tsx b/packages/features/data-table/components/DataTable.tsx index bd27dfa886..dba790090d 100644 --- a/packages/features/data-table/components/DataTable.tsx +++ b/packages/features/data-table/components/DataTable.tsx @@ -2,15 +2,30 @@ import type { Row } from "@tanstack/react-table"; import { flexRender } from "@tanstack/react-table"; -import type { Table as ReactTableType } from "@tanstack/react-table"; +import type { Table as ReactTableType, Header } from "@tanstack/react-table"; import { useVirtualizer, type Virtualizer } from "@tanstack/react-virtual"; // eslint-disable-next-line no-restricted-imports import kebabCase from "lodash/kebabCase"; import { usePathname } from "next/navigation"; -import { useEffect, memo } from "react"; +import { useEffect, useState, memo } from "react"; import classNames from "@calcom/lib/classNames"; -import { Icon, TableNew, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@calcom/ui"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { + TableNew, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + Popover, + PopoverTrigger, + PopoverContent, + Command, + CommandList, + CommandItem, + Icon, +} from "@calcom/ui"; import { useColumnSizingVars } from "../hooks"; import { usePersistentColumnResizing } from "../lib/resizing"; @@ -132,27 +147,9 @@ export function DataTable({ className={classNames( "relative flex shrink-0 items-center", "bg-subtle", - header.column.getCanSort() ? "hover:bg-muted cursor-pointer select-none" : "", column.getIsPinned() && "top-0 z-20 sm:sticky" )}> -
- {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} - {header.column.getIsSorted() && ( - - )} -
+ {Boolean(enableColumnResizing) && header.column.getCanResize() && (
({ ); } + +const TableHeadLabel = ({ header }: { header: Header }) => { + const [open, setOpen] = useState(false); + const { t } = useLocale(); + + if (!header.column.getCanSort() && !header.column.getCanHide()) { + return ( +
+ {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} +
+ ); + } + + return ( + + + + + + + + {header.column.getCanSort() && ( + <> + { + if (header.column.getIsSorted() === "asc") { + header.column.clearSorting(); + } else { + header.column.toggleSorting(false, true); + } + }}> + + {t("asc")} +
+ {header.column.getIsSorted() === "asc" && } + + { + if (header.column.getIsSorted() === "desc") { + header.column.clearSorting(); + } else { + header.column.toggleSorting(true, true); + } + }}> + + {t("desc")} +
+ {header.column.getIsSorted() === "desc" && } + + + )} + {header.column.getCanHide() && ( + { + header.column.toggleVisibility(false); + setOpen(false); + }}> + + {t("hide")} + + )} + + + + + ); +}; diff --git a/packages/features/ee/dsync/components/GroupTeamMappingTable.tsx b/packages/features/ee/dsync/components/GroupTeamMappingTable.tsx index 0bd4d93dca..6dd4d2aef9 100644 --- a/packages/features/ee/dsync/components/GroupTeamMappingTable.tsx +++ b/packages/features/ee/dsync/components/GroupTeamMappingTable.tsx @@ -29,6 +29,8 @@ const GroupTeamMappingTable = () => { id: "name", header: t("team"), size: 200, + enableHiding: false, + enableSorting: false, cell: ({ row }) => { const { name } = row.original; @@ -39,6 +41,8 @@ const GroupTeamMappingTable = () => { id: "group", header: t("group_name"), size: 200, + enableHiding: false, + enableSorting: false, cell: ({ row }) => { const { id, groupNames, directoryId } = row.original; diff --git a/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx b/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx index a3ad8221c8..9c07735a84 100644 --- a/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx +++ b/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx @@ -95,6 +95,8 @@ export function AvailabilitySliderTable(props: { userTimeFormat: number | null; { id: "member", accessorFn: (data) => data.username, + enableHiding: false, + enableSorting: false, header: "Member", size: 200, cell: ({ row }) => { @@ -127,6 +129,8 @@ export function AvailabilitySliderTable(props: { userTimeFormat: number | null; id: "timezone", accessorFn: (data) => data.timeZone, header: "Timezone", + enableHiding: false, + enableSorting: false, size: 160, cell: ({ row }) => { const { timeZone } = row.original; @@ -152,6 +156,8 @@ export function AvailabilitySliderTable(props: { userTimeFormat: number | null; meta: { autoWidth: true, }, + enableHiding: false, + enableSorting: false, header: () => { return (
diff --git a/packages/ui/components/icon/icon-list.mjs b/packages/ui/components/icon/icon-list.mjs index c3d2ebacc0..b5f3aac588 100644 --- a/packages/ui/components/icon/icon-list.mjs +++ b/packages/ui/components/icon/icon-list.mjs @@ -141,6 +141,7 @@ export const lucideIconList = new Set([ "zap", "waypoints", "chevrons-down-up", + "chevrons-up-down", "sliders-horizontal", "calendar-range", ]); diff --git a/packages/ui/components/icon/icon-names.ts b/packages/ui/components/icon/icon-names.ts index 2f2e59f172..d6e184658c 100644 --- a/packages/ui/components/icon/icon-names.ts +++ b/packages/ui/components/icon/icon-names.ts @@ -38,6 +38,7 @@ export type IconName = | "chevrons-down-up" | "chevrons-left" | "chevrons-right" + | "chevrons-up-down" | "circle-alert" | "circle-arrow-up" | "circle-check-big"