From e0dfb55c5457aec489fc084c4567e791b4c65eb1 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 26 Mar 2025 00:37:41 +0000
Subject: 3/25 까지의 대표님 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../client-data-table/data-table-view-options.tsx  | 192 +++++++++++++++++++++
 1 file changed, 192 insertions(+)
 create mode 100644 components/client-data-table/data-table-view-options.tsx

(limited to 'components/client-data-table/data-table-view-options.tsx')

diff --git a/components/client-data-table/data-table-view-options.tsx b/components/client-data-table/data-table-view-options.tsx
new file mode 100644
index 00000000..68026ff5
--- /dev/null
+++ b/components/client-data-table/data-table-view-options.tsx
@@ -0,0 +1,192 @@
+"use client"
+
+import * as React from "react"
+import { type Table } from "@tanstack/react-table"
+import {
+  ArrowUpDown,
+  Check,
+  ChevronsUpDown,
+  GripVertical,
+  Settings2,
+} from "lucide-react"
+
+import { cn, toSentenceCase } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import {
+  Command,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+} from "@/components/ui/command"
+import {
+  Popover,
+  PopoverContent,
+  PopoverTrigger,
+} from "@/components/ui/popover"
+
+// Sortable
+import {
+  Sortable,
+  SortableItem,
+  SortableDragHandle,
+} from "@/components/ui/sortable"
+
+
+/**
+ * ViewOptionsProps:
+ * - table: TanStack Table instance
+ */
+interface DataTableViewOptionsProps<TData> {
+  table: Table<TData>
+}
+
+declare module "@tanstack/react-table" {
+  interface ColumnMeta<TData, TValue> {
+    excelHeader?: string
+    group?: string
+    type?: string
+    // or whatever other fields you actually use
+  }
+}
+/**
+ * DataTableViewOptions:
+ * - Renders a Popover with hideable columns
+ * - Lets user reorder columns (drag & drop) + toggle visibility
+ */
+export function ClientDataTableViewOptions<TData>({
+  table,
+}: DataTableViewOptionsProps<TData>) {
+  const triggerRef = React.useRef<HTMLButtonElement>(null)
+
+  // 1) Identify columns that can be hidden
+  const hideableCols = React.useMemo(() => {
+    return table
+      .getAllLeafColumns()
+      .filter((col) =>  col.getCanHide() && col.accessorFn !== undefined)
+  }, [table])
+
+  // 2) local state for "columnOrder" (just the ID of hideable columns)
+  //    We'll reorder these with drag & drop
+  const [columnOrder, setColumnOrder] = React.useState<string[]>(() =>
+    hideableCols.map((c) => c.id)
+  )
+
+  // 3) onMove: when user finishes drag
+  //    - update local `columnOrder` only (no table.setColumnOrder yet)
+  const handleMove = React.useCallback(
+    ({ activeIndex, overIndex }: { activeIndex: number; overIndex: number }) => {
+      setColumnOrder((prev) => {
+        const newOrder = [...prev]
+        const [removed] = newOrder.splice(activeIndex, 1)
+        newOrder.splice(overIndex, 0, removed)
+        return newOrder
+      })
+    },
+    []
+  )
+
+  
+
+  // 4) After local state changes, reflect in tanstack table
+  //    - We do this in useEffect to avoid "update a different component" error
+  React.useEffect(() => {
+    // Also consider "non-hideable" columns, if any, to keep them in original positions
+    const nonHideable = table
+      .getAllColumns()
+      .filter((col) =>  col.getCanHide() && col.accessorFn !== undefined)
+      .filter((col) => !hideableCols.some((hc) => hc.id === col.id))
+      .map((c) => c.id)
+
+    // e.g. place nonHideable at the front, then our local hideable order
+    const finalOrder = ["select",...nonHideable, ...columnOrder]
+
+    // Now we set the table's official column order
+    table.setColumnOrder(finalOrder)
+  }, [columnOrder, hideableCols, table])
+
+
+  return (
+    <Popover modal>
+      <PopoverTrigger asChild>
+        <Button
+          ref={triggerRef}
+          aria-label="Toggle columns"
+          variant="outline"
+          role="combobox"
+          size="sm"
+          className="gap-2"
+        >
+          <Settings2 className="size-4" />
+          <span className="hidden sm:inline">View</span>
+          <ChevronsUpDown className="ml-auto size-4 shrink-0 opacity-50 hidden sm:inline" />
+        </Button>
+      </PopoverTrigger>
+
+      <PopoverContent
+        align="end"
+        className="w-44 p-0"
+        onCloseAutoFocus={() => triggerRef.current?.focus()}
+      >
+        <Command>
+          <CommandInput placeholder="Search columns..." />
+          <CommandList>
+            <CommandEmpty>No columns found.</CommandEmpty>
+
+            <CommandGroup>
+              {/**
+               * 5) Sortable: we pass an array of { id: string } from `columnOrder`,
+               *    so we can reorder them with drag & drop
+               */}
+              <Sortable
+                value={columnOrder.map((id) => ({ id }))}
+                onMove={handleMove}
+              >
+                {columnOrder.map((colId) => {
+                  // find column instance
+                  const column = hideableCols.find((c) => c.id === colId)
+
+    
+                  if (!column) return null
+
+                  return (
+                    <SortableItem key={colId} value={colId} asChild>
+                      <CommandItem
+                        onSelect={() =>
+                          column.toggleVisibility(!column.getIsVisible())
+                        }
+                      >
+                        {/* Drag handle on the left */}
+                        <SortableDragHandle
+                          variant="outline"
+                          size="icon"
+                          className="mr-2 size-5 shrink-0 rounded cursor-grab active:cursor-grabbing"
+                        >
+                          <ArrowUpDown className="size-2.5" aria-hidden="true" />
+                        </SortableDragHandle>
+
+                        {/* label */}
+                        <span className="truncate">
+                          {column?.columnDef?.meta?.excelHeader}
+                        </span>
+
+                        {/* check if visible */}
+                        <Check
+                          className={cn(
+                            "ml-auto size-4 shrink-0",
+                            column.getIsVisible() ? "opacity-100" : "opacity-0"
+                          )}
+                        />
+                      </CommandItem>
+                    </SortableItem>
+                  )
+                })}
+              </Sortable>
+            </CommandGroup>
+          </CommandList>
+        </Command>
+      </PopoverContent>
+    </Popover>
+  )
+}
\ No newline at end of file
-- 
cgit v1.2.3

