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
This commit is contained in:
Eunjae Lee
2025-01-27 10:20:59 +00:00
committed by GitHub
parent 41f63582b2
commit d3ef807af5
7 changed files with 119 additions and 22 deletions
+4
View File
@@ -191,6 +191,10 @@
<path d="m6 17 5-5-5-5" ></path>
<path d="m13 17 5-5-5-5" ></path>
</symbol>
<symbol class="lucide lucide-chevrons-up-down" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="chevrons-up-down">
<path d="m7 15 5 5 5-5" ></path>
<path d="m7 9 5-5 5 5" ></path>
</symbol>
<symbol class="lucide lucide-circle-alert" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="circle-alert">
<circle cx="12" cy="12" r="10" ></circle>
<line x1="12" x2="12" y1="8" y2="12" ></line>

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

@@ -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 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -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<TData, TValue>({
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"
)}>
<div
className="flex h-full w-full items-center overflow-hidden"
onClick={header.column.getToggleSortingHandler()}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getIsSorted() && (
<Icon
name="arrow-up"
className="ml-2 h-4 w-4"
style={{
transform:
header.column.getIsSorted() === "asc" ? "rotate(0deg)" : "rotate(180deg)",
transition: "transform 0.2s ease-in-out",
}}
/>
)}
</div>
<TableHeadLabel header={header} />
{Boolean(enableColumnResizing) && header.column.getCanResize() && (
<div
onMouseDown={header.getResizeHandler()}
@@ -287,3 +284,84 @@ function DataTableBody<TData>({
</TableBody>
);
}
const TableHeadLabel = ({ header }: { header: Header<any, any> }) => {
const [open, setOpen] = useState(false);
const { t } = useLocale();
if (!header.column.getCanSort() && !header.column.getCanHide()) {
return (
<div className="-ml-2 px-2 py-1">
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</div>
);
}
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<button
type="button"
className={classNames(
"group -ml-2 flex items-center gap-2 rounded-md px-2 py-1",
open && "bg-muted"
)}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
<Icon
name="chevrons-up-down"
className={classNames("text-subtle h-4 w-4", !open && "opacity-0 group-hover:opacity-100")}
/>
</button>
</PopoverTrigger>
<PopoverContent align="start" className="w-32 p-0">
<Command>
<CommandList>
{header.column.getCanSort() && (
<>
<CommandItem
className="flex cursor-pointer items-center gap-2 px-3 py-2"
onSelect={() => {
if (header.column.getIsSorted() === "asc") {
header.column.clearSorting();
} else {
header.column.toggleSorting(false, true);
}
}}>
<Icon name="arrow-up" className="h-4 w-4" />
{t("asc")}
<div className="flex-1" />
{header.column.getIsSorted() === "asc" && <Icon name="check" className="h-4 w-4" />}
</CommandItem>
<CommandItem
className="flex cursor-pointer items-center gap-2 px-3 py-2"
onSelect={() => {
if (header.column.getIsSorted() === "desc") {
header.column.clearSorting();
} else {
header.column.toggleSorting(true, true);
}
}}>
<Icon name="arrow-down" className="h-4 w-4" />
{t("desc")}
<div className="flex-1" />
{header.column.getIsSorted() === "desc" && <Icon name="check" className="h-4 w-4" />}
</CommandItem>
</>
)}
{header.column.getCanHide() && (
<CommandItem
className="flex cursor-pointer items-center gap-2 px-3 py-2"
onSelect={() => {
header.column.toggleVisibility(false);
setOpen(false);
}}>
<Icon name="eye-off" className="h-4 w-4" />
{t("hide")}
</CommandItem>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};
@@ -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;
@@ -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 (
<div className="flex items-center space-x-2">
@@ -141,6 +141,7 @@ export const lucideIconList = new Set([
"zap",
"waypoints",
"chevrons-down-up",
"chevrons-up-down",
"sliders-horizontal",
"calendar-range",
]);
@@ -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"