From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- .../client-data-table/data-table-group-list.tsx | 279 +++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 components/client-data-table/data-table-group-list.tsx (limited to 'components/client-data-table/data-table-group-list.tsx') diff --git a/components/client-data-table/data-table-group-list.tsx b/components/client-data-table/data-table-group-list.tsx new file mode 100644 index 00000000..519b7327 --- /dev/null +++ b/components/client-data-table/data-table-group-list.tsx @@ -0,0 +1,279 @@ +"use client" + +import * as React from "react" +import { type Table } from "@tanstack/react-table" +import { Layers, Check, ChevronsUpDown, GripVertical, XCircle } from "lucide-react" + +import { toSentenceCase, cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" +import { + Command, + CommandList, + CommandGroup, + CommandItem, + CommandInput, + CommandEmpty, +} from "@/components/ui/command" +import { + Sortable, + SortableItem, + SortableDragHandle, +} from "@/components/ui/sortable" + +interface DataTableGroupListLocalProps { + /** TanStack Table 인스턴스 (grouping을 사용할 수 있어야 함) */ + table: Table +} + +export function ClientDataTableGroupList({ + table, +}: DataTableGroupListLocalProps) { + // ----------------------------- + // 1) Local grouping state + // ----------------------------- + const [grouping, setGrouping] = React.useState( + (table.initialState.grouping as string[]) ?? [] + ) + + // Keep the table grouping in sync + React.useEffect(() => { + table.setGrouping(grouping) + }, [grouping, table]) + + // Avoid duplicates (just in case) + const uniqueGrouping = React.useMemo( + () => [...new Set(grouping)], + [grouping] + ) + + // ----------------------------- + // 2) Groupable columns + // ----------------------------- + const groupableColumns = React.useMemo( + () => + table + .getAllColumns() + .filter((col) => col.getCanGroup?.() !== false) + .map((col) => { + // If meta?.excelHeader is missing or undefined, fall back to `col.id` + const friendlyName = + typeof col.columnDef?.meta?.excelHeader === "string" + ? col.columnDef.meta.excelHeader + : col.id + + return { + id: col.id, + // Ensure it's always a string, so no type error: + label: toSentenceCase(friendlyName), + } + }), + [table] + ) + + const ungroupedColumns = React.useMemo( + () => groupableColumns.filter((c) => !uniqueGrouping.includes(c.id)), + [groupableColumns, uniqueGrouping] + ) + + // ----------------------------- + // 3) Handlers + // ----------------------------- + // Add the first ungrouped column + function addGroup() { + const firstAvailable = ungroupedColumns[0] + if (!firstAvailable) return + setGrouping((prev) => [...prev, firstAvailable.id]) + } + + // Remove a group + function removeGroup(colId: string) { + setGrouping((prev) => prev.filter((g) => g !== colId)) + } + + // Reset grouping entirely + function resetGrouping() { + setGrouping([]) + } + + // Reorder groups via Sortable + function onGroupOrderChange(newGroups: string[]) { + setGrouping(newGroups) + } + + // ----------------------------- + // 4) Render + // ----------------------------- + return ( + ({ id }))} + onValueChange={(items) => onGroupOrderChange(items.map((i) => i.id))} + overlay={ +
+
+
+
+
+ } + > + + + + + + 0 ? "gap-3.5" : "gap-2" + )} + > + {uniqueGrouping.length > 0 ? ( + <> +

Group by

+

+ Grouping is applied to the currently loaded data only. +

+ + ) : ( +
+

No grouping applied

+

+ Add grouping to organize your results. +

+
+ )} + + {/* Current groups */} +
+
+ {uniqueGrouping.map((colId) => { + // Find the column's friendly label + const colDef = groupableColumns.find((c) => c.id === colId) + const label = colDef?.label ?? toSentenceCase(colId) + + return ( + +
+ + + + + + + + + No columns found. + + {ungroupedColumns.map((column) => ( + { + // Replace colId with new value + setGrouping((prev) => + prev.map((g) => (g === colId ? value : g)) + ) + }} + > + + {column.label} + + + ))} + + + + + + + {/* remove group */} + + + {/* drag handle */} + + +
+
+ ) + })} +
+
+ + {/* Footer: "Add group" & "Reset grouping" */} +
+ + {uniqueGrouping.length > 0 && ( + + )} +
+
+
+ + ) +} \ No newline at end of file -- cgit v1.2.3