jbetancur / react-data-table-component

A responsive table library with built-in sorting, pagination, selection, expandable rows, and customizable styling.
https://react-data-table-component.netlify.app
Apache License 2.0
2.06k stars 413 forks source link

[FEATURE]: Live Search #1177

Open bytemtek opened 1 year ago

bytemtek commented 1 year ago

Is this component supported or will it support live search? Thank you.

mohammedsaif-fb commented 11 months ago

Please support live search across cols and rows

bytemtek commented 6 months ago

Is there any update about search ?

mohammedsaif-fb commented 6 months ago

@bytemtek I have achieved desired functionality using another lib https://www.npmjs.com/package/react-data-table-component-extensions

bytemtek commented 6 months ago

@bytemtek I have achieved desired functionality using another lib https://www.npmjs.com/package/react-data-table-component-extensions

Thank you so much !

lechnerio commented 2 months ago

Just picking this Feature Request up because it might help someone. I ended up creating a Helper-Component (customTable.tsx) so really only need to pass the most basic Info inside the actual pages.

Example Page

"use client"

import CustomTable from "@/components/customTable"

const ExamplePage = () => {
  const columns = [
    {
      name: "Vorname",
      columnName: "firstName",
      sortable: true,
    },
    {
      name: "Nachname",
      columnName: "lastName",
      sortable: true,
    },
    {
      name: "Adresse",
      columnName: "address",
      sortable: true,
    },
    {
      name: "Tel",
      columnName: "phone",
      sortable: false,
      searchable: false,
    },
    {
      name: "Password",
      columnName: "password",
      sortable: true,
    },
    {
      name: "Domain",
      columnName: "domain",
      sortable: true,
    },
    {
      name: "Price",
      columnName: "price",
      sortable: true,
    },
  ]

  const data = [
    {
      id: "8c5e932d-486c-4518-9844-16ea95d953ed",
      firstName: "Sasha",
      lastName: "Armstrong",
      address: "53155 Dortha Landing 1, 30003-6945 Estonia",
      phone: "1-412-368-8275 x7224",
      password: "V3Ib3it5gyIKDYf",
      domain: "weak-trim.biz",
      price: "942.00",
    },
    {
      id: "dd5c1ba6-b205-4e4e-b6c9-4685c4a188c2",
      firstName: "Piper",
      lastName: "Nicolas",
      address: "99080 Abbey Islands 1, 30051 Faroe Islands",
      phone: "1-499-890-1017",
      password: "25EokNu2NxQoXHi",
      domain: "hideous-dart.biz",
      price: "542.00",
    },
    {
      id: "c701cb3a-a0bb-4ed6-a0cd-c56cec696155",
      firstName: "Michale",
      lastName: "Blick",
      address: "7000 Hessel Groves 1, 23758-6810 Saint Pierre and Miquelon",
      phone: "1-403-512-7971 x476",
      password: "niZRpgXuHRdihe6",
      domain: "affectionate-history.biz",
      price: "603.00",
    },
  ]

  return <CustomTable columns={columns} data={data} />
}

export default ExamplePage

the columns themself are just passing sortable and searchable as well as the Label and actual data-value:

{
  name: "Vorname",            <-- the Label that get's displayed on the frontend
  columnName: "firstName",    <-- the name of the data-column
  sortable: true,             <-- sortable active by default
  searchable: true            <-- searchable active by default
},

customTable Component

this custom component has my default settings hardcoded, CSV and Live-Search enabled while maintaining a minimal and easy to use page. Overall it's used like this:

"use client"

import { Button } from "@/components/ui/button"
import { ArrowUp, FileDown, TrashIcon } from "lucide-react"
import { useMemo, useState } from "react"
import DataTable, { TableColumn } from "react-data-table-component"
import { Input } from "./ui/input"

interface CustomColumn {
  name: string
  columnName: string
  sortable?: boolean
  searchable?: boolean
}

type CustomTableProps<T> = {
  columns: CustomColumn[]
  data: T[]
}

const CustomTable = <T extends Record<string, unknown>>({
  columns,
  data,
}: CustomTableProps<T>) => {
  const [filterText, setFilterText] = useState("")

  const searchableColumns = columns.filter((col) => col.searchable !== false)

  const filteredData = useMemo(() => {
    return data.filter((item) =>
      searchableColumns.some((col) =>
        String(item[col.columnName] || "")
          .toLowerCase()
          .includes(filterText.toLowerCase()),
      ),
    )
  }, [data, filterText, searchableColumns])

  const tableColumns: TableColumn<T>[] = columns.map((col) => ({
    name: col.name,
    selector: (row: T) => row[col.columnName as keyof T] as unknown as string,
    sortable: col.sortable !== false
  }))

  function downloadCSV(array: T[]) {
    const link = document.createElement("a")
    const csv = array
      .map((row) =>
        searchableColumns.map((col) => String(row[col.columnName])).join(";"),
      )
      .join("\n")

    link.setAttribute("href", `data:text/csv;charset=utf-8,${csv}`)
    link.setAttribute("download", "export.csv")
    link.click()
  }

  const Export = ({ onExport }: { onExport: () => void }) => (
    <Button onClick={() => onExport()}>
      <FileDown className="h-4 w-4 mr-4" /> Export (CSV)
    </Button>
  )

  const actionsMemo = useMemo(
    () => <Export onExport={() => downloadCSV(filteredData)} />,
    [filteredData],
  )

  const subHeaderComponentMemo = useMemo(() => {
    const handleClear = () => setFilterText("")
    return (
      <div className="flex justify-center items-center">
        <Input
          type="text"
          placeholder="Search"
          value={filterText}
          onChange={(e) => setFilterText(e.target.value)}
        />
        <Button onClick={handleClear} variant="outline">
          <TrashIcon className="h-4 w-4" />
        </Button>
      </div>
    )
  }, [filterText])

  return (
    <div>
      <DataTable
        pagination
        columns={tableColumns}
        data={filteredData}
        responsive
        highlightOnHover
        sortIcon={<ArrowUp />}
        paginationRowsPerPageOptions={[10, 25, 50, 100]}
        actions={actionsMemo}
        noDataComponent="Keine Daten vorhanden"
        progressComponent="Daten werden geladen..."
        paginationComponentOptions={{
          rowsPerPageText: "Einträge pro Seite",
          rangeSeparatorText: "von",
        }}
        customStyles={{
          header: { style: { padding: "0px" } },
          subHeader: { style: { padding: "0px" } },
        }}
        subHeader
        subHeaderComponent={subHeaderComponentMemo}
      />
    </div>
  )
}

export default CustomTable