diff --git a/apps/www/src/app/examples/table/page.tsx b/apps/www/src/app/examples/table/page.tsx new file mode 100644 index 00000000..e8de99e3 --- /dev/null +++ b/apps/www/src/app/examples/table/page.tsx @@ -0,0 +1,179 @@ +'use client'; +import { + Button, + DataTable, + DataTableColumnDef, + DataTableQuery, + Flex, + IconButton +} from '@raystack/apsara'; +import { FilterIcon } from '@raystack/apsara/icons'; +import { useMemo, useState } from 'react'; + +const data: Payment[] = [ + { + id: 'm5gr84i9', + amount: 316, + status: 'success', + email: 'ken99@yahoo.com' + }, + { + id: '3u1reuv4', + amount: 242, + status: 'success', + email: 'Abe45@gmail.com' + }, + { + id: 'derv1ws0', + amount: 837, + status: 'processing', + email: 'Monserrat44@gmail.com' + }, + { + id: '5kma53ae', + amount: 874, + status: 'success', + email: 'Silas22@gmail.com' + }, + { + id: 'bhqecj4p', + amount: 721, + status: 'failed', + email: 'carmella@hotmail.com' + } +]; + +export type Payment = { + id: string; + amount: number; + status: 'pending' | 'processing' | 'success' | 'failed'; + email: string; +}; + +export const columns: DataTableColumnDef[] = [ + { + accessorKey: 'status', + header: 'Status', + cell: ({ row }) => ( +
{row.getValue('status')}
+ ), + filterOptions: [ + { + label: 'Pending', + value: 'pending' + }, + { + label: 'Processing', + value: 'processing' + }, + { + label: 'Success', + value: 'success' + }, + { + label: 'Failed', + value: 'failed' + } + ], + filterType: 'multiselect', + enableColumnFilter: true + }, + { + accessorKey: 'email', + header: 'Email', + cell: ({ row }) =>
{row.getValue('email')}
, + enableColumnFilter: true + }, + { + accessorKey: 'amount', + header: 'Amount', + cell: ({ row }) => { + const amount = parseFloat(row.getValue('amount')); + // Format the amount as a dollar amount + const formatted = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD' + }).format(amount); + + return
{formatted}
; + }, + enableColumnFilter: true, + filterType: 'number' + } +]; + +const Page = () => { + const [tableQuery, setTableQuery] = useState(); + console.log('tableQuery>> ', tableQuery); + + const filteredData = useMemo(() => { + const filters = tableQuery?.filters?.map(filter => filter.name) || []; + return data.filter(item => { + let shouldShow = true; + if (filters.includes('email')) { + shouldShow = item.email.includes( + tableQuery?.filters?.[filters.indexOf('email')]?.value || '' + ); + } + if (shouldShow && filters.includes('amount')) { + shouldShow = + item.amount === + tableQuery?.filters?.[filters.indexOf('amount')]?.value; + } + if (shouldShow && filters.includes('status')) { + shouldShow = tableQuery?.filters?.[ + filters.indexOf('status') + ]?.value.includes(item.status); + } + return shouldShow; + }); + }, [tableQuery]); + + return ( + + + + + appliedFilters.size > 0 ? ( + + + + ) : ( + + ) + } + /> + {filteredData.map(item => ( +
{`${item.email} - ${item.amount} - ${item.status}`}
+ ))} +
+
+
+ ); +}; + +export default Page; diff --git a/apps/www/src/content/docs/components/datatable/index.mdx b/apps/www/src/content/docs/components/datatable/index.mdx index 4bfc39f2..754c3fc5 100644 --- a/apps/www/src/content/docs/components/datatable/index.mdx +++ b/apps/www/src/content/docs/components/datatable/index.mdx @@ -189,3 +189,18 @@ const columns = [ }, ]; ``` + +### Using DataTable Filter + +The `DataTable.Filters` component can be used separately to filter data for custom views. + +```tsx + + + +``` diff --git a/packages/raystack/components/data-table/components/filters.tsx b/packages/raystack/components/data-table/components/filters.tsx index e1d34e76..a716b908 100644 --- a/packages/raystack/components/data-table/components/filters.tsx +++ b/packages/raystack/components/data-table/components/filters.tsx @@ -1,5 +1,6 @@ 'use client'; +import { ReactNode, useMemo } from 'react'; import { FilterIcon } from '~/icons'; import { FilterOperatorTypes, FilterType } from '~/types/filters'; import { Button } from '../../button'; @@ -11,39 +12,59 @@ import { DataTableColumn } from '../data-table.types'; import { useDataTable } from '../hooks/useDataTable'; import { useFilters } from '../hooks/useFilters'; +type Trigger = + | ReactNode + | (({ + availableFilters, + appliedFilters + }: { + availableFilters: DataTableColumn[]; + appliedFilters: Set; + }) => ReactNode); + interface AddFilterProps { columnList: DataTableColumn[]; appliedFiltersSet: Set; onAddFilter: (column: DataTableColumn) => void; + children?: Trigger; } function AddFilter({ columnList = [], appliedFiltersSet, - onAddFilter + onAddFilter, + children }: AddFilterProps) { const availableFilters = columnList?.filter( col => !appliedFiltersSet.has(col.id) ); + const trigger = useMemo(() => { + if (typeof children === 'function') + return children({ availableFilters, appliedFilters: appliedFiltersSet }); + else if (children) return children; + else if (appliedFiltersSet.size > 0) + return ( + + + + ); + else + return ( + + ); + }, [children, appliedFiltersSet, availableFilters]); + return availableFilters.length > 0 ? ( - - {appliedFiltersSet.size > 0 ? ( - - - - ) : ( - - )} - + {trigger} {availableFilters?.map(column => { const columnDef = column.columnDef; @@ -59,7 +80,19 @@ function AddFilter({ ) : null; } -export function Filters() { +export function Filters({ + classNames, + className, + trigger +}: { + classNames?: { + container?: string; + filterChips?: string; + addFilter?: string; + }; + className?: string; + trigger?: Trigger; +}) { const { table, tableQuery } = useDataTable(); const columns = table?.getAllColumns() as DataTableColumn[]; @@ -94,8 +127,8 @@ export function Filters() { }) || []; return ( - - + + {appliedFilters.map(filter => ( () { } columnType={filter.filterType} options={filter.options} + className={classNames?.filterChips} /> ))} @@ -118,7 +152,9 @@ export function Filters() { columnList={columnList} appliedFiltersSet={appliedFiltersSet} onAddFilter={onAddFilter} - /> + > + {trigger} + ); } diff --git a/packages/raystack/components/data-table/data-table.tsx b/packages/raystack/components/data-table/data-table.tsx index 05277fab..fb5cdc3f 100644 --- a/packages/raystack/components/data-table/data-table.tsx +++ b/packages/raystack/components/data-table/data-table.tsx @@ -10,6 +10,7 @@ import { } from '@tanstack/react-table'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Content } from './components/content'; +import { Filters } from './components/filters'; import { TableSearch } from './components/search'; import { Toolbar } from './components/toolbar'; import { TableContext } from './context'; @@ -171,5 +172,6 @@ function DataTableRoot({ export const DataTable = Object.assign(DataTableRoot, { Content: Content, Toolbar: Toolbar, - Search: TableSearch + Search: TableSearch, + Filters: Filters });