UNPKG

laif-ds

Version:

Design System di Laif con componenti React basati su principi di Atomic Design

403 lines (342 loc) 13.1 kB
# DataTable ## Overview Powerful table built on TanStack Table v8. Supports client-side and server-side modes with a unified API, advanced filtering (badges + builder), global search, multi-column sorting, row selection, column pinning, dynamic pagination, i18n, and loading skeletons. --- ## Props | Prop | Type | Default | Description | | ---------------------- | ------------------------------------- | ---------------------- | --------------------------------------------------------------------- | | `columns` | `ColumnDef<TData, TValue>[]` | — | TanStack column definitions. Use `meta` to enable features. | | `data` | `TData[]` | — | Table data. | | `loading` | `boolean` | `false` | Show skeleton rows. | | `emptyComponent` | `ReactNode` | `undefined` | Rendered when no rows. | | `className` | `string` | `undefined` | Wrapper classes. | | `checkable` | `boolean` | `false` | Adds a left checkbox column for row selection. | | `rowSelection` | `Record<string, boolean>` | `{}` | Controlled selection map. | | `onRowSelectionChange` | `OnChangeFn<Record<string, boolean>>` | `undefined` | Selection change handler (controlled selection). | | `onCheckedRowsChange` | `(checkedRows: TData[]) => void` | `undefined` | Returns selected row values (requires `checkable`). | | `actions` | `DataTableActions[]` | `[]` | Actions for the actions row. | | `hidePagination` | `boolean` | `false` | Hide pagination UI. | | `hideActionsRow` | `boolean` | `false` | Hide actions row. | | `i18n` | `DataTableI18n` | `defaultDataTableI18n` | Internationalization strings. | | `maxSortedColumns` | `number` | `2` | Max number of sorted columns. | | `initialState` | `DataTableState<TData>` | `undefined` | Initial filters, sorting, pagination and optional `columnVisibility`. | | `serverMode` | `boolean` | `false` | Enable server-side mode. | | `serverConfig` | `DataTableServerConfig` | `undefined` | `{ totalItems, onStateChange }` handler for server mode. | | `disableAutoPageSize` | `boolean` | `false` | Disable auto pageSize (still updates pageIndex). | --- ## Column meta Configure columns via `column.meta`: ```ts type ColumnMeta = { type: | "string" | "number" | "boolean" | "date" | "datetime" | "list_single_select" | "list_multi_select" | "other"; headerLabel?: string; sortable?: boolean; filterable?: boolean; searchable?: boolean; pinned?: "left" | "right"; listOptions?: { value: string; label: string }[]; }; ``` Notes: - `id` must match the field key used for filtering/sorting. - If `header` is a `ReactNode` (e.g. `header: () => <div>Nome</div>`), set `meta.headerLabel` to provide a stable text label for sorting/filtering/column-visibility UI. - Filtering nested accessors (e.g., `"user.name"`) is not supported. --- ## Behavior - **Filtering**: Badge filters + advanced builder with logical `_and`/`_or`. - **Global search**: Across `meta.searchable` columns; strings use `like`, arrays use `array_overlap` (mapped automatically). - **Operators**: Full operator set per type, including `eq_null`/`n_eq_null`, list operators (`array_overlap`, `n_array_overlap`), date/time before/after, checked/unchecked. - **Sorting**: Multi-column (limited by `maxSortedColumns`). - **Selection**: Integrated checkbox column when `checkable`. - **Column visibility**: Columns can start hidden via `initialState.columnVisibility` and be toggled from the eye icon popover in the toolbar (supports drag-and-drop reordering). - **Non-hideable columns**: Set `enableHiding: false` on a column to prevent it from being hidden. - **Custom JSX headers**: Use `meta.headerLabel` when `header` is a ReactNode to provide a stable text label for menus. - **Pinning**: `meta.pinned` supports left/right pinned columns. - **Pagination**: Auto page size from container height; respects `disableAutoPageSize`. - **Loading**: Skeleton rows adapt to viewport height (no hardcoded length). - **i18n**: All labels (sorting/filtering menus included) use `DataTableI18n`. - **Datetime**: Supports microseconds format like `2025-08-22T12:53:54.060315` and timezone-safe date handling. --- ## Server-side mode When `serverMode=true`, the table emits state to `serverConfig.onStateChange(state)` with: ```ts type ServerState = { pagination: { pageIndex: number; pageSize: number }; filters?: IFilterState; computedFilter?: SearchFilter; computedSorting?: { sort_by: string[]; sort_order: ("asc" | "desc")[] }; }; ``` The table manages debounced emissions and preserves computed values across updates. --- ## Examples ### Client mode using utilities (columns + filters + initial state) ```tsx import * as React from "react"; import type { ColumnDef } from "@tanstack/react-table"; import { DataTable, createStringColumn, createNumberColumn, createSingleSelectColumn, createMultiSelectColumn, createActionColumn, createInitialState, createStringFilter, createNumberFilter, } from "laif-ds"; import { Button } from "laif-ds"; type Person = { id: string; name: string; age: number; role: string; tags: string[]; created_at: string; active: boolean; }; const columns = [ createStringColumn<Person>({ accessorKey: "name", header: "Nome", sortable: true, filterable: true, searchable: true, }), createNumberColumn<Person>({ accessorKey: "age", header: "Età", sortable: true, filterable: true, }), createSingleSelectColumn<Person>({ accessorKey: "role", header: "Ruolo", options: ["Admin", "User", "Guest"], filterable: true, searchable: true, }), createMultiSelectColumn<Person>({ accessorKey: "tags", header: "Tags", options: ["frontend", "backend", "devops"], filterable: true, }), createActionColumn<Person>({ id: "actions", header: "Azioni", pinned: "right", cell: (row) => <Button size="sm">Edit {row.name}</Button>, }), ] satisfies ColumnDef<Person, any>[]; const initialState = createInitialState({ filters: [ createStringFilter("name", "name", "Nome", "like", "John"), createNumberFilter("age", "age", "Età", "ge", 18), ], searchbarFilter: "developer", sorting: [{ column: "name", order: "asc" }], pagination: { pageIndex: 0, pageSize: 15 }, }); const actions = [ { label: "Export CSV", icon: "FileDown", onClick: () => { /* ... */ }, }, { label: "Bulk Delete", icon: "Trash2", onClick: () => { /* ... */ }, }, ]; export function ClientTable({ data }: { data: Person[] }) { return ( <DataTable columns={columns} data={data} initialState={initialState} actions={actions} checkable onCheckedRowsChange={(rows) => console.log("checked rows", rows)} /> ); } ``` ### Server mode using utilities (state-driven fetch) ```tsx import * as React from "react"; import type { ServerState } from "laif-ds"; import { DataTable, createInitialState } from "laif-ds"; export function ServerTable({ data, total, }: { data: Person[]; total: number; }) { const [loading, setLoading] = React.useState(false); const handleState = React.useCallback(async (state: ServerState) => { // Use state.pagination, state.computedFilter, state.computedSorting // to fetch from your backend setLoading(true); try { await fetch("/api/people", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(state), }); // Update your data + total here based on response } finally { setLoading(false); } }, []); return ( <DataTable columns={columns} data={data} loading={loading} serverMode serverConfig={{ totalItems: total, onStateChange: handleState }} initialState={createInitialState({ pagination: { pageIndex: 0, pageSize: 20 }, })} checkable /> ); } ``` ### Initial column visibility You can start with some columns hidden and let the user control them via the column-visibility (eye) popover: ```tsx const initialStateWithHiddenColumns = createInitialState<Person>({ pagination: { pageIndex: 0, pageSize: 15 }, sorting: [{ column: "name", order: "asc" }], columnVisibility: { // these columns will start hidden in the UI secret_field: false, debug_column: false, }, }); export function TableWithHiddenColumns({ data }: { data: Person[] }) { return ( <DataTable columns={columns} data={data} initialState={initialStateWithHiddenColumns} /> ); } ``` ### Non-hideable columns If a column must always stay visible, set `enableHiding: false` on the column definition. ```tsx import type { ColumnDef } from "@tanstack/react-table"; const columns: ColumnDef<Person>[] = [ { accessorKey: "name", header: "Nome", meta: { type: "string", sortable: true, searchable: true }, }, { accessorKey: "email", header: "Email", enableHiding: false, meta: { type: "string", searchable: true }, }, ]; export function TableWithNonHideableColumn({ data }: { data: Person[] }) { return <DataTable columns={columns} data={data} />; } ``` ### Checkbox selection column (`checkable`) When `checkable` is enabled, the DataTable adds a left checkbox column for row selection. This column is not hideable and not draggable in the column visibility popover. ```tsx export function TableCheckable({ data }: { data: Person[] }) { return <DataTable columns={columns} data={data} checkable />; } ``` ### Drag-and-drop reordering You can reorder columns by opening the column visibility popover (eye icon) and dragging items (grip icon). The order is managed internally via table `columnOrder` state. ```tsx export function TableWithReordering({ data }: { data: Person[] }) { return <DataTable columns={columns} data={data} />; } ``` ### ReactNode headers + `meta.headerLabel` If you use a `ReactNode` header, set `meta.headerLabel` so that sorting/filtering/visibility menus can show a readable label. ```tsx import type { ColumnDef } from "@tanstack/react-table"; const columns: ColumnDef<Person>[] = [ { accessorKey: "name", header: () => <div>Nome</div>, meta: { type: "string", headerLabel: "Nome", sortable: true, filterable: true, searchable: true, }, }, ]; ``` ### Utility recipes (pinning, list options, quick filters) ```tsx import { pinColumns, updateColumnListOptions, toSelectOptions, createFilterBadges, createStringFilter, createNumberFilter, } from "laif-ds"; // Pin columns to left/right const pinnedColumns = pinColumns(columns, { left: ["name"], right: ["actions"], }); // Update list options dynamically (e.g., fetched from API) const roleOptions = toSelectOptions(["Admin", "User", "Guest", "Manager"]); const columnsWithUpdatedRoles = updateColumnListOptions( columns, "role", roleOptions, ); // Build multiple filter badges quickly const quickFilters = createFilterBadges([ { columnId: "name", columnAccessorKey: "name", columnLabel: "Nome", columnType: "string", operator: "like", value: "Jane", }, { columnId: "age", columnAccessorKey: "age", columnLabel: "Età", columnType: "number", operator: "ge", value: 30, }, ]); ``` --- ## Notes - **Performance**: Client mode filters/sorts in-memory; use server mode for large datasets. - **UX**: Page size auto-adapts; use `disableAutoPageSize` to manage it manually. - **Internationalization**: Provide custom `i18n` for localized actions and labels.