summaryrefslogtreecommitdiff
path: root/components/data-table/data-table-toolbar.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
committerjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
commit1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch)
tree8a5587f10ca55b162d7e3254cb088b323a34c41b /components/data-table/data-table-toolbar.tsx
initial commit
Diffstat (limited to 'components/data-table/data-table-toolbar.tsx')
-rw-r--r--components/data-table/data-table-toolbar.tsx119
1 files changed, 119 insertions, 0 deletions
diff --git a/components/data-table/data-table-toolbar.tsx b/components/data-table/data-table-toolbar.tsx
new file mode 100644
index 00000000..78c7c39d
--- /dev/null
+++ b/components/data-table/data-table-toolbar.tsx
@@ -0,0 +1,119 @@
+"use client"
+
+import * as React from "react"
+import type { DataTableFilterField } from "@/types/table"
+import type { Table } from "@tanstack/react-table"
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { DataTableFacetedFilter } from "@/components/data-table/data-table-faceted-filter"
+import { DataTableViewOptions } from "@/components/data-table/data-table-view-options"
+
+interface DataTableToolbarProps<TData>
+ extends React.HTMLAttributes<HTMLDivElement> {
+ table: Table<TData>
+ /**
+ * An array of filter field configurations for the data table.
+ * When options are provided, a faceted filter is rendered.
+ * Otherwise, a search filter is rendered.
+ *
+ * @example
+ * const filterFields = [
+ * {
+ * id: 'name',
+ * label: 'Name',
+ * placeholder: 'Filter by name...'
+ * },
+ * {
+ * id: 'status',
+ * label: 'Status',
+ * options: [
+ * { label: 'Active', value: 'active', icon: ActiveIcon, count: 10 },
+ * { label: 'Inactive', value: 'inactive', icon: InactiveIcon, count: 5 }
+ * ]
+ * }
+ * ]
+ */
+ filterFields?: DataTableFilterField<TData>[]
+}
+
+export function DataTableToolbar<TData>({
+ table,
+ filterFields = [],
+ children,
+ className,
+ ...props
+}: DataTableToolbarProps<TData>) {
+ const isFiltered = table.getState().columnFilters.length > 0
+
+ // Memoize computation of searchableColumns and filterableColumns
+ const { searchableColumns, filterableColumns } = React.useMemo(() => {
+ return {
+ searchableColumns: filterFields.filter((field) => !field.options),
+ filterableColumns: filterFields.filter((field) => field.options),
+ }
+ }, [filterFields])
+
+ return (
+ <div
+ className={cn(
+ "flex w-full items-center justify-between gap-2 overflow-auto p-1",
+ className
+ )}
+ {...props}
+ >
+ <div className="flex flex-1 items-center gap-2">
+ {searchableColumns.length > 0 &&
+ searchableColumns.map(
+ (column) =>
+ table.getColumn(column.id ? String(column.id) : "") && (
+ <Input
+ key={String(column.id)}
+ placeholder={column.placeholder}
+ value={
+ (table
+ .getColumn(String(column.id))
+ ?.getFilterValue() as string) ?? ""
+ }
+ onChange={(event) =>
+ table
+ .getColumn(String(column.id))
+ ?.setFilterValue(event.target.value)
+ }
+ className="h-8 w-40 lg:w-64"
+ />
+ )
+ )}
+ {filterableColumns.length > 0 &&
+ filterableColumns.map(
+ (column) =>
+ table.getColumn(column.id ? String(column.id) : "") && (
+ <DataTableFacetedFilter
+ key={String(column.id)}
+ column={table.getColumn(column.id ? String(column.id) : "")}
+ title={column.label}
+ options={column.options ?? []}
+ />
+ )
+ )}
+ {isFiltered && (
+ <Button
+ aria-label="Reset filters"
+ variant="ghost"
+ className="h-8 px-2 lg:px-3"
+ onClick={() => table.resetColumnFilters()}
+ >
+ Reset
+ <X className="ml-2 size-4" aria-hidden="true" />
+ </Button>
+ )}
+ </div>
+ <div className="flex items-center gap-2">
+ {children}
+ <DataTableViewOptions table={table} />
+ </div>
+ </div>
+ )
+}