From 5dc9ed5b3cde541ffc08a2ceee36b98d3f4765b4 Mon Sep 17 00:00:00 2001 From: Amir Astaneh Date: Tue, 14 Jul 2026 18:54:33 -0400 Subject: [PATCH 1/4] fix: preserve row hover background with inline styles (#1351) Ensure highlightOnHover overrides inline row background styles from customStyles. --- src/DataTable.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataTable.css b/src/DataTable.css index 7905bccf..20b3dbad 100644 --- a/src/DataTable.css +++ b/src/DataTable.css @@ -167,7 +167,7 @@ .rdt_rowHighlight:hover { color: var(--rdt-color-highlight-text, rgba(0, 0, 0, 0.87)); - background-color: var(--rdt-color-highlight, #eee); + background-color: var(--rdt-color-highlight, #eee) !important; outline: 1px solid var(--rdt-color-bg, #fff); transition-duration: 0.15s; transition-property: background-color; From 0e9239864bd17b1a027bd0f5306f333a45032053 Mon Sep 17 00:00:00 2001 From: John Betancur <1385932+jbetancur@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:07:42 -0400 Subject: [PATCH 2/4] fix: drop numeric hide breakpoint (no-op since v8), fix hover bg override (#1352) TableColumn.hide now only accepts Media ('sm'|'md'|'lg'); numeric pixel values silently did nothing. Also forces highlightOnHover's background over inline customStyles.rows.style. --- CHANGELOG.md | 9 +++++++++ apps/docs/src/pages/docs/api.astro | 2 +- apps/docs/src/pages/docs/columns.astro | 2 +- apps/docs/src/pages/docs/mobile.astro | 8 +------- apps/docs/src/pages/index.astro | 2 +- src/__tests__/DataTable.test.tsx | 8 -------- src/types.ts | 2 +- 7 files changed, 14 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b01162fe..eccbdfef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ A summary of notable changes per release. For the full commit history see the [repository on GitHub](https://github.com/jbetancur/react-data-table-component/commits/master). +## 8.6.2 + +### Bug fixes + +- A numeric pixel value for `TableColumn.hide` (e.g. `hide: 480`) was a no-op — the column stayed visible at every width. Numeric breakpoints stopped working in the v8 refactor; the type now accepts only `Media` (`'sm' | 'md' | 'lg'`) to match. For a custom breakpoint, track viewport width yourself and toggle the column's `omit` prop instead. → [Mobile](/docs/mobile) ([#1346](https://github.com/jbetancur/react-data-table-component/issues/1346)) +- Fixed `highlightOnHover` not showing when a row's background color was set via `customStyles.rows.style`, since the inline style was winning over the hover class. → [Styling](/docs/custom-styles) ([#1351](https://github.com/jbetancur/react-data-table-component/pull/1351)) + +--- + ## 8.6.1 ### Bug fixes diff --git a/apps/docs/src/pages/docs/api.astro b/apps/docs/src/pages/docs/api.astro index 6677604f..4cb405d6 100644 --- a/apps/docs/src/pages/docs/api.astro +++ b/apps/docs/src/pages/docs/api.astro @@ -145,7 +145,7 @@ const columns: TableColumn[] = [ ['button', 'boolean', 'Center content and suppress row click propagation.'], ['allowOverflow', 'boolean', 'Allow cell content to overflow (useful for dropdowns and popovers).'], ['ignoreRowClick', 'boolean', 'Prevent clicks in this cell from firing onRowClicked.'], - ['hide', 'Media | number', 'Hide the column below the given breakpoint (Media.SM = 599px, MD = 959px, LG = 1280px) or a custom pixel value.'], + ['hide', 'Media', 'Hide the column below the given breakpoint (Media.SM = 599px, MD = 959px, LG = 1280px).'], ['omit', 'boolean', 'Exclude the column entirely. Toggle this to show/hide a column.'], ['reorder', 'boolean', 'Allow drag-to-reorder for this column (requires reorder on at least two columns).'], ['pinned', "'left' | 'right'", 'Freeze the column to an edge during horizontal scroll. Only visible when the table overflows its container — give columns explicit widths. See Column pinning.'], diff --git a/apps/docs/src/pages/docs/columns.astro b/apps/docs/src/pages/docs/columns.astro index ec2b120c..bd68bdf1 100644 --- a/apps/docs/src/pages/docs/columns.astro +++ b/apps/docs/src/pages/docs/columns.astro @@ -136,7 +136,7 @@ const columns: TableColumn[] = [ ['button', 'boolean', 'Center content and suppress row click propagation'], ['allowOverflow', 'boolean', 'Allow content to overflow the cell (for dropdowns/popovers)'], ['ignoreRowClick', 'boolean', 'Prevent cell clicks from triggering onRowClicked'], - ['hide', 'number | "sm" | "md" | "lg"', 'Hide the column below this breakpoint'], + ['hide', '"sm" | "md" | "lg"', 'Hide the column below this breakpoint'], ['omit', 'boolean', 'Exclude column entirely (useful for toggling visibility)'], ['reorder', 'boolean', 'Allow drag-to-reorder for this column'], ['style', 'CSSProperties', 'Inline styles applied to every cell in this column'], diff --git a/apps/docs/src/pages/docs/mobile.astro b/apps/docs/src/pages/docs/mobile.astro index f8f0bbd1..186db23e 100644 --- a/apps/docs/src/pages/docs/mobile.astro +++ b/apps/docs/src/pages/docs/mobile.astro @@ -56,12 +56,6 @@ const columns: TableColumn[] = [ { name: 'Joined', selector: r => r.joined, hide: 'lg' }, // hidden below 1280px ];`} /> -

- You can also pass a numeric pixel value to hide at a custom breakpoint: -

- - r.notes, hide: 480 }`} /> -

Pagination on small screens

@@ -111,7 +105,7 @@ function MyPage() { hide', 'Media | number', 'Hide the column below the given breakpoint (Media.SM = 599px, MD = 959px, LG = 1280px) or a custom pixel value. See the API reference for every column field.'], + ['hide', 'Media', 'Hide the column below the given breakpoint (Media.SM = 599px, MD = 959px, LG = 1280px). See the API reference for every column field.'], ]} /> diff --git a/apps/docs/src/pages/index.astro b/apps/docs/src/pages/index.astro index c1e0690d..27690239 100644 --- a/apps/docs/src/pages/index.astro +++ b/apps/docs/src/pages/index.astro @@ -35,7 +35,7 @@ function fmt(n: number): string { v8.0 · No peer deps · React 18+ What's new in v8 → diff --git a/src/__tests__/DataTable.test.tsx b/src/__tests__/DataTable.test.tsx index f0c3c0ca..f9a28b8e 100644 --- a/src/__tests__/DataTable.test.tsx +++ b/src/__tests__/DataTable.test.tsx @@ -395,14 +395,6 @@ describe('DataTable::columns', () => { expect(container.querySelector('.rdt_hideOnLg')).not.toBeNull(); }); - test('should render correctly if column.hide is an integer', () => { - const mock = dataMock({ hide: 300 }); - const { container } = render(); - - // integer does not map to a media class, column still renders - expect(container.querySelector('div[data-column-id="1"]')).not.toBeNull(); - }); - test('should render correctly if column.omit is true', () => { const mock = dataMock(); const mockColumns = mock.columns.slice(); diff --git a/src/types.ts b/src/types.ts index 3f33e6d2..25085c85 100644 --- a/src/types.ts +++ b/src/types.ts @@ -379,7 +379,7 @@ export type TableColumnBase = { compact?: boolean; reorder?: boolean; grow?: number; - hide?: number | Media; + hide?: Media; id?: string | number; ignoreRowClick?: boolean; maxWidth?: string; From 2bd3b459e8de33da9c9b1054201ecab2c34c509b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 14 Jul 2026 23:10:08 +0000 Subject: [PATCH 3/4] chore: release v8.6.2 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6544148d..c364f013 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-data-table-component", - "version": "8.6.1", + "version": "8.6.2", "description": "A fast, feature-rich React data table. Working table in 10 lines.", "funding": [ { From ddeb5a6df540c8963b091da097058d3c6e48a841 Mon Sep 17 00:00:00 2001 From: John Betancur <1385932+jbetancur@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:25:32 -0400 Subject: [PATCH 4/4] docs formatting and updates (#1353) --- .../components/demos/ApprovalWorkflowDemo.tsx | 94 +++-- .../src/components/demos/AuditLogDemo.tsx | 187 ++++++++-- .../components/demos/ColumnPinningDemo.tsx | 4 +- .../src/components/demos/ContextMenuDemo.tsx | 69 +++- .../src/components/demos/CsvImportDemo.tsx | 13 +- .../demos/DashboardDrilldownDemo.tsx | 122 +++++-- .../src/components/demos/EditableGridDemo.tsx | 11 +- .../src/components/demos/ExpandableDemo.tsx | 36 +- apps/docs/src/components/demos/ExportDemo.tsx | 17 +- .../src/components/demos/FooterBasicDemo.tsx | 26 +- .../src/components/demos/FooterCustomDemo.tsx | 34 +- .../src/components/demos/HeadlessDemo.tsx | 9 +- apps/docs/src/components/demos/IconsDemo.tsx | 233 ++++++++++--- .../components/demos/InlineRowActionsDemo.tsx | 134 ++++++-- .../components/demos/KeyboardArrowNavDemo.tsx | 6 +- .../src/components/demos/KeyboardEditDemo.tsx | 4 +- .../src/components/demos/KeyboardSortDemo.tsx | 6 +- .../src/components/demos/LiveUpdatesDemo.tsx | 35 +- .../docs/src/components/demos/LoadingDemo.tsx | 26 +- apps/docs/src/components/demos/MobileDemo.tsx | 21 +- .../src/components/demos/MultiSortDemo.tsx | 4 +- .../demos/PersistColumnWidthsDemo.tsx | 7 +- apps/docs/src/components/demos/RTLDemo.tsx | 49 ++- .../src/components/demos/RowGroupingDemo.tsx | 16 +- .../components/demos/RowInteractionsDemo.tsx | 70 ++-- .../src/components/demos/RowPinningDemo.tsx | 171 ++++++---- .../src/components/demos/SelectionDemo.tsx | 46 ++- .../components/demos/ServerSideRecipeDemo.tsx | 37 +- .../demos/ServerSideSortPaginationDemo.tsx | 17 +- .../src/components/demos/SortResetDemo.tsx | 13 +- .../docs/src/components/demos/UrlSyncDemo.tsx | 97 ++++-- apps/docs/src/pages/docs/animations.astro | 43 +-- apps/docs/src/pages/docs/api.astro | 18 +- apps/docs/src/pages/docs/cells.astro | 45 ++- apps/docs/src/pages/docs/column-groups.astro | 57 ++-- apps/docs/src/pages/docs/column-pinning.astro | 41 ++- apps/docs/src/pages/docs/column-reorder.astro | 97 +++--- .../src/pages/docs/column-visibility.astro | 24 +- apps/docs/src/pages/docs/columns.astro | 57 ++-- .../src/pages/docs/conditional-styles.astro | 10 +- apps/docs/src/pages/docs/context-menu.astro | 8 +- apps/docs/src/pages/docs/custom-styles.astro | 44 ++- apps/docs/src/pages/docs/expandable.astro | 88 +++-- apps/docs/src/pages/docs/export.astro | 30 +- apps/docs/src/pages/docs/filtering.astro | 69 ++-- apps/docs/src/pages/docs/fixed-header.astro | 33 +- apps/docs/src/pages/docs/footer.astro | 62 ++-- .../docs/src/pages/docs/getting-started.astro | 18 +- apps/docs/src/pages/docs/headless.astro | 73 ++-- apps/docs/src/pages/docs/inline-editing.astro | 68 ++-- .../src/pages/docs/keyboard-navigation.astro | 13 +- apps/docs/src/pages/docs/loading.astro | 27 +- apps/docs/src/pages/docs/localization.astro | 120 +++---- apps/docs/src/pages/docs/mobile.astro | 60 +++- apps/docs/src/pages/docs/pagination.astro | 175 ++++++---- apps/docs/src/pages/docs/performance.astro | 72 ++-- apps/docs/src/pages/docs/resizable.astro | 34 +- .../src/pages/docs/row-interactions.astro | 32 +- apps/docs/src/pages/docs/row-pinning.astro | 45 ++- apps/docs/src/pages/docs/rtl.astro | 29 +- apps/docs/src/pages/docs/selection.astro | 57 ++-- apps/docs/src/pages/docs/sorting.astro | 109 +++--- apps/docs/src/pages/docs/ssr.astro | 18 +- apps/docs/src/pages/docs/themes.astro | 86 +++-- apps/docs/src/pages/docs/typescript.astro | 33 +- package-lock.json | 320 +++++------------- 66 files changed, 2214 insertions(+), 1515 deletions(-) diff --git a/apps/docs/src/components/demos/ApprovalWorkflowDemo.tsx b/apps/docs/src/components/demos/ApprovalWorkflowDemo.tsx index f7783497..01d99089 100644 --- a/apps/docs/src/components/demos/ApprovalWorkflowDemo.tsx +++ b/apps/docs/src/components/demos/ApprovalWorkflowDemo.tsx @@ -14,27 +14,83 @@ interface Request { } const STATUS_LABEL: Record = { - 'pending': 'Pending', - 'approved': 'Approved', - 'rejected': 'Rejected', + pending: 'Pending', + approved: 'Approved', + rejected: 'Rejected', 'needs-info': 'Needs info', }; const STATUS_CLASS: Record = { - 'pending': 'bg-yellow-50 text-yellow-700 border border-yellow-200', - 'approved': 'bg-green-50 text-green-700 border border-green-200', - 'rejected': 'bg-red-50 text-red-700 border border-red-200', + pending: 'bg-yellow-50 text-yellow-700 border border-yellow-200', + approved: 'bg-green-50 text-green-700 border border-green-200', + rejected: 'bg-red-50 text-red-700 border border-red-200', 'needs-info': 'bg-blue-50 text-blue-700 border border-blue-200', }; const initialData: Request[] = [ - { id: 1, title: 'New MacBook Pro', requester: 'Sam Rivera', department: 'Engineering', amount: 2499, status: 'pending', submittedAt: '2024-05-14' }, - { id: 2, title: 'Figma Teams plan', requester: 'Priya Kapoor', department: 'Design', amount: 720, status: 'pending', submittedAt: '2024-05-14' }, - { id: 3, title: 'AWS reserved instance', requester: 'Aria Chen', department: 'Engineering', amount: 8400, status: 'needs-info', submittedAt: '2024-05-13' }, - { id: 4, title: 'Office chairs (x4)', requester: 'Marcus Webb', department: 'Product', amount: 1200, status: 'pending', submittedAt: '2024-05-12' }, - { id: 5, title: 'Tableau license', requester: 'Jordan Ellis', department: 'Analytics', amount: 1800, status: 'approved', submittedAt: '2024-05-10' }, - { id: 6, title: 'Conference travel', requester: 'Taylor Brooks', department: 'Sales', amount: 950, status: 'rejected', submittedAt: '2024-05-09' }, - { id: 7, title: 'Slack Enterprise', requester: 'Casey Morgan', department: 'Engineering', amount: 3600, status: 'pending', submittedAt: '2024-05-08' }, + { + id: 1, + title: 'New MacBook Pro', + requester: 'Sam Rivera', + department: 'Engineering', + amount: 2499, + status: 'pending', + submittedAt: '2024-05-14', + }, + { + id: 2, + title: 'Figma Teams plan', + requester: 'Priya Kapoor', + department: 'Design', + amount: 720, + status: 'pending', + submittedAt: '2024-05-14', + }, + { + id: 3, + title: 'AWS reserved instance', + requester: 'Aria Chen', + department: 'Engineering', + amount: 8400, + status: 'needs-info', + submittedAt: '2024-05-13', + }, + { + id: 4, + title: 'Office chairs (x4)', + requester: 'Marcus Webb', + department: 'Product', + amount: 1200, + status: 'pending', + submittedAt: '2024-05-12', + }, + { + id: 5, + title: 'Tableau license', + requester: 'Jordan Ellis', + department: 'Analytics', + amount: 1800, + status: 'approved', + submittedAt: '2024-05-10', + }, + { + id: 6, + title: 'Conference travel', + requester: 'Taylor Brooks', + department: 'Sales', + amount: 950, + status: 'rejected', + submittedAt: '2024-05-09', + }, + { + id: 7, + title: 'Slack Enterprise', + requester: 'Casey Morgan', + department: 'Engineering', + amount: 3600, + status: 'pending', + submittedAt: '2024-05-08', + }, ]; const ACTIONABLE: Status[] = ['pending', 'needs-info']; @@ -51,7 +107,7 @@ export default function ApprovalWorkflowDemo() { } function applyStatus(ids: number[], next: Status) { - setData(prev => prev.map(r => ids.includes(r.id) ? { ...r, status: next } : r)); + setData(prev => prev.map(r => (ids.includes(r.id) ? { ...r, status: next } : r))); ref.current?.clearSelectedRows(); showToast(`${ids.length} request${ids.length !== 1 ? 's' : ''} marked as "${STATUS_LABEL[next]}"`); } @@ -60,9 +116,9 @@ export default function ApprovalWorkflowDemo() { const selectedIds = actionable.map(r => r.id); const columns: TableColumn[] = [ - { id: 'title', name: 'Request', selector: r => r.title, grow: 2 }, - { id: 'requester', name: 'Requester', selector: r => r.requester, sortable: true }, - { id: 'department', name: 'Dept', selector: r => r.department, sortable: true, width: '120px' }, + { id: 'title', name: 'Request', selector: r => r.title, grow: 2 }, + { id: 'requester', name: 'Requester', selector: r => r.requester, sortable: true }, + { id: 'department', name: 'Dept', selector: r => r.department, sortable: true, width: '120px' }, { id: 'amount', name: 'Amount', @@ -79,9 +135,7 @@ export default function ApprovalWorkflowDemo() { sortable: true, width: '130px', cell: r => ( - - {STATUS_LABEL[r.status]} - + {STATUS_LABEL[r.status]} ), }, { id: 'submittedAt', name: 'Submitted', selector: r => r.submittedAt, sortable: true, width: '115px' }, diff --git a/apps/docs/src/components/demos/AuditLogDemo.tsx b/apps/docs/src/components/demos/AuditLogDemo.tsx index e5a2ed61..9d780e2a 100644 --- a/apps/docs/src/components/demos/AuditLogDemo.tsx +++ b/apps/docs/src/components/demos/AuditLogDemo.tsx @@ -14,35 +14,155 @@ interface LogEntry { } const SEVERITY_STYLE: Record = { - info: 'bg-blue-50 text-blue-700 border border-blue-100', - warning: 'bg-yellow-50 text-yellow-700 border border-yellow-100', - error: 'bg-red-50 text-red-700 border border-red-100', + info: 'bg-blue-50 text-blue-700 border border-blue-100', + warning: 'bg-yellow-50 text-yellow-700 border border-yellow-100', + error: 'bg-red-50 text-red-700 border border-red-100', critical: 'bg-red-100 text-red-900 border border-red-300 font-bold', }; const ALL_LOGS: LogEntry[] = [ - { id: 1, timestamp: '2024-05-16 09:01:12', severity: 'info', user: 'aria.chen', action: 'LOGIN', resource: '/dashboard', ip: '10.0.1.42' }, - { id: 2, timestamp: '2024-05-16 09:03:44', severity: 'info', user: 'marcus.webb', action: 'VIEW', resource: '/reports/q1', ip: '10.0.1.18' }, - { id: 3, timestamp: '2024-05-16 09:12:05', severity: 'warning', user: 'aria.chen', action: 'EXPORT', resource: '/users/all', ip: '10.0.1.42' }, - { id: 4, timestamp: '2024-05-16 09:15:30', severity: 'error', user: 'unknown', action: 'LOGIN_FAILED', resource: '/auth', ip: '203.0.113.7' }, - { id: 5, timestamp: '2024-05-16 09:15:31', severity: 'error', user: 'unknown', action: 'LOGIN_FAILED', resource: '/auth', ip: '203.0.113.7' }, - { id: 6, timestamp: '2024-05-16 09:15:32', severity: 'critical', user: 'unknown', action: 'BRUTE_FORCE', resource: '/auth', ip: '203.0.113.7' }, - { id: 7, timestamp: '2024-05-16 09:22:18', severity: 'info', user: 'priya.kapoor', action: 'UPDATE', resource: '/settings/theme', ip: '10.0.1.55' }, - { id: 8, timestamp: '2024-05-16 09:31:00', severity: 'warning', user: 'jordan.ellis', action: 'DELETE', resource: '/reports/draft-4', ip: '10.0.2.11' }, - { id: 9, timestamp: '2024-05-16 09:45:09', severity: 'info', user: 'sam.rivera', action: 'DEPLOY', resource: '/infra/staging', ip: '10.0.1.99' }, - { id: 10, timestamp: '2024-05-16 10:02:44', severity: 'critical', user: 'system', action: 'DB_CONN_LOST', resource: '/db/primary', ip: '10.0.0.1' }, - { id: 11, timestamp: '2024-05-16 10:03:01', severity: 'critical', user: 'system', action: 'FAILOVER', resource: '/db/replica', ip: '10.0.0.2' }, - { id: 12, timestamp: '2024-05-16 10:15:22', severity: 'info', user: 'aria.chen', action: 'LOGOUT', resource: '/auth', ip: '10.0.1.42' }, - { id: 13, timestamp: '2024-05-16 10:28:33', severity: 'warning', user: 'taylor.brooks',action: 'PERMISSION_DENY', resource: '/admin/users', ip: '10.0.1.77' }, - { id: 14, timestamp: '2024-05-16 10:55:50', severity: 'error', user: 'system', action: 'DISK_FULL', resource: '/storage/logs', ip: '10.0.0.5' }, - { id: 15, timestamp: '2024-05-16 11:10:04', severity: 'info', user: 'marcus.webb', action: 'LOGIN', resource: '/dashboard', ip: '10.0.1.18' }, + { + id: 1, + timestamp: '2024-05-16 09:01:12', + severity: 'info', + user: 'aria.chen', + action: 'LOGIN', + resource: '/dashboard', + ip: '10.0.1.42', + }, + { + id: 2, + timestamp: '2024-05-16 09:03:44', + severity: 'info', + user: 'marcus.webb', + action: 'VIEW', + resource: '/reports/q1', + ip: '10.0.1.18', + }, + { + id: 3, + timestamp: '2024-05-16 09:12:05', + severity: 'warning', + user: 'aria.chen', + action: 'EXPORT', + resource: '/users/all', + ip: '10.0.1.42', + }, + { + id: 4, + timestamp: '2024-05-16 09:15:30', + severity: 'error', + user: 'unknown', + action: 'LOGIN_FAILED', + resource: '/auth', + ip: '203.0.113.7', + }, + { + id: 5, + timestamp: '2024-05-16 09:15:31', + severity: 'error', + user: 'unknown', + action: 'LOGIN_FAILED', + resource: '/auth', + ip: '203.0.113.7', + }, + { + id: 6, + timestamp: '2024-05-16 09:15:32', + severity: 'critical', + user: 'unknown', + action: 'BRUTE_FORCE', + resource: '/auth', + ip: '203.0.113.7', + }, + { + id: 7, + timestamp: '2024-05-16 09:22:18', + severity: 'info', + user: 'priya.kapoor', + action: 'UPDATE', + resource: '/settings/theme', + ip: '10.0.1.55', + }, + { + id: 8, + timestamp: '2024-05-16 09:31:00', + severity: 'warning', + user: 'jordan.ellis', + action: 'DELETE', + resource: '/reports/draft-4', + ip: '10.0.2.11', + }, + { + id: 9, + timestamp: '2024-05-16 09:45:09', + severity: 'info', + user: 'sam.rivera', + action: 'DEPLOY', + resource: '/infra/staging', + ip: '10.0.1.99', + }, + { + id: 10, + timestamp: '2024-05-16 10:02:44', + severity: 'critical', + user: 'system', + action: 'DB_CONN_LOST', + resource: '/db/primary', + ip: '10.0.0.1', + }, + { + id: 11, + timestamp: '2024-05-16 10:03:01', + severity: 'critical', + user: 'system', + action: 'FAILOVER', + resource: '/db/replica', + ip: '10.0.0.2', + }, + { + id: 12, + timestamp: '2024-05-16 10:15:22', + severity: 'info', + user: 'aria.chen', + action: 'LOGOUT', + resource: '/auth', + ip: '10.0.1.42', + }, + { + id: 13, + timestamp: '2024-05-16 10:28:33', + severity: 'warning', + user: 'taylor.brooks', + action: 'PERMISSION_DENY', + resource: '/admin/users', + ip: '10.0.1.77', + }, + { + id: 14, + timestamp: '2024-05-16 10:55:50', + severity: 'error', + user: 'system', + action: 'DISK_FULL', + resource: '/storage/logs', + ip: '10.0.0.5', + }, + { + id: 15, + timestamp: '2024-05-16 11:10:04', + severity: 'info', + user: 'marcus.webb', + action: 'LOGIN', + resource: '/dashboard', + ip: '10.0.1.18', + }, ]; const SEVERITIES: ('all' | Severity)[] = ['all', 'info', 'warning', 'error', 'critical']; const conditionalRowStyles: ConditionalStyles[] = [ { when: r => r.severity === 'critical', style: { backgroundColor: '#fef2f2' } }, - { when: r => r.severity === 'error', style: { backgroundColor: '#fff7f7' } }, + { when: r => r.severity === 'error', style: { backgroundColor: '#fff7f7' } }, ]; const columns: TableColumn[] = [ @@ -53,16 +173,25 @@ const columns: TableColumn[] = [ selector: r => r.severity, sortable: true, width: '110px', - cell: r => ( - - {r.severity} - - ), - }, - { id: 'user', name: 'User', selector: r => r.user, sortable: true, width: '145px' }, - { id: 'action', name: 'Action', selector: r => r.action, sortable: true, width: '145px', style: { fontFamily: 'monospace', fontSize: 12 } }, - { id: 'resource', name: 'Resource', selector: r => r.resource, grow: 1, style: { fontFamily: 'monospace', fontSize: 12 } }, - { id: 'ip', name: 'IP', selector: r => r.ip, width: '130px', style: { fontFamily: 'monospace', fontSize: 12 } }, + cell: r => {r.severity}, + }, + { id: 'user', name: 'User', selector: r => r.user, sortable: true, width: '145px' }, + { + id: 'action', + name: 'Action', + selector: r => r.action, + sortable: true, + width: '145px', + style: { fontFamily: 'monospace', fontSize: 12 }, + }, + { + id: 'resource', + name: 'Resource', + selector: r => r.resource, + grow: 1, + style: { fontFamily: 'monospace', fontSize: 12 }, + }, + { id: 'ip', name: 'IP', selector: r => r.ip, width: '130px', style: { fontFamily: 'monospace', fontSize: 12 } }, ]; export default function AuditLogDemo() { diff --git a/apps/docs/src/components/demos/ColumnPinningDemo.tsx b/apps/docs/src/components/demos/ColumnPinningDemo.tsx index 8e525f62..31dc6a83 100644 --- a/apps/docs/src/components/demos/ColumnPinningDemo.tsx +++ b/apps/docs/src/components/demos/ColumnPinningDemo.tsx @@ -143,7 +143,9 @@ function PinPicker({ }) { const btn = (active: boolean) => `px-2.5 py-1 rounded-md text-xs font-medium border transition-colors ${ - active ? 'bg-brand-600 text-white border-brand-600' : 'bg-white text-gray-600 border-gray-200 hover:border-gray-300' + active + ? 'bg-brand-600 text-white border-brand-600' + : 'bg-white text-gray-600 border-gray-200 hover:border-gray-300' }`; return (

diff --git a/apps/docs/src/components/demos/ContextMenuDemo.tsx b/apps/docs/src/components/demos/ContextMenuDemo.tsx index 878e74e9..22a2bbd4 100644 --- a/apps/docs/src/components/demos/ContextMenuDemo.tsx +++ b/apps/docs/src/components/demos/ContextMenuDemo.tsx @@ -14,11 +14,56 @@ interface Row { } const initialData: Row[] = [ - { id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', location: 'San Francisco', salary: 155000, startDate: '2019-03-11', status: 'Active' }, - { id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', location: 'New York', salary: 132000, startDate: '2021-07-02', status: 'Active' }, - { id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', location: 'Austin', salary: 118000, startDate: '2020-01-20', status: 'Active' }, - { id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', location: 'Seattle', salary: 143000, startDate: '2018-10-05', status: 'On Leave' }, - { id: 5, name: 'Sam Rivera', role: 'DevOps Engineer', department: 'Engineering', location: 'Remote', salary: 128000, startDate: '2022-04-14', status: 'Active' }, + { + id: 1, + name: 'Aria Chen', + role: 'Engineering Lead', + department: 'Engineering', + location: 'San Francisco', + salary: 155000, + startDate: '2019-03-11', + status: 'Active', + }, + { + id: 2, + name: 'Marcus Webb', + role: 'Product Manager', + department: 'Product', + location: 'New York', + salary: 132000, + startDate: '2021-07-02', + status: 'Active', + }, + { + id: 3, + name: 'Priya Kapoor', + role: 'Senior Designer', + department: 'Design', + location: 'Austin', + salary: 118000, + startDate: '2020-01-20', + status: 'Active', + }, + { + id: 4, + name: 'Jordan Ellis', + role: 'Data Scientist', + department: 'Analytics', + location: 'Seattle', + salary: 143000, + startDate: '2018-10-05', + status: 'On Leave', + }, + { + id: 5, + name: 'Sam Rivera', + role: 'DevOps Engineer', + department: 'Engineering', + location: 'Remote', + salary: 128000, + startDate: '2022-04-14', + status: 'Active', + }, ]; // Fixed widths so the table overflows horizontally — otherwise pinning a column @@ -28,7 +73,15 @@ const columns: TableColumn[] = [ { id: 'role', name: 'Role', selector: row => row.role, sortable: true, width: '220px' }, { id: 'department', name: 'Department', selector: row => row.department, sortable: true, width: '180px' }, { id: 'location', name: 'Location', selector: row => row.location, sortable: true, width: '200px' }, - { id: 'salary', name: 'Salary', selector: row => row.salary, sortable: true, right: true, width: '140px', format: row => `$${row.salary.toLocaleString()}` }, + { + id: 'salary', + name: 'Salary', + selector: row => row.salary, + sortable: true, + right: true, + width: '140px', + format: row => `$${row.salary.toLocaleString()}`, + }, { id: 'startDate', name: 'Start Date', selector: row => row.startDate, sortable: true, width: '150px' }, { id: 'status', name: 'Status', selector: row => row.status, width: '140px' }, ]; @@ -51,8 +104,8 @@ export default function ContextMenuDemo(): JSX.Element { highlightOnHover />

- Last action: {lastAction} · Right-click a header, or use its ⋮ button, to sort, pin, hide, - or reset columns. + Last action: {lastAction} · Right-click a header, or use its ⋮ button, to sort, pin, hide, or + reset columns.

); diff --git a/apps/docs/src/components/demos/CsvImportDemo.tsx b/apps/docs/src/components/demos/CsvImportDemo.tsx index af0cd727..a3845b57 100644 --- a/apps/docs/src/components/demos/CsvImportDemo.tsx +++ b/apps/docs/src/components/demos/CsvImportDemo.tsx @@ -24,8 +24,8 @@ function parseCsv(text: string): Contact[] { } const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, - { id: 'email', name: 'Email', selector: r => r.email, sortable: true, grow: 1 }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, + { id: 'email', name: 'Email', selector: r => r.email, sortable: true, grow: 1 }, { id: 'company', name: 'Company', selector: r => r.company, sortable: true }, ]; @@ -59,7 +59,10 @@ export default function CsvImportDemo() { /> ))} - + ); } diff --git a/apps/docs/src/components/demos/FooterCustomDemo.tsx b/apps/docs/src/components/demos/FooterCustomDemo.tsx index 89267bb4..27cf28ea 100644 --- a/apps/docs/src/components/demos/FooterCustomDemo.tsx +++ b/apps/docs/src/components/demos/FooterCustomDemo.tsx @@ -11,22 +11,22 @@ interface Row { } const ALL_DATA: Row[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, bonus: 12000 }, - { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, bonus: 9500 }, - { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, bonus: 7800 }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, bonus: 11200 }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, bonus: 9000 }, - { id: 6, name: 'Taylor Brooks', department: 'Engineering', salary: 122000, bonus: 8400 }, - { id: 7, name: 'Casey Morgan', department: 'Product', salary: 108000, bonus: 6600 }, - { id: 8, name: 'Alex Kim', department: 'Analytics', salary: 137000, bonus: 10100 }, - { id: 9, name: 'Morgan Lee', department: 'Design', salary: 114000, bonus: 7200 }, - { id: 10, name: 'Drew Park', department: 'Engineering', salary: 141000, bonus: 10800 }, + { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, bonus: 12000 }, + { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, bonus: 9500 }, + { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, bonus: 7800 }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, bonus: 11200 }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, bonus: 9000 }, + { id: 6, name: 'Taylor Brooks', department: 'Engineering', salary: 122000, bonus: 8400 }, + { id: 7, name: 'Casey Morgan', department: 'Product', salary: 108000, bonus: 6600 }, + { id: 8, name: 'Alex Kim', department: 'Analytics', salary: 137000, bonus: 10100 }, + { id: 9, name: 'Morgan Lee', department: 'Design', salary: 114000, bonus: 7200 }, + { id: 10, name: 'Drew Park', department: 'Engineering', salary: 141000, bonus: 10800 }, ]; function SummaryFooter({ rows }: FooterComponentProps) { const totalSalary = rows.reduce((s, r) => s + r.salary, 0); - const totalBonus = rows.reduce((s, r) => s + r.bonus, 0); - const avgSalary = rows.length ? Math.round(totalSalary / rows.length) : 0; + const totalBonus = rows.reduce((s, r) => s + r.bonus, 0); + const avgSalary = rows.length ? Math.round(totalSalary / rows.length) : 0; return (
) { } const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, { id: 'salary', @@ -109,13 +109,7 @@ export default function FooterCustomDemo() { ))}
- + ); } diff --git a/apps/docs/src/components/demos/HeadlessDemo.tsx b/apps/docs/src/components/demos/HeadlessDemo.tsx index f11900b2..f474c374 100644 --- a/apps/docs/src/components/demos/HeadlessDemo.tsx +++ b/apps/docs/src/components/demos/HeadlessDemo.tsx @@ -1,5 +1,12 @@ import React from 'react'; -import { useColumns, useTableState, useTableData, useColumnFilter, type TableColumn, type FilterState } from 'react-data-table-component'; +import { + useColumns, + useTableState, + useTableData, + useColumnFilter, + type TableColumn, + type FilterState, +} from 'react-data-table-component'; interface Employee { id: number; diff --git a/apps/docs/src/components/demos/IconsDemo.tsx b/apps/docs/src/components/demos/IconsDemo.tsx index b9c38c88..954efc13 100644 --- a/apps/docs/src/components/demos/IconsDemo.tsx +++ b/apps/docs/src/components/demos/IconsDemo.tsx @@ -4,39 +4,102 @@ import { type TableColumn } from 'react-data-table-component'; // Inline SVG icon primitives — no external dependency const ChevronLeft = () => ( - + ); const ChevronRight = () => ( - + ); const ChevronsLeft = () => ( - + ); const ChevronsRight = () => ( - + ); const ChevronDown = () => ( - + ); const ChevronRightSmall = () => ( - + ); const ArrowUp = () => ( - + @@ -51,33 +114,105 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', bio: 'Leads the platform team with a focus on reliability.' }, - { id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', bio: 'Drives roadmap strategy across three product lines.' }, - { id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', bio: 'Owns the design system and mobile experience.' }, - { id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', bio: 'Builds ML pipelines for churn and revenue forecasting.' }, - { id: 5, name: 'Sam Rivera', role: 'SRE', department: 'Engineering', bio: 'Manages uptime, incidents, and deployment pipelines.' }, - { id: 6, name: 'Taylor Brooks',role: 'Sales Lead', department: 'Sales', bio: 'Responsible for enterprise accounts in EMEA.' }, - { id: 7, name: 'Casey Morgan', role: 'HR Manager', department: 'HR', bio: 'Owns hiring, onboarding, and culture initiatives.' }, - { id: 8, name: 'Alex Kim', role: 'Staff Engineer', department: 'Engineering', bio: 'Architect for the data platform and internal APIs.' }, - { id: 9, name: 'Morgan Lee', role: 'Designer', department: 'Design', bio: 'Works on user research and prototype validation.' }, - { id:10, name: 'Drew Park', role: 'Analyst', department: 'Analytics', bio: 'Produces dashboards and weekly business metrics.' }, - { id:11, name: 'Riley Nguyen', role: 'Frontend Eng', department: 'Engineering', bio: 'Builds customer-facing product surfaces in React.' }, - { id:12, name: 'Jamie Okafor', role: 'Backend Eng', department: 'Engineering', bio: 'Maintains the microservices and event bus.' }, + { + id: 1, + name: 'Aria Chen', + role: 'Engineering Lead', + department: 'Engineering', + bio: 'Leads the platform team with a focus on reliability.', + }, + { + id: 2, + name: 'Marcus Webb', + role: 'Product Manager', + department: 'Product', + bio: 'Drives roadmap strategy across three product lines.', + }, + { + id: 3, + name: 'Priya Kapoor', + role: 'Senior Designer', + department: 'Design', + bio: 'Owns the design system and mobile experience.', + }, + { + id: 4, + name: 'Jordan Ellis', + role: 'Data Scientist', + department: 'Analytics', + bio: 'Builds ML pipelines for churn and revenue forecasting.', + }, + { + id: 5, + name: 'Sam Rivera', + role: 'SRE', + department: 'Engineering', + bio: 'Manages uptime, incidents, and deployment pipelines.', + }, + { + id: 6, + name: 'Taylor Brooks', + role: 'Sales Lead', + department: 'Sales', + bio: 'Responsible for enterprise accounts in EMEA.', + }, + { + id: 7, + name: 'Casey Morgan', + role: 'HR Manager', + department: 'HR', + bio: 'Owns hiring, onboarding, and culture initiatives.', + }, + { + id: 8, + name: 'Alex Kim', + role: 'Staff Engineer', + department: 'Engineering', + bio: 'Architect for the data platform and internal APIs.', + }, + { + id: 9, + name: 'Morgan Lee', + role: 'Designer', + department: 'Design', + bio: 'Works on user research and prototype validation.', + }, + { + id: 10, + name: 'Drew Park', + role: 'Analyst', + department: 'Analytics', + bio: 'Produces dashboards and weekly business metrics.', + }, + { + id: 11, + name: 'Riley Nguyen', + role: 'Frontend Eng', + department: 'Engineering', + bio: 'Builds customer-facing product surfaces in React.', + }, + { + id: 12, + name: 'Jamie Okafor', + role: 'Backend Eng', + department: 'Engineering', + bio: 'Maintains the microservices and event bus.', + }, ]; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Role', selector: r => r.role }, { name: 'Department', selector: r => r.department, sortable: true }, ]; type Mode = 'default' | 'custom-pagination' | 'custom-expander' | 'custom-sort'; const modes: { key: Mode; label: string }[] = [ - { key: 'default', label: 'Default icons' }, + { key: 'default', label: 'Default icons' }, { key: 'custom-pagination', label: 'Custom pagination' }, - { key: 'custom-expander', label: 'Custom expander' }, - { key: 'custom-sort', label: 'Custom sort' }, + { key: 'custom-expander', label: 'Custom expander' }, + { key: 'custom-sort', label: 'Custom sort' }, ]; const ExpandedRow = ({ data: row }: { data: Employee }) => ( @@ -90,8 +225,8 @@ export default function IconsDemo() { const [mode, setMode] = useState('default'); const btnBase = 'px-2.5 py-1 rounded-md text-xs font-medium border transition-colors cursor-pointer'; - const btnOn = 'bg-brand-600 text-white border-brand-600'; - const btnOff = 'bg-white text-gray-600 border-gray-200 hover:border-gray-300'; + const btnOn = 'bg-brand-600 text-white border-brand-600'; + const btnOff = 'bg-white text-gray-600 border-gray-200 hover:border-gray-300'; const showExpander = mode === 'custom-expander' || mode === 'default'; @@ -99,7 +234,11 @@ export default function IconsDemo() {
{modes.map(m => ( - ))} @@ -113,27 +252,33 @@ export default function IconsDemo() { highlightOnHover // Pagination icons - {...(mode === 'custom-pagination' ? { - paginationIconFirstPage: , - paginationIconLastPage: , - paginationIconPrevious: , - paginationIconNext: , - } : {})} + {...(mode === 'custom-pagination' + ? { + paginationIconFirstPage: , + paginationIconLastPage: , + paginationIconPrevious: , + paginationIconNext: , + } + : {})} // Sort icon - {...(mode === 'custom-sort' ? { - sortIcon: , - } : {})} + {...(mode === 'custom-sort' + ? { + sortIcon: , + } + : {})} // Expandable rows expandableRows={showExpander} expandableRowsComponent={ExpandedRow} - {...(mode === 'custom-expander' ? { - expandableIcon: { - collapsed: , - expanded: , - }, - } : {})} + {...(mode === 'custom-expander' + ? { + expandableIcon: { + collapsed: , + expanded: , + }, + } + : {})} /> {mode === 'custom-pagination' && ( @@ -143,7 +288,9 @@ export default function IconsDemo() {

Expander icons replaced with inline SVG chevrons.

)} {mode === 'custom-sort' && ( -

Sort icon replaced with an up-arrow. Click a sortable column header to see it.

+

+ Sort icon replaced with an up-arrow. Click a sortable column header to see it. +

)}
); diff --git a/apps/docs/src/components/demos/InlineRowActionsDemo.tsx b/apps/docs/src/components/demos/InlineRowActionsDemo.tsx index 1d721adf..f07fb08e 100644 --- a/apps/docs/src/components/demos/InlineRowActionsDemo.tsx +++ b/apps/docs/src/components/demos/InlineRowActionsDemo.tsx @@ -10,12 +10,12 @@ interface Employee { } const seed: Employee[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', role: 'Engineering Lead', email: 'aria@example.com' }, - { id: 2, name: 'Marcus Webb', department: 'Product', role: 'Product Manager', email: 'marcus@example.com' }, - { id: 3, name: 'Priya Kapoor', department: 'Design', role: 'Senior Designer', email: 'priya@example.com' }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', role: 'Data Scientist', email: 'jordan@example.com' }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', role: 'DevOps Engineer', email: 'sam@example.com' }, - { id: 6, name: 'Taylor Brooks', department: 'Sales', role: 'Account Manager', email: 'taylor@example.com' }, + { id: 1, name: 'Aria Chen', department: 'Engineering', role: 'Engineering Lead', email: 'aria@example.com' }, + { id: 2, name: 'Marcus Webb', department: 'Product', role: 'Product Manager', email: 'marcus@example.com' }, + { id: 3, name: 'Priya Kapoor', department: 'Design', role: 'Senior Designer', email: 'priya@example.com' }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', role: 'Data Scientist', email: 'jordan@example.com' }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', role: 'DevOps Engineer', email: 'sam@example.com' }, + { id: 6, name: 'Taylor Brooks', department: 'Sales', role: 'Account Manager', email: 'taylor@example.com' }, ]; type ModalState = @@ -24,7 +24,12 @@ type ModalState = | { type: 'delete'; row: Employee } | { type: 'duplicate'; row: Employee }; -function DropdownMenu({ row, onEdit, onDuplicate, onDelete }: { +function DropdownMenu({ + row, + onEdit, + onDuplicate, + onDelete, +}: { row: Employee; onEdit: (r: Employee) => void; onDuplicate: (r: Employee) => void; @@ -45,31 +50,48 @@ function DropdownMenu({ row, onEdit, onDuplicate, onDelete }: { return (
{open && (
- + +
@@ -172,13 +210,31 @@ export default function InlineRowActionsDemo() { {/* Duplicate confirmation */} {modal.type === 'duplicate' && ( -
setModal({ type: 'none' })}> -
e.stopPropagation()}> +
setModal({ type: 'none' })} + > +
e.stopPropagation()} + >

Duplicate row?

-

A copy of {modal.row.name} will be added to the list.

+

+ A copy of {modal.row.name} will be added to the list. +

- - + +
@@ -186,13 +242,31 @@ export default function InlineRowActionsDemo() { {/* Delete confirmation */} {modal.type === 'delete' && ( -
setModal({ type: 'none' })}> -
e.stopPropagation()}> +
setModal({ type: 'none' })} + > +
e.stopPropagation()} + >

Delete employee?

-

This will permanently remove {modal.row.name}.

+

+ This will permanently remove {modal.row.name}. +

- - + +
diff --git a/apps/docs/src/components/demos/KeyboardArrowNavDemo.tsx b/apps/docs/src/components/demos/KeyboardArrowNavDemo.tsx index f86c607b..d4f3caf7 100644 --- a/apps/docs/src/components/demos/KeyboardArrowNavDemo.tsx +++ b/apps/docs/src/components/demos/KeyboardArrowNavDemo.tsx @@ -27,9 +27,9 @@ export default function KeyboardArrowNavDemo() {

A plain read-only table. Click a cell, then use to move,{' '} - Home/End to jump to the row edges, and Ctrl+Home/ - Ctrl+End to jump to the grid corners. No editing, sorting, selection, or expansion - here — just movement, which works on any table. + Home/End to jump to the row edges, and Ctrl+Home/Ctrl+ + End to jump to the grid corners. No editing, sorting, selection, or expansion here — just movement, + which works on any table.

diff --git a/apps/docs/src/components/demos/KeyboardEditDemo.tsx b/apps/docs/src/components/demos/KeyboardEditDemo.tsx index 020109ab..4858dcd4 100644 --- a/apps/docs/src/components/demos/KeyboardEditDemo.tsx +++ b/apps/docs/src/components/demos/KeyboardEditDemo.tsx @@ -44,8 +44,8 @@ export default function KeyboardEditDemo() {

Arrow to Name or Salary, then press Enter or F2 to open the editor. Enter{' '} - commits and Escape cancels — either way, focus returns to the cell so arrow-key navigation - continues immediately. Department has no editor, so Enter/F2 do nothing there. + commits and Escape cancels — either way, focus returns to the cell so arrow-key navigation continues + immediately. Department has no editor, so Enter/F2 do nothing there.

diff --git a/apps/docs/src/components/demos/KeyboardSortDemo.tsx b/apps/docs/src/components/demos/KeyboardSortDemo.tsx index 19d3afc1..4162d349 100644 --- a/apps/docs/src/components/demos/KeyboardSortDemo.tsx +++ b/apps/docs/src/components/demos/KeyboardSortDemo.tsx @@ -33,9 +33,9 @@ export default function KeyboardSortDemo() { return (

- from the first body row moves into the header. Enter or Space on a - sortable header cycles ascending → descending → unsorted, same as a click. from the header - returns to the body in the same column. + from the first body row moves into the header. Enter or Space on a sortable + header cycles ascending → descending → unsorted, same as a click. from the header returns to the + body in the same column.

diff --git a/apps/docs/src/components/demos/LiveUpdatesDemo.tsx b/apps/docs/src/components/demos/LiveUpdatesDemo.tsx index 204c0b63..a5d0d7e3 100644 --- a/apps/docs/src/components/demos/LiveUpdatesDemo.tsx +++ b/apps/docs/src/components/demos/LiveUpdatesDemo.tsx @@ -9,16 +9,16 @@ interface Ticker { } const INITIAL: Ticker[] = [ - { symbol: 'ACME', name: 'Acme Corp', price: 182.4, change: 0 }, - { symbol: 'BLTC', name: 'Baltic Freight', price: 64.12, change: 0 }, - { symbol: 'CNDL', name: 'Candle Systems', price: 305.9, change: 0 }, - { symbol: 'DYNM', name: 'Dynamo Energy', price: 48.77, change: 0 }, - { symbol: 'EVRG', name: 'Evergreen Foods', price: 96.3, change: 0 }, + { symbol: 'ACME', name: 'Acme Corp', price: 182.4, change: 0 }, + { symbol: 'BLTC', name: 'Baltic Freight', price: 64.12, change: 0 }, + { symbol: 'CNDL', name: 'Candle Systems', price: 305.9, change: 0 }, + { symbol: 'DYNM', name: 'Dynamo Energy', price: 48.77, change: 0 }, + { symbol: 'EVRG', name: 'Evergreen Foods', price: 96.3, change: 0 }, ]; const columns: TableColumn[] = [ { id: 'symbol', name: 'Symbol', selector: r => r.symbol, sortable: true, width: '110px', style: { fontWeight: 600 } }, - { id: 'name', name: 'Name', selector: r => r.name, grow: 1 }, + { id: 'name', name: 'Name', selector: r => r.name, grow: 1 }, { id: 'price', name: 'Price', @@ -50,20 +50,29 @@ export default function LiveUpdatesDemo() { const symbol = INITIAL[Math.floor(Math.random() * INITIAL.length)].symbol; const delta = +(Math.random() * 4 - 2).toFixed(2); - setData(prev => prev.map(r => (r.symbol === symbol ? { ...r, price: +(r.price + delta).toFixed(2), change: delta } : r))); + setData(prev => + prev.map(r => (r.symbol === symbol ? { ...r, price: +(r.price + delta).toFixed(2), change: delta } : r)), + ); setFlashed(prev => new Set(prev).add(symbol)); - setTimeout(() => setFlashed(prev => { - const next = new Set(prev); - next.delete(symbol); - return next; - }), 600); + setTimeout( + () => + setFlashed(prev => { + const next = new Set(prev); + next.delete(symbol); + return next; + }), + 600, + ); }, 1200); return () => clearInterval(tick); }, []); const conditionalRowStyles: ConditionalStyles[] = [ - { when: r => flashed.has(r.symbol), style: { backgroundColor: '#fefce8', transition: 'background-color 0.6s ease' } }, + { + when: r => flashed.has(r.symbol), + style: { backgroundColor: '#fefce8', transition: 'background-color 0.6s ease' }, + }, ]; return ( diff --git a/apps/docs/src/components/demos/LoadingDemo.tsx b/apps/docs/src/components/demos/LoadingDemo.tsx index 22676b15..1d2a4f01 100644 --- a/apps/docs/src/components/demos/LoadingDemo.tsx +++ b/apps/docs/src/components/demos/LoadingDemo.tsx @@ -11,18 +11,18 @@ interface Row { } const ROWS: Row[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, status: 'Active' }, - { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, status: 'Remote' }, - { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, status: 'Active' }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, status: 'On Leave' }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, status: 'Contractor' }, + { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, status: 'Active' }, + { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, status: 'Remote' }, + { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, status: 'Active' }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, status: 'On Leave' }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, status: 'Contractor' }, ]; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Salary', selector: r => r.salary, right: true, format: r => `$${r.salary.toLocaleString()}` }, - { name: 'Status', selector: r => r.status }, + { name: 'Salary', selector: r => r.salary, right: true, format: r => `$${r.salary.toLocaleString()}` }, + { name: 'Status', selector: r => r.status }, ]; function delay(ms: number) { @@ -63,7 +63,8 @@ export default function LoadingDemo() { setMode('idle'); } - const btnBase = 'px-3 py-1.5 rounded-md text-xs font-medium border transition-colors disabled:opacity-40 disabled:cursor-not-allowed'; + const btnBase = + 'px-3 py-1.5 rounded-md text-xs font-medium border transition-colors disabled:opacity-40 disabled:cursor-not-allowed'; const btnPrimary = `${btnBase} bg-brand-600 text-white border-brand-600 hover:bg-brand-700`; const btnSecondary = `${btnBase} bg-white text-gray-600 border-gray-200 hover:border-gray-400`; @@ -86,12 +87,7 @@ export default function LoadingDemo() { Use custom loader {mode !== 'idle' && ( diff --git a/apps/docs/src/components/demos/MobileDemo.tsx b/apps/docs/src/components/demos/MobileDemo.tsx index b464d322..d4330e98 100644 --- a/apps/docs/src/components/demos/MobileDemo.tsx +++ b/apps/docs/src/components/demos/MobileDemo.tsx @@ -12,7 +12,19 @@ interface Employee { const data: Employee[] = Array.from({ length: 20 }, (_, i) => ({ id: i + 1, - name: ['Aria Chen', 'Marcus Webb', 'Priya Kapoor', 'Jordan Ellis', 'Sam Rivera', 'Taylor Brooks', 'Casey Morgan', 'Alex Kim', 'Morgan Lee', 'Drew Park'][i % 10] + (i >= 10 ? ` ${Math.floor(i / 10) + 1}` : ''), + name: + [ + 'Aria Chen', + 'Marcus Webb', + 'Priya Kapoor', + 'Jordan Ellis', + 'Sam Rivera', + 'Taylor Brooks', + 'Casey Morgan', + 'Alex Kim', + 'Morgan Lee', + 'Drew Park', + ][i % 10] + (i >= 10 ? ` ${Math.floor(i / 10) + 1}` : ''), department: ['Engineering', 'Product', 'Design', 'Analytics', 'Sales', 'HR'][i % 6], salary: 80000 + i * 4200, status: (['Active', 'Remote', 'Contractor', 'On Leave'] as const)[i % 4], @@ -57,9 +69,7 @@ export default function MobileDemo() { selector: r => r.status, center: true, cell: r => ( - - {r.status} - + {r.status} ), }, ]; @@ -119,7 +129,8 @@ export default function MobileDemo() { {isNarrow && (

- Department and Salary are hidden at this width — use omit or hide: "sm" to drop columns on small screens. + Department and Salary are hidden at this width — use omit or{' '} + hide: "sm" to drop columns on small screens.

)}
diff --git a/apps/docs/src/components/demos/MultiSortDemo.tsx b/apps/docs/src/components/demos/MultiSortDemo.tsx index 9071d507..4916e2ba 100644 --- a/apps/docs/src/components/demos/MultiSortDemo.tsx +++ b/apps/docs/src/components/demos/MultiSortDemo.tsx @@ -46,8 +46,8 @@ export default function MultiSortDemo() { return (

- Ctrl-click (⌘-click on macOS) a second column header to add it to the sort. Cycle each column - ascending → descending → off. + Ctrl-click (⌘-click on macOS) a second column header to add it to the sort. Cycle each column ascending → + descending → off.

{ - try { return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}'); } - catch { return {}; } + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}'); + } catch { + return {}; + } } const columnDefs: TableColumn[] = [ diff --git a/apps/docs/src/components/demos/RTLDemo.tsx b/apps/docs/src/components/demos/RTLDemo.tsx index d153f6f6..19ac8e69 100644 --- a/apps/docs/src/components/demos/RTLDemo.tsx +++ b/apps/docs/src/components/demos/RTLDemo.tsx @@ -12,18 +12,39 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', nameAr: 'أريا تشن', department: 'Engineering', departmentAr: 'هندسة', salary: 155000 }, - { id: 2, name: 'Marcus Webb', nameAr: 'ماركوس ويب', department: 'Product', departmentAr: 'منتج', salary: 132000 }, - { id: 3, name: 'Priya Kapoor', nameAr: 'بريا كابور', department: 'Design', departmentAr: 'تصميم', salary: 118000 }, - { id: 4, name: 'Jordan Ellis', nameAr: 'جوردان إليس', department: 'Analytics', departmentAr: 'تحليلات', salary: 143000 }, - { id: 5, name: 'Sam Rivera', nameAr: 'سام ريفيرا', department: 'Engineering', departmentAr: 'هندسة', salary: 128000 }, - { id: 6, name: 'Taylor Brooks', nameAr: 'تايلور بروكس', department: 'Sales', departmentAr: 'مبيعات', salary: 97000 }, - { id: 7, name: 'Morgan Lee', nameAr: 'مورغان لي', department: 'Engineering', departmentAr: 'هندسة', salary: 162000 }, - { id: 8, name: 'Casey Park', nameAr: 'كيسي بارك', department: 'Design', departmentAr: 'تصميم', salary: 109000 }, - { id: 9, name: 'Drew Santos', nameAr: 'درو سانتوس', department: 'Product', departmentAr: 'منتج', salary: 138000 }, - { id: 10, name: 'Avery Johnson', nameAr: 'أفيري جونسون', department: 'Sales', departmentAr: 'مبيعات', salary: 104000 }, - { id: 11, name: 'Quinn Torres', nameAr: 'كوين توريس', department: 'Design', departmentAr: 'تصميم', salary: 109000 }, - { id: 12, name: 'Devon Walsh', nameAr: 'ديفون والش', department: 'Engineering', departmentAr: 'هندسة', salary: 134000 }, + { id: 1, name: 'Aria Chen', nameAr: 'أريا تشن', department: 'Engineering', departmentAr: 'هندسة', salary: 155000 }, + { id: 2, name: 'Marcus Webb', nameAr: 'ماركوس ويب', department: 'Product', departmentAr: 'منتج', salary: 132000 }, + { id: 3, name: 'Priya Kapoor', nameAr: 'بريا كابور', department: 'Design', departmentAr: 'تصميم', salary: 118000 }, + { + id: 4, + name: 'Jordan Ellis', + nameAr: 'جوردان إليس', + department: 'Analytics', + departmentAr: 'تحليلات', + salary: 143000, + }, + { id: 5, name: 'Sam Rivera', nameAr: 'سام ريفيرا', department: 'Engineering', departmentAr: 'هندسة', salary: 128000 }, + { id: 6, name: 'Taylor Brooks', nameAr: 'تايلور بروكس', department: 'Sales', departmentAr: 'مبيعات', salary: 97000 }, + { id: 7, name: 'Morgan Lee', nameAr: 'مورغان لي', department: 'Engineering', departmentAr: 'هندسة', salary: 162000 }, + { id: 8, name: 'Casey Park', nameAr: 'كيسي بارك', department: 'Design', departmentAr: 'تصميم', salary: 109000 }, + { id: 9, name: 'Drew Santos', nameAr: 'درو سانتوس', department: 'Product', departmentAr: 'منتج', salary: 138000 }, + { + id: 10, + name: 'Avery Johnson', + nameAr: 'أفيري جونسون', + department: 'Sales', + departmentAr: 'مبيعات', + salary: 104000, + }, + { id: 11, name: 'Quinn Torres', nameAr: 'كوين توريس', department: 'Design', departmentAr: 'تصميم', salary: 109000 }, + { + id: 12, + name: 'Devon Walsh', + nameAr: 'ديفون والش', + department: 'Engineering', + departmentAr: 'هندسة', + salary: 134000, + }, ]; const DIRECTIONS = [ @@ -39,7 +60,7 @@ export default function RTLDemo() { const columns: TableColumn[] = [ { name: isRTL ? 'الاسم' : 'Name', - selector: r => isRTL ? r.nameAr : r.name, + selector: r => (isRTL ? r.nameAr : r.name), sortable: true, width: '200px', minWidth: '120px', @@ -47,7 +68,7 @@ export default function RTLDemo() { }, { name: isRTL ? 'القسم' : 'Department', - selector: r => isRTL ? r.departmentAr : r.department, + selector: r => (isRTL ? r.departmentAr : r.department), sortable: true, minWidth: '140px', reorder: true, diff --git a/apps/docs/src/components/demos/RowGroupingDemo.tsx b/apps/docs/src/components/demos/RowGroupingDemo.tsx index 046f3a9c..4c3a12bc 100644 --- a/apps/docs/src/components/demos/RowGroupingDemo.tsx +++ b/apps/docs/src/components/demos/RowGroupingDemo.tsx @@ -16,13 +16,13 @@ interface RegionGroup { } const ORDERS: Order[] = [ - { id: 1, region: 'West', customer: 'Acme Corp', amount: 4200 }, - { id: 2, region: 'West', customer: 'Blue Harbor Inc', amount: 1850 }, - { id: 3, region: 'West', customer: 'Candle Systems', amount: 3100 }, - { id: 4, region: 'East', customer: 'Dynamo Energy', amount: 2600 }, - { id: 5, region: 'East', customer: 'Evergreen Foods', amount: 5400 }, - { id: 6, region: 'Central', customer: 'Frontier Retail', amount: 990 }, - { id: 7, region: 'Central', customer: 'Granite Works', amount: 2200 }, + { id: 1, region: 'West', customer: 'Acme Corp', amount: 4200 }, + { id: 2, region: 'West', customer: 'Blue Harbor Inc', amount: 1850 }, + { id: 3, region: 'West', customer: 'Candle Systems', amount: 3100 }, + { id: 4, region: 'East', customer: 'Dynamo Energy', amount: 2600 }, + { id: 5, region: 'East', customer: 'Evergreen Foods', amount: 5400 }, + { id: 6, region: 'Central', customer: 'Frontier Retail', amount: 990 }, + { id: 7, region: 'Central', customer: 'Granite Works', amount: 2200 }, { id: 8, region: 'Central', customer: 'Harbor Logistics', amount: 1475 }, { id: 9, region: 'Central', customer: 'Ironclad Freight', amount: 3050 }, ]; @@ -40,7 +40,7 @@ const groups: RegionGroup[] = Object.values( const columns: TableColumn[] = [ { id: 'region', name: 'Region', selector: r => r.region, sortable: true, style: { fontWeight: 600 } }, - { id: 'count', name: 'Orders', selector: r => r.count, sortable: true, right: true, width: '110px' }, + { id: 'count', name: 'Orders', selector: r => r.count, sortable: true, right: true, width: '110px' }, { id: 'total', name: 'Total', diff --git a/apps/docs/src/components/demos/RowInteractionsDemo.tsx b/apps/docs/src/components/demos/RowInteractionsDemo.tsx index aab4f65e..9472d51f 100644 --- a/apps/docs/src/components/demos/RowInteractionsDemo.tsx +++ b/apps/docs/src/components/demos/RowInteractionsDemo.tsx @@ -11,34 +11,37 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', role: 'Engineering Lead', email: 'aria@example.com' }, - { id: 2, name: 'Marcus Webb', department: 'Product', role: 'Product Manager', email: 'marcus@example.com' }, - { id: 3, name: 'Priya Kapoor', department: 'Design', role: 'Senior Designer', email: 'priya@example.com' }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', role: 'Data Scientist', email: 'jordan@example.com' }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', role: 'DevOps Engineer', email: 'sam@example.com' }, - { id: 6, name: 'Taylor Brooks', department: 'Sales', role: 'Account Manager', email: 'taylor@example.com' }, + { id: 1, name: 'Aria Chen', department: 'Engineering', role: 'Engineering Lead', email: 'aria@example.com' }, + { id: 2, name: 'Marcus Webb', department: 'Product', role: 'Product Manager', email: 'marcus@example.com' }, + { id: 3, name: 'Priya Kapoor', department: 'Design', role: 'Senior Designer', email: 'priya@example.com' }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', role: 'Data Scientist', email: 'jordan@example.com' }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', role: 'DevOps Engineer', email: 'sam@example.com' }, + { id: 6, name: 'Taylor Brooks', department: 'Sales', role: 'Account Manager', email: 'taylor@example.com' }, ]; export function RowClickDemo() { const [selected, setSelected] = useState(null); const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Role', selector: r => r.role }, ]; return (
- setSelected(row)} - /> + setSelected(row)} /> {selected && ( -
+
{selected.name} — {selected.role}, {selected.department} {selected.email}
@@ -53,9 +56,9 @@ export function RowLinkDemo() { const addLog = (msg: string) => setLog(prev => [msg, ...prev].slice(0, 4)); const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department }, - { name: 'Role', selector: r => r.role }, + { name: 'Role', selector: r => r.role }, { name: 'Email', ignoreRowClick: true, @@ -84,7 +87,9 @@ export function RowLinkDemo() { /> {log.length > 0 && (
- {log.map((l, i) =>
{l}
)} + {log.map((l, i) => ( +
{l}
+ ))}
)}

@@ -100,22 +105,37 @@ export function ActionCellDemo() { const addLog = (msg: string) => setLog(prev => [msg, ...prev].slice(0, 4)); const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Role', selector: r => r.role }, { name: 'Actions', button: true, cell: row => (

- {r.name} - {pinnedIds.has(r.id) && ( - - pinned - - )} -
- ), - }, - { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, - { - id: 'status', - name: 'Status', - selector: r => r.status, - cell: r => {r.status}, - sortable: true, - }, - { - id: 'salary', - name: 'Salary', - selector: r => r.salary, - format: r => `$${r.salary.toLocaleString()}`, - right: true, - sortable: true, - }, - ], [pinnedIds]); + const columns = useMemo( + (): TableColumn[] => [ + { + id: 'name', + name: 'Name', + selector: r => r.name, + sortable: true, + cell: r => ( +
+ + {r.name} + {pinnedIds.has(r.id) && ( + + pinned + + )} +
+ ), + }, + { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, + { + id: 'status', + name: 'Status', + selector: r => r.status, + cell: r => {r.status}, + sortable: true, + }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + format: r => `$${r.salary.toLocaleString()}`, + right: true, + sortable: true, + }, + ], + [pinnedIds], + ); const sortFunction = useMemo( - () => - (rows: Employee[], selector: (r: Employee) => string | number | boolean, direction: SortOrder) => { - const pinned = rows.filter(r => pinnedIds.has(r.id)); - const rest = rows.filter(r => !pinnedIds.has(r.id)); - const sorted = [...rest].sort((a, b) => { - const av = selector(a); - const bv = selector(b); - if (av === bv) return 0; - const cmp = av > bv ? 1 : -1; - return direction === SortOrder.ASC ? cmp : -cmp; - }); - return [...pinned, ...sorted]; - }, + () => (rows: Employee[], selector: (r: Employee) => string | number | boolean, direction: SortOrder) => { + const pinned = rows.filter(r => pinnedIds.has(r.id)); + const rest = rows.filter(r => !pinnedIds.has(r.id)); + const sorted = [...rest].sort((a, b) => { + const av = selector(a); + const bv = selector(b); + if (av === bv) return 0; + const cmp = av > bv ? 1 : -1; + return direction === SortOrder.ASC ? cmp : -cmp; + }); + return [...pinned, ...sorted]; + }, [pinnedIds], ); return (

- Click the 📌 icon on any row to pin it. Pinned rows stay at the top regardless of how you sort. - Sort any column to see the behaviour. + Click the 📌 icon on any row to pin it. Pinned rows stay at the top regardless of how you sort. Sort any column + to see the behaviour.

[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Role', selector: r => r.role }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Status', selector: r => r.status, + { + name: 'Status', + selector: r => r.status, cell: r => ( - {r.status} + + {r.status} + ), }, ]; @@ -48,7 +57,12 @@ export default function SelectionDemo() { Single select
diff --git a/apps/docs/src/components/demos/ServerSideSortPaginationDemo.tsx b/apps/docs/src/components/demos/ServerSideSortPaginationDemo.tsx index 0a8253c4..5ac72388 100644 --- a/apps/docs/src/components/demos/ServerSideSortPaginationDemo.tsx +++ b/apps/docs/src/components/demos/ServerSideSortPaginationDemo.tsx @@ -79,16 +79,13 @@ export default function ServerSideSortPaginationDemo() { const [sortDir, setSortDir] = useState(SortOrder.ASC); const [resetPage, setResetPage] = useState(false); - const load = useCallback( - async (params: { page: number; perPage: number; sortField: string; sortDir: SortOrder }) => { - setLoading(true); - const result = await fakeFetch(params); - setData(result.rows); - setTotal(result.total); - setLoading(false); - }, - [], - ); + const load = useCallback(async (params: { page: number; perPage: number; sortField: string; sortDir: SortOrder }) => { + setLoading(true); + const result = await fakeFetch(params); + setData(result.rows); + setTotal(result.total); + setLoading(false); + }, []); useEffect(() => { load({ page, perPage, sortField, sortDir }); diff --git a/apps/docs/src/components/demos/SortResetDemo.tsx b/apps/docs/src/components/demos/SortResetDemo.tsx index 8d57f01d..1d3524ff 100644 --- a/apps/docs/src/components/demos/SortResetDemo.tsx +++ b/apps/docs/src/components/demos/SortResetDemo.tsx @@ -24,7 +24,14 @@ const data: Employee[] = [ const columns: TableColumn[] = [ { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'salary', name: 'Salary', selector: r => r.salary, format: r => `$${r.salary.toLocaleString()}`, right: true, sortable: true }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + format: r => `$${r.salary.toLocaleString()}`, + right: true, + sortable: true, + }, { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true }, ]; @@ -44,9 +51,7 @@ export default function SortResetDemo() { > Reset sort - - {lastSort ?? 'Sort by any column, then reset'} - + {lastSort ?? 'Sort by any column, then reset'}
state helpers --- -function readParams(): { sortId: string; sortDir: SortOrder; page: number; perPage: number; filters: Record } { +function readParams(): { + sortId: string; + sortDir: SortOrder; + page: number; + perPage: number; + filters: Record; +} { const p = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : ''); return { - sortId: p.get('sort') ?? '', + sortId: p.get('sort') ?? '', sortDir: p.get('dir') === 'desc' ? SortOrder.DESC : SortOrder.ASC, - page: parseInt(p.get('page') ?? '1', 10), + page: parseInt(p.get('page') ?? '1', 10), perPage: parseInt(p.get('per') ?? '5', 10), - filters: Object.fromEntries( - [...p.entries()] - .filter(([k]) => k.startsWith('f_')) - .map(([k, v]) => [k.slice(2), v]), - ), + filters: Object.fromEntries([...p.entries()].filter(([k]) => k.startsWith('f_')).map(([k, v]) => [k.slice(2), v])), }; } -function writeParams(state: { sortId: string; sortDir: SortOrder; page: number; perPage: number; filters: Record }) { +function writeParams(state: { + sortId: string; + sortDir: SortOrder; + page: number; + perPage: number; + filters: Record; +}) { const p = new URLSearchParams(); - if (state.sortId) { p.set('sort', state.sortId); p.set('dir', state.sortDir === SortOrder.DESC ? 'desc' : 'asc'); } + if (state.sortId) { + p.set('sort', state.sortId); + p.set('dir', state.sortDir === SortOrder.DESC ? 'desc' : 'asc'); + } if (state.page > 1) p.set('page', String(state.page)); if (state.perPage !== 5) p.set('per', String(state.perPage)); - Object.entries(state.filters).forEach(([k, v]) => { if (v) p.set(`f_${k}`, v); }); + Object.entries(state.filters).forEach(([k, v]) => { + if (v) p.set(`f_${k}`, v); + }); const qs = p.toString(); history.replaceState(null, '', qs ? `?${qs}` : window.location.pathname); } @@ -56,22 +69,30 @@ function writeParams(state: { sortId: string; sortDir: SortOrder; page: number; export default function UrlSyncDemo() { const init = useMemo(() => readParams(), []); - const [sortId, setSortId] = useState(init.sortId); + const [sortId, setSortId] = useState(init.sortId); const [sortDir, setSortDir] = useState(init.sortDir); - const [page, setPage] = useState(init.page); + const [page, setPage] = useState(init.page); const [perPage, setPerPage] = useState(init.perPage); const [filters, setFilters] = useState>(() => Object.fromEntries( - Object.entries(init.filters).map(([k, v]) => [k, { value: v, operator: 'contains' as const, condition2: null, join: 'and' as const }]), + Object.entries(init.filters).map(([k, v]) => [ + k, + { value: v, operator: 'contains' as const, condition2: null, join: 'and' as const }, + ]), ), ); // Sync every state change to the URL useEffect(() => { writeParams({ - sortId, sortDir, page, perPage, + sortId, + sortDir, + page, + perPage, filters: Object.fromEntries( - Object.entries(filters).map(([k, f]) => [k, String(f.value ?? '')]).filter(([, v]) => v), + Object.entries(filters) + .map(([k, f]) => [k, String(f.value ?? '')]) + .filter(([, v]) => v), ), }); }, [sortId, sortDir, page, perPage, filters]); @@ -88,11 +109,18 @@ export default function UrlSyncDemo() { }, []); const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true, filterable: true, grow: 1 }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true, filterable: true, grow: 1 }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true, filterable: true }, - { id: 'role', name: 'Role', selector: r => r.role, sortable: true, grow: 1 }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, right: true, width: '110px', - format: r => `$${r.salary.toLocaleString()}` }, + { id: 'role', name: 'Role', selector: r => r.role, sortable: true, grow: 1 }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + sortable: true, + right: true, + width: '110px', + format: r => `$${r.salary.toLocaleString()}`, + }, ]; // Show current URL params so the demo is self-explanatory @@ -120,7 +148,10 @@ export default function UrlSyncDemo() { paginationDefaultPage={page} paginationPerPage={perPage} onChangePage={p => setPage(p)} - onChangeRowsPerPage={(pp, p) => { setPerPage(pp); setPage(p); }} + onChangeRowsPerPage={(pp, p) => { + setPerPage(pp); + setPage(p); + }} highlightOnHover />
diff --git a/apps/docs/src/pages/docs/animations.astro b/apps/docs/src/pages/docs/animations.astro index 4ef0db4c..416260eb 100644 --- a/apps/docs/src/pages/docs/animations.astro +++ b/apps/docs/src/pages/docs/animations.astro @@ -21,22 +21,32 @@ import AnimationsDemo from '../../components/demos/AnimationsDemo.tsx'; code={`import { useState } from 'react'; import DataTable, { type TableColumn } from 'react-data-table-component'; -interface Employee { id: number; name: string; department: string; salary: number } +interface Employee { + id: number; + name: string; + department: string; + salary: number; +} const data: Employee[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000 }, - { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000 }, - { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000 }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000 }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000 }, - { id: 6, name: 'Taylor Brooks', department: 'Sales', salary: 97000 }, + { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000 }, + { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000 }, + { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000 }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000 }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000 }, + { id: 6, name: 'Taylor Brooks', department: 'Sales', salary: 97000 }, ]; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Salary', selector: r => r.salary, sortable: true, - format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, + { + name: 'Salary', + selector: r => r.salary, + sortable: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + right: true, + }, ]; export default function App() { @@ -46,17 +56,10 @@ export default function App() { return (
- +
); }`} @@ -110,7 +113,7 @@ export default function App() {

Prop reference

diff --git a/apps/docs/src/pages/docs/api.astro b/apps/docs/src/pages/docs/api.astro index 4cb405d6..e501f389 100644 --- a/apps/docs/src/pages/docs/api.astro +++ b/apps/docs/src/pages/docs/api.astro @@ -90,7 +90,7 @@ import PropsTable from '../../components/PropsTable.astro'; @@ -386,17 +386,17 @@ const customStyles: TableStyles = { row.status, // sort key - cell: row => , // display + selector: row => row.status, // sort key + cell: row => , // display sortable: true, } // ✅ Currency column — format preserves sort order { name: 'Salary', - selector: row => row.salary, // sort on raw number + selector: row => row.salary, // sort on raw number format: row => \`$\${row.salary.toLocaleString()}\`, // display formatted sortable: true, right: true, @@ -109,10 +109,23 @@ const columns: TableColumn[] = [ name: 'Employee', cell: row => (
-
- {row.name.split(' ').map(n => n[0]).join('')} +
+ {row.name + .split(' ') + .map(n => n[0]) + .join('')}
{row.name}
@@ -126,17 +139,21 @@ const columns: TableColumn[] = [ { name: 'Salary', selector: row => row.salary, - format: row => new Intl.NumberFormat('en-US', { - style: 'currency', currency: 'USD', maximumFractionDigits: 0, - }).format(row.salary), + format: row => + new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + maximumFractionDigits: 0, + }).format(row.salary), sortable: true, right: true, }, { name: 'Status', cell: row => ( - + {row.status} ), @@ -145,9 +162,7 @@ const columns: TableColumn[] = [ }, { name: '', - cell: row => ( - - ), + cell: row => , button: true, width: '120px', }, diff --git a/apps/docs/src/pages/docs/column-groups.astro b/apps/docs/src/pages/docs/column-groups.astro index a3dab6a4..879695b3 100644 --- a/apps/docs/src/pages/docs/column-groups.astro +++ b/apps/docs/src/pages/docs/column-groups.astro @@ -29,37 +29,42 @@ interface Employee { } const data: Employee[] = [ - { id: 1, firstName: 'Aria', lastName: 'Chen', department: 'Engineering', baseSalary: 140000, bonus: 15000 }, - { id: 2, firstName: 'Marcus', lastName: 'Webb', department: 'Product', baseSalary: 125000, bonus: 7000 }, - { id: 3, firstName: 'Priya', lastName: 'Kapoor', department: 'Design', baseSalary: 110000, bonus: 8000 }, - { id: 4, firstName: 'Jordan', lastName: 'Ellis', department: 'Analytics', baseSalary: 135000, bonus: 12000 }, + { id: 1, firstName: 'Aria', lastName: 'Chen', department: 'Engineering', baseSalary: 140000, bonus: 15000 }, + { id: 2, firstName: 'Marcus', lastName: 'Webb', department: 'Product', baseSalary: 125000, bonus: 7000 }, + { id: 3, firstName: 'Priya', lastName: 'Kapoor', department: 'Design', baseSalary: 110000, bonus: 8000 }, + { id: 4, firstName: 'Jordan', lastName: 'Ellis', department: 'Analytics', baseSalary: 135000, bonus: 12000 }, ]; // Every column that belongs to a group must have a stable \`id\` const columns: TableColumn[] = [ - { id: 'first', name: 'First name', selector: r => r.firstName, sortable: true }, - { id: 'last', name: 'Last name', selector: r => r.lastName, sortable: true }, - { id: 'dept', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'base', name: 'Base salary', selector: r => r.baseSalary, sortable: true, right: true, - format: r => \`$\${r.baseSalary.toLocaleString()}\` }, - { id: 'bonus', name: 'Bonus', selector: r => r.bonus, sortable: true, right: true, - format: r => \`$\${r.bonus.toLocaleString()}\` }, + { id: 'first', name: 'First name', selector: r => r.firstName, sortable: true }, + { id: 'last', name: 'Last name', selector: r => r.lastName, sortable: true }, + { id: 'dept', name: 'Department', selector: r => r.department, sortable: true }, + { + id: 'base', + name: 'Base salary', + selector: r => r.baseSalary, + sortable: true, + right: true, + format: r => \`$\${r.baseSalary.toLocaleString()}\`, + }, + { + id: 'bonus', + name: 'Bonus', + selector: r => r.bonus, + sortable: true, + right: true, + format: r => \`$\${r.bonus.toLocaleString()}\`, + }, ]; const columnGroups: ColumnGroup[] = [ - { name: 'Employee', columnIds: ['first', 'last', 'dept'] }, + { name: 'Employee', columnIds: ['first', 'last', 'dept'] }, { name: 'Compensation', columnIds: ['base', 'bonus'] }, ]; export default function App() { - return ( - - ); + return ; }`} > @@ -74,8 +79,8 @@ export default function App() {

`} /> +/>;`} />

Rules

@@ -100,8 +105,8 @@ export default function App() {

ColumnGroup type

@@ -112,7 +117,7 @@ export default function App() {

diff --git a/apps/docs/src/pages/docs/column-pinning.astro b/apps/docs/src/pages/docs/column-pinning.astro index a2aa66ec..56df1b13 100644 --- a/apps/docs/src/pages/docs/column-pinning.astro +++ b/apps/docs/src/pages/docs/column-pinning.astro @@ -23,15 +23,15 @@ import ColumnPinningDemo from '../../components/demos/ColumnPinningDemo.tsx'; code={`import DataTable, { type TableColumn } from 'react-data-table-component'; const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, width: '180px', pinned: 'left' }, - { id: 'role', name: 'Role', selector: r => r.role, width: '220px' }, - { id: 'dept', name: 'Department', selector: r => r.department, width: '180px' }, - { id: 'loc', name: 'Location', selector: r => r.location, width: '200px' }, - { id: 'salary', name: 'Salary', selector: r => r.salary, width: '140px', right: true }, - { id: 'status', name: 'Status', selector: r => r.status, width: '120px', pinned: 'right' }, + { id: 'name', name: 'Name', selector: r => r.name, width: '180px', pinned: 'left' }, + { id: 'role', name: 'Role', selector: r => r.role, width: '220px' }, + { id: 'dept', name: 'Department', selector: r => r.department, width: '180px' }, + { id: 'loc', name: 'Location', selector: r => r.location, width: '200px' }, + { id: 'salary', name: 'Salary', selector: r => r.salary, width: '140px', right: true }, + { id: 'status', name: 'Status', selector: r => r.status, width: '120px', pinned: 'right' }, ]; -`} +;`} > @@ -84,11 +84,11 @@ const columns: TableColumn[] = [

[] = [ - { id: 'id', name: 'ID', width: '80px', pinned: 'left' }, - { id: 'name', name: 'Name', width: '180px', pinned: 'left' }, + { id: 'id', name: 'ID', width: '80px', pinned: 'left' }, + { id: 'name', name: 'Name', width: '180px', pinned: 'left' }, // ... scrolling columns ... { id: 'status', name: 'Status', width: '120px', pinned: 'right' }, - { id: 'action', name: '', width: '64px', pinned: 'right' }, + { id: 'action', name: '', width: '64px', pinned: 'right' }, ];`} />

@@ -106,11 +106,11 @@ const columns: TableColumn[] = [

+ selectableRows // checkbox column pins at 0 + expandableRows // expander column pins after the checkbox +/>; // → Name sticks at left: 96px (48px checkbox + 48px expander)`} />

@@ -121,7 +121,9 @@ const columns: TableColumn[] = [

+.rdt_table { + --rdt-system-col-width: 56px; +}`} lang="css" />

With resizable columns

@@ -129,7 +131,7 @@ const columns: TableColumn[] = [ triggers a re-render with new offsets so columns pinned further from the edge shift to match.

- `} /> + ;`} />

With fixed header

@@ -138,12 +140,7 @@ const columns: TableColumn[] = [ corner) stays above scrolling body content.

- `} /> + ;`} />

Not supported with column groups

diff --git a/apps/docs/src/pages/docs/column-reorder.astro b/apps/docs/src/pages/docs/column-reorder.astro index 833a8bd4..f62270ad 100644 --- a/apps/docs/src/pages/docs/column-reorder.astro +++ b/apps/docs/src/pages/docs/column-reorder.astro @@ -38,32 +38,32 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', salary: 155000 }, - { id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', salary: 132000 }, - { id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', salary: 118000 }, - { id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', salary: 143000 }, - { id: 5, name: 'Sam Rivera', role: 'DevOps Engineer', department: 'Engineering', salary: 128000 }, + { id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', salary: 155000 }, + { id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', salary: 132000 }, + { id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', salary: 118000 }, + { id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', salary: 143000 }, + { id: 5, name: 'Sam Rivera', role: 'DevOps Engineer', department: 'Engineering', salary: 128000 }, ]; const initialColumns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true, reorder: true }, - { id: 'role', name: 'Role', selector: r => r.role, reorder: true }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true, reorder: true }, + { id: 'role', name: 'Role', selector: r => r.role, reorder: true }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true, reorder: true }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, reorder: true, - format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + sortable: true, + reorder: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + right: true, + }, ]; export default function App() { const [columns, setColumns] = useState(initialColumns); - return ( - - ); + return ; }`} > @@ -78,9 +78,9 @@ export default function App() {

[] = [ - { id: 'name', name: 'Name', selector: r => r.name, /* no reorder — pinned */ }, - { id: 'role', name: 'Role', selector: r => r.role, reorder: true }, - { id: 'dept', name: 'Dept', selector: r => r.dept, reorder: true }, + { id: 'name', name: 'Name', selector: r => r.name /* no reorder — pinned */ }, + { id: 'role', name: 'Role', selector: r => r.role, reorder: true }, + { id: 'dept', name: 'Dept', selector: r => r.dept, reorder: true }, { id: 'salary', name: 'Salary', selector: r => r.salary, reorder: true }, ];`} /> @@ -99,8 +99,11 @@ function applyOrder(cols: TableColumn[], order: string[]) { } function loadOrder(): string[] { - try { return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]'); } - catch { return []; } + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]'); + } catch { + return []; + } } export default function App() { @@ -111,9 +114,7 @@ export default function App() { localStorage.setItem(STORAGE_KEY, JSON.stringify(next.map(c => String(c.id)))); } - return ( - - ); + return ; }`} />

Reordering column groups

@@ -132,22 +133,34 @@ export default function App() { import DataTable, { type TableColumn, type ColumnGroup } from 'react-data-table-component'; const columns: TableColumn[] = [ - { id: 'first', name: 'First name', selector: r => r.firstName, sortable: true }, - { id: 'last', name: 'Last name', selector: r => r.lastName, sortable: true }, - { id: 'dept', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'base', name: 'Base salary', selector: r => r.baseSalary, sortable: true, right: true, - format: r => \`$\${r.baseSalary.toLocaleString()}\` }, - { id: 'bonus', name: 'Bonus', selector: r => r.bonus, sortable: true, right: true, - format: r => \`$\${r.bonus.toLocaleString()}\` }, + { id: 'first', name: 'First name', selector: r => r.firstName, sortable: true }, + { id: 'last', name: 'Last name', selector: r => r.lastName, sortable: true }, + { id: 'dept', name: 'Department', selector: r => r.department, sortable: true }, + { + id: 'base', + name: 'Base salary', + selector: r => r.baseSalary, + sortable: true, + right: true, + format: r => \`$\${r.baseSalary.toLocaleString()}\`, + }, + { + id: 'bonus', + name: 'Bonus', + selector: r => r.bonus, + sortable: true, + right: true, + format: r => \`$\${r.bonus.toLocaleString()}\`, + }, ]; const initialGroups: ColumnGroup[] = [ - { name: 'Employee', columnIds: ['first', 'last', 'dept'], reorder: true }, - { name: 'Compensation', columnIds: ['base', 'bonus'], reorder: true }, + { name: 'Employee', columnIds: ['first', 'last', 'dept'], reorder: true }, + { name: 'Compensation', columnIds: ['base', 'bonus'], reorder: true }, ]; export default function App() { - const [cols, setCols] = useState(columns); + const [cols, setCols] = useState(columns); const [groups, setGroups] = useState(initialGroups); return ( @@ -175,16 +188,16 @@ export default function App() {

[] = [ - { id: 'first', name: 'First name', selector: r => r.firstName, sortable: true, reorder: true }, - { id: 'last', name: 'Last name', selector: r => r.lastName, sortable: true, reorder: true }, - { id: 'dept', name: 'Department', selector: r => r.department, sortable: true, reorder: true }, - { id: 'base', name: 'Base salary', selector: r => r.baseSalary, sortable: true, reorder: true }, - { id: 'bonus', name: 'Bonus', selector: r => r.bonus, sortable: true, reorder: true }, + { id: 'first', name: 'First name', selector: r => r.firstName, sortable: true, reorder: true }, + { id: 'last', name: 'Last name', selector: r => r.lastName, sortable: true, reorder: true }, + { id: 'dept', name: 'Department', selector: r => r.department, sortable: true, reorder: true }, + { id: 'base', name: 'Base salary', selector: r => r.baseSalary, sortable: true, reorder: true }, + { id: 'bonus', name: 'Bonus', selector: r => r.bonus, sortable: true, reorder: true }, ]; const columnGroups: ColumnGroup[] = [ - { name: 'Employee', columnIds: ['first', 'last', 'dept'], reorder: true }, - { name: 'Compensation', columnIds: ['base', 'bonus'], reorder: true }, + { name: 'Employee', columnIds: ['first', 'last', 'dept'], reorder: true }, + { name: 'Compensation', columnIds: ['base', 'bonus'], reorder: true }, ];`} />

Prop reference

diff --git a/apps/docs/src/pages/docs/column-visibility.astro b/apps/docs/src/pages/docs/column-visibility.astro index 0d0e8372..5b79d4d7 100644 --- a/apps/docs/src/pages/docs/column-visibility.astro +++ b/apps/docs/src/pages/docs/column-visibility.astro @@ -21,11 +21,11 @@ import ColumnVisibilityDemo from '../../components/demos/ColumnVisibilityDemo.ts code={`import DataTable, { useColumnVisibility, type TableColumn } from 'react-data-table-component'; const initialColumns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'role', name: 'Role', selector: r => r.role }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true }, - { id: 'location', name: 'Location', selector: r => r.location }, + { id: 'role', name: 'Role', selector: r => r.role }, + { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true }, + { id: 'location', name: 'Location', selector: r => r.location }, ]; export default function App() { @@ -47,12 +47,12 @@ export default function App() {

Hook API

[] — pass directly to DataTable, omit is managed for you - entries, // { column, visible }[] — drive your toggle UI from this + columns, // TableColumn[] — pass directly to DataTable, omit is managed for you + entries, // { column, visible }[] — drive your toggle UI from this toggleColumn, // (columnId) => void — flip one column's visibility - isVisible, // (columnId) => boolean — check a specific column - showAll, // () => void — make all columns visible - hideAll, // () => void — hide all columns + isVisible, // (columnId) => boolean — check a specific column + showAll, // () => void — make all columns visible + hideAll, // () => void — hide all columns } = useColumnVisibility(initialColumns);`} />

Column IDs are required

@@ -75,7 +75,7 @@ export default function App() { [] = [ - { id: 'name', name: 'Name', selector: r => r.name }, + { id: 'name', name: 'Name', selector: r => r.name }, { id: 'salary', name: 'Salary', selector: r => r.salary, omit: saved.includes('salary') }, ]; @@ -83,9 +83,7 @@ const { columns, entries, toggleColumn } = useColumnVisibility(initialColumns); const handleToggle = (id: string | number) => { toggleColumn(id); - const next = entries - .filter(e => (e.column.id === id ? e.visible : !e.visible)) - .map(e => e.column.id as string); + const next = entries.filter(e => (e.column.id === id ? e.visible : !e.visible)).map(e => e.column.id as string); localStorage.setItem('hiddenCols', JSON.stringify(next)); };`} /> diff --git a/apps/docs/src/pages/docs/columns.astro b/apps/docs/src/pages/docs/columns.astro index bd68bdf1..f7c6a27b 100644 --- a/apps/docs/src/pages/docs/columns.astro +++ b/apps/docs/src/pages/docs/columns.astro @@ -30,42 +30,61 @@ interface Employee { } const StatusBadge = ({ status }: { status: Employee['status'] }) => ( - + {status} ); export default function App() { - const [showDept, setShowDept] = useState(true); + const [showDept, setShowDept] = useState(true); const [showSalary, setShowSalary] = useState(true); const [filterable, setFilterable] = useState(false); const columns: TableColumn[] = [ { - id: 'name', name: 'Name', selector: r => r.name, - sortable: true, filterable, // filterable toggles a filter input in the header + id: 'name', + name: 'Name', + selector: r => r.name, + sortable: true, + filterable, // filterable toggles a filter input in the header }, { - id: 'department', name: 'Department', selector: r => r.department, - sortable: true, omit: !showDept, + id: 'department', + name: 'Department', + selector: r => r.department, + sortable: true, + omit: !showDept, }, { - id: 'salary', name: 'Salary', selector: r => r.salary, - sortable: true, right: true, + id: 'salary', + name: 'Salary', + selector: r => r.salary, + sortable: true, + right: true, format: r => \`$\${r.salary.toLocaleString()}\`, omit: !showSalary, - conditionalCellStyles: [ - { when: r => r.salary > 150000, style: { fontWeight: 700 } }, - ], + conditionalCellStyles: [{ when: r => r.salary > 150000, style: { fontWeight: 700 } }], }, { - id: 'status', name: 'Status', selector: r => r.status, - center: true, cell: r => , + id: 'status', + name: 'Status', + selector: r => r.status, + center: true, + cell: r => , }, { - id: 'start', name: 'Start date', selector: r => r.startDate, + id: 'start', + name: 'Start date', + selector: r => r.startDate, sortable: true, format: r => new Date(r.startDate).toLocaleDateString(), }, @@ -183,11 +202,7 @@ const columns: TableColumn[] = [ `} /> +;`} />

Hiding columns

diff --git a/apps/docs/src/pages/docs/conditional-styles.astro b/apps/docs/src/pages/docs/conditional-styles.astro index 63c07a76..f29b5cea 100644 --- a/apps/docs/src/pages/docs/conditional-styles.astro +++ b/apps/docs/src/pages/docs/conditional-styles.astro @@ -32,7 +32,7 @@ const conditionalRowStyles: ConditionalStyles[] = [ const columns: TableColumn[] = [ { name: 'Company', selector: r => r.company, sortable: true, grow: 2 }, - { name: 'Rep', selector: r => r.rep, sortable: true }, + { name: 'Rep', selector: r => r.rep, sortable: true }, { name: 'Value', selector: r => r.value, @@ -41,7 +41,7 @@ const columns: TableColumn[] = [ right: true, conditionalCellStyles: [ { when: r => r.value >= 200000, style: { fontWeight: 700, color: '#15803d' } }, - { when: r => r.value < 20000, style: { color: '#9ca3af' } }, + { when: r => r.value < 20000, style: { color: '#9ca3af' } }, ], }, { @@ -56,7 +56,7 @@ const columns: TableColumn[] = [ }, ]; -`} +;`} > @@ -82,7 +82,7 @@ const conditionalRowStyles: ConditionalStyles[] = [ }, ]; -`} /> +;`} />

Style as a function

@@ -125,7 +125,7 @@ const conditionalRowStyles: ConditionalStyles[] = [ right: true, conditionalCellStyles: [ { when: r => r.value >= 200000, style: { fontWeight: 700, color: '#15803d' } }, - { when: r => r.value < 20000, style: { color: '#9ca3af' } }, + { when: r => r.value < 20000, style: { color: '#9ca3af' } }, ], }, { diff --git a/apps/docs/src/pages/docs/context-menu.astro b/apps/docs/src/pages/docs/context-menu.astro index cc52c754..770656f4 100644 --- a/apps/docs/src/pages/docs/context-menu.astro +++ b/apps/docs/src/pages/docs/context-menu.astro @@ -34,7 +34,7 @@ import RowContextMenuDemo from '../../components/demos/RowContextMenuDemo.tsx'; if (ctx.type === 'header') console.log(action.id, ctx.column); }} resizable -/>`} +/>;`} > @@ -97,9 +97,9 @@ const rowActions = (row: Task): ContextMenuAction[] => [ onContextMenuAction={(action, ctx) => { if (ctx.type !== 'row') return; if (action.id === 'delete') setRows(prev => prev.filter(r => r.id !== ctx.row.id)); - if (action.id === 'mark-done') setRows(prev => prev.map(r => r.id === ctx.row.id ? { ...r, status: 'Done' } : r)); + if (action.id === 'mark-done') setRows(prev => prev.map(r => (r.id === ctx.row.id ? { ...r, status: 'Done' } : r))); }} -/>`} +/>;`} > @@ -134,7 +134,7 @@ const rowActions = (row: Task): ContextMenuAction[] => [ if (ctx.type === 'header') console.log(action.id, ctx.column); else console.log(action.id, ctx.row, ctx.rowIndex); }} -/>`} /> +/>;`} />

The built-in ids are reserved: sort-asc, sort-desc, diff --git a/apps/docs/src/pages/docs/custom-styles.astro b/apps/docs/src/pages/docs/custom-styles.astro index b1cc7e19..904d50c5 100644 --- a/apps/docs/src/pages/docs/custom-styles.astro +++ b/apps/docs/src/pages/docs/custom-styles.astro @@ -29,26 +29,38 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, active: true }, - { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, active: true }, - { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, active: false }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, active: true }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, active: false }, - { id: 6, name: 'Taylor Brooks', department: 'Sales', salary: 97000, active: true }, + { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, active: true }, + { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, active: true }, + { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, active: false }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, active: true }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, active: false }, + { id: 6, name: 'Taylor Brooks', department: 'Sales', salary: 97000, active: true }, ]; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Salary', selector: r => r.salary, sortable: true, - format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, + { + name: 'Salary', + selector: r => r.salary, + sortable: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + right: true, + }, { name: 'Status', selector: r => r.active, cell: r => ( - + {r.active ? 'Active' : 'Inactive'} ), @@ -128,7 +140,7 @@ const customStyles: TableStyles = { }, }; -`} /> +;`} />

Available style targets

@@ -179,11 +191,7 @@ const conditionalRowStyles: ConditionalStyles[] = [ }, ]; -`} /> +;`} />

CSS custom properties

diff --git a/apps/docs/src/pages/docs/expandable.astro b/apps/docs/src/pages/docs/expandable.astro index 2f4b025a..d04993e3 100644 --- a/apps/docs/src/pages/docs/expandable.astro +++ b/apps/docs/src/pages/docs/expandable.astro @@ -32,32 +32,48 @@ interface Employee { const data: Employee[] = [ { - id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', - salary: 155000, locked: false, + id: 1, + name: 'Aria Chen', + role: 'Engineering Lead', + department: 'Engineering', + salary: 155000, + locked: false, bio: 'Aria leads the platform team, focusing on reliability and developer experience.', }, { - id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', - salary: 132000, locked: true, + id: 2, + name: 'Marcus Webb', + role: 'Product Manager', + department: 'Product', + salary: 132000, + locked: true, bio: 'Marcus drives roadmap strategy across three product lines.', }, { - id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', - salary: 118000, locked: false, + id: 3, + name: 'Priya Kapoor', + role: 'Senior Designer', + department: 'Design', + salary: 118000, + locked: false, bio: 'Priya leads the design system and owns the mobile experience.', }, { - id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', - salary: 143000, locked: true, + id: 4, + name: 'Jordan Ellis', + role: 'Data Scientist', + department: 'Analytics', + salary: 143000, + locked: true, bio: 'Jordan builds ML pipelines for churn prediction and revenue forecasting.', }, ]; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Role', selector: r => r.role }, { name: 'Department', selector: r => r.department }, - { name: 'Salary', selector: r => r.salary, format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, + { name: 'Salary', selector: r => r.salary, format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, ]; // The expandable component receives the row as the \`data\` prop @@ -75,19 +91,18 @@ export default function App() { return (
r.locked) : undefined} + expandableRowDisabled={disableLocked ? r => r.locked : undefined} animateRows={animate} highlightOnHover /> @@ -121,14 +136,7 @@ const ExpandedRow = ({ data: row }: ExpanderComponentProps) => ( ); export default function App() { - return ( - - ); + return ; }`} />

Passing extra props to the expander

@@ -149,7 +157,7 @@ export default function App() { expandableRows expandableRowsComponent={DetailPanel} expandableRowsComponentProps={{ onDelete: handleDelete }} -/>`} /> +/>;`} />

Controlling which rows are expanded

@@ -226,7 +234,7 @@ export default function App() { onRowExpandToggled={(expanded: boolean, row: Employee) => { console.log(expanded ? 'opened' : 'closed', row.name); }} -/>`} /> +/>;`} />

Custom expander icons

@@ -237,9 +245,9 @@ export default function App() { expandableRowsComponent={ExpandedRow} expandableIcon={{ collapsed: , - expanded: , + expanded: , }} -/>`} /> +/>;`} />

Animation

@@ -247,11 +255,7 @@ export default function App() { The animation is automatically disabled when the user's OS has prefers-reduced-motion set.

- `} /> + ;`} />

Localization

@@ -263,31 +267,19 @@ export default function App() { import DataTable from 'react-data-table-component'; import { fr } from 'react-data-table-component/locales'; -`} /> +;`} /> `} /> +;`} />

Prop reference

diff --git a/apps/docs/src/pages/docs/export.astro b/apps/docs/src/pages/docs/export.astro index ebabade4..90664ece 100644 --- a/apps/docs/src/pages/docs/export.astro +++ b/apps/docs/src/pages/docs/export.astro @@ -27,13 +27,17 @@ import DocsTable from '../../components/DocsTable.astro'; code={`import { useState } from 'react'; import DataTable, { useTableExport, type TableColumn } from 'react-data-table-component'; -interface Employee { id: number; name: string; department: string; salary: number; } +interface Employee { + id: number; + name: string; + department: string; + salary: number; +} const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name }, + { id: 'name', name: 'Name', selector: r => r.name }, { id: 'department', name: 'Department', selector: r => r.department }, - { id: 'salary', name: 'Salary', selector: r => r.salary, right: true, - format: r => \`$\${r.salary.toLocaleString()}\` }, + { id: 'salary', name: 'Salary', selector: r => r.salary, right: true, format: r => \`$\${r.salary.toLocaleString()}\` }, ]; export default function App() { @@ -68,10 +72,9 @@ export default function App() { type Employee = { id: number; name: string; salary: number }; const columns: TableColumn[] = [ - { id: 'id', name: 'ID', selector: r => r.id }, - { id: 'name', name: 'Name', selector: r => r.name }, - { id: 'salary', name: 'Salary', selector: r => r.salary, - format: r => \`$\${r.salary.toLocaleString()}\` }, + { id: 'id', name: 'ID', selector: r => r.id }, + { id: 'name', name: 'Name', selector: r => r.name }, + { id: 'salary', name: 'Salary', selector: r => r.salary, format: r => \`$\${r.salary.toLocaleString()}\` }, ]; function EmployeesTable({ data }: { data: Employee[] }) { @@ -107,19 +110,14 @@ function EmployeesTable({ data }: { data: Employee[] }) { DataTable from the same hooks that feed useTableExport:

-

Override header labels

diff --git a/apps/docs/src/pages/docs/filtering.astro b/apps/docs/src/pages/docs/filtering.astro index 60440d05..e708d8ea 100644 --- a/apps/docs/src/pages/docs/filtering.astro +++ b/apps/docs/src/pages/docs/filtering.astro @@ -29,27 +29,32 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, hired: '2019-03-12' }, - { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, hired: '2020-07-01' }, - { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, hired: '2021-01-15' }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, hired: '2018-11-30' }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, hired: '2022-04-22' }, - { id: 6, name: 'Taylor Brooks', department: 'Sales', salary: 97000, hired: '2023-02-08' }, - { id: 7, name: 'Morgan Lee', department: 'Engineering', salary: 162000, hired: '2017-09-05' }, - { id: 8, name: 'Casey Park', department: 'Design', salary: 109000, hired: '2022-11-19' }, - { id: 9, name: 'Drew Santos', department: 'Product', salary: 138000, hired: '2020-03-30' }, - { id: 10, name: 'Avery Johnson', department: 'Sales', salary: 104000, hired: '2021-08-14' }, + { id: 1, name: 'Aria Chen', department: 'Engineering', salary: 155000, hired: '2019-03-12' }, + { id: 2, name: 'Marcus Webb', department: 'Product', salary: 132000, hired: '2020-07-01' }, + { id: 3, name: 'Priya Kapoor', department: 'Design', salary: 118000, hired: '2021-01-15' }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', salary: 143000, hired: '2018-11-30' }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', salary: 128000, hired: '2022-04-22' }, + { id: 6, name: 'Taylor Brooks', department: 'Sales', salary: 97000, hired: '2023-02-08' }, + { id: 7, name: 'Morgan Lee', department: 'Engineering', salary: 162000, hired: '2017-09-05' }, + { id: 8, name: 'Casey Park', department: 'Design', salary: 109000, hired: '2022-11-19' }, + { id: 9, name: 'Drew Santos', department: 'Product', salary: 138000, hired: '2020-03-30' }, + { id: 10, name: 'Avery Johnson', department: 'Sales', salary: 104000, hired: '2021-08-14' }, ]; const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true, filterable: true }, - { id: 'dept', name: 'Department', selector: r => r.department, filterable: true }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true, filterable: true }, + { id: 'dept', name: 'Department', selector: r => r.department, filterable: true }, { - id: 'salary', name: 'Salary', selector: r => r.salary, + id: 'salary', + name: 'Salary', + selector: r => r.salary, format: r => \`$\${r.salary.toLocaleString()}\`, - right: true, sortable: true, filterable: true, filterType: 'number', + right: true, + sortable: true, + filterable: true, + filterType: 'number', }, - { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true, filterable: true, filterType: 'date' }, + { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true, filterable: true, filterType: 'date' }, ]; export default function App() { @@ -66,11 +71,9 @@ export default function App() { pass every active filter to appear.

- [] = [ - { id: 'name', name: 'Name', selector: r => r.name, filterable: true }, -]; + [] = [{ id: 'name', name: 'Name', selector: r => r.name, filterable: true }]; -`} /> +;`} />

Filter types

@@ -78,9 +81,9 @@ export default function App() {

[] = [ - { id: 'name', name: 'Name', selector: r => r.name, filterable: true }, - { id: 'score', name: 'Score', selector: r => r.score, filterable: true, filterType: 'number' }, - { id: 'dob', name: 'Birth date', selector: r => r.dob, filterable: true, filterType: 'date' }, + { id: 'name', name: 'Name', selector: r => r.name, filterable: true }, + { id: 'score', name: 'Score', selector: r => r.score, filterable: true, filterType: 'number' }, + { id: 'dob', name: 'Birth date', selector: r => r.dob, filterable: true, filterType: 'date' }, ];`} />

Apply / Clear behaviour

@@ -157,7 +160,7 @@ function App() { function handleFilterChange(columnId: string | number, filter: FilterState) { setFilterValues(prev => ({ ...prev, [columnId]: filter })); - setResetPage(v => !v); // jump back to page 1 after each filter + setResetPage(v => !v); // jump back to page 1 after each filter } return ( @@ -177,13 +180,13 @@ function App() { +isFilterActive({ condition1: { operator: 'blank' } }); // true — no value needed`} />

Localization

@@ -196,7 +199,7 @@ isFilterActive({ condition1: { operator: 'blank' } }); // true — import DataTable from 'react-data-table-component'; import { fr } from 'react-data-table-component/locales'; -`} /> +;`} /> `} /> +;`} /> `} /> +;`} />

Headless usage

diff --git a/apps/docs/src/pages/docs/fixed-header.astro b/apps/docs/src/pages/docs/fixed-header.astro index 6e242a45..b004719e 100644 --- a/apps/docs/src/pages/docs/fixed-header.astro +++ b/apps/docs/src/pages/docs/fixed-header.astro @@ -22,21 +22,26 @@ import PropsTable from '../../components/PropsTable.astro'; code={`import DataTable, { type TableColumn } from 'react-data-table-component'; const columns: TableColumn[] = [ - { name: '#', selector: r => r.id, width: '60px' }, - { name: 'Name', selector: r => r.name, sortable: true }, + { name: '#', selector: r => r.id, width: '60px' }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Salary', selector: r => r.salary, sortable: true, - right: true, format: r => \`$\${r.salary.toLocaleString()}\` }, - { name: 'Status', selector: r => r.status }, + { + name: 'Salary', + selector: r => r.salary, + sortable: true, + right: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + }, + { name: 'Status', selector: r => r.status }, ]; `} +/>;`} > @@ -81,7 +86,7 @@ const columns: TableColumn[] = [ -

`} /> +
;`} />

Trade-offs: features that rely on the table's own scrollport degrade. Pinned @@ -106,7 +111,7 @@ const columns: TableColumn[] = [ fixedHeaderScrollHeight="400px" pagination paginationPerPage={20} -/>`} /> +/>;`} />

With selection

@@ -114,13 +119,7 @@ const columns: TableColumn[] = [ checkbox remains accessible at the top of the scrollable area.

- `} /> + ;`} />

Tracking scroll position with onScroll

@@ -140,7 +139,7 @@ const columns: TableColumn[] = [ fixedHeader fixedHeaderScrollHeight="300px" onScroll={e => setScrollTop(Math.round((e.target as HTMLDivElement).scrollTop))} -/>`} +/>;`} > diff --git a/apps/docs/src/pages/docs/footer.astro b/apps/docs/src/pages/docs/footer.astro index 552fcd54..aa60aedb 100644 --- a/apps/docs/src/pages/docs/footer.astro +++ b/apps/docs/src/pages/docs/footer.astro @@ -80,7 +80,7 @@ const columns: TableColumn[] = [ }, ]; -`} +;`} > @@ -112,25 +112,41 @@ const columns: TableColumn[] = [ function SummaryFooter({ rows }: FooterComponentProps) { const totalSalary = rows.reduce((s, r) => s + r.salary, 0); - const totalBonus = rows.reduce((s, r) => s + r.bonus, 0); - const avgSalary = rows.length ? Math.round(totalSalary / rows.length) : 0; + const totalBonus = rows.reduce((s, r) => s + r.bonus, 0); + const avgSalary = rows.length ? Math.round(totalSalary / rows.length) : 0; return ( -

- {rows.length} employees - Salary total: \${totalSalary.toLocaleString()} - Salary avg: \${avgSalary.toLocaleString()} - Bonus total: \${totalBonus.toLocaleString()} - Total comp: \${(totalSalary + totalBonus).toLocaleString()} +
+ + {rows.length} employees + + + Salary total: \${totalSalary.toLocaleString()} + + + Salary avg: \${avgSalary.toLocaleString()} + + + Bonus total: \${totalBonus.toLocaleString()} + + + Total comp: \${(totalSalary + totalBonus).toLocaleString()} +
); } -`} +;`} > @@ -177,7 +193,7 @@ function SummaryFooter({ rows }: FooterComponentProps) { }, }; -`} /> +;`} />

All built-in themes (including their dark modes) set a background.footer value @@ -185,13 +201,17 @@ function SummaryFooter({ rows }: FooterComponentProps) { theme with createTheme, set background.footer to control it:

- + 'default', +);`} />

The CSS variable --rdt-color-footer-bg is emitted automatically when diff --git a/apps/docs/src/pages/docs/getting-started.astro b/apps/docs/src/pages/docs/getting-started.astro index 10d23922..05789935 100644 --- a/apps/docs/src/pages/docs/getting-started.astro +++ b/apps/docs/src/pages/docs/getting-started.astro @@ -24,14 +24,14 @@ import PropsTable from '../../components/PropsTable.astro'; row.name, sortable: true }, - { name: 'Role', selector: row => row.role }, - { name: 'Salary', selector: row => row.salary, right: true }, + { name: 'Name', selector: row => row.name, sortable: true }, + { name: 'Role', selector: row => row.role }, + { name: 'Salary', selector: row => row.salary, right: true }, ]; const data = [ - { id: 1, name: 'Aria Chen', role: 'Engineering Lead', salary: 155000 }, - { id: 2, name: 'Marcus Webb', role: 'Product Manager', salary: 132000 }, + { id: 1, name: 'Aria Chen', role: 'Engineering Lead', salary: 155000 }, + { id: 2, name: 'Marcus Webb', role: 'Product Manager', salary: 132000 }, ]; export default function App() { @@ -60,11 +60,7 @@ export default function App() {

`} /> +;`} />

For display-only tables (no row selection) this doesn't affect rendering. DataTable falls back @@ -88,7 +84,7 @@ interface Employee { } const columns: TableColumn[] = [ - { name: 'Name', selector: row => row.name }, + { name: 'Name', selector: row => row.name }, { name: 'Salary', selector: row => row.salary }, ];`} /> diff --git a/apps/docs/src/pages/docs/headless.astro b/apps/docs/src/pages/docs/headless.astro index 55e3df7f..627b3181 100644 --- a/apps/docs/src/pages/docs/headless.astro +++ b/apps/docs/src/pages/docs/headless.astro @@ -197,9 +197,9 @@ interface Person { } const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: row => row.name, sortable: true }, + { id: 'name', name: 'Name', selector: row => row.name, sortable: true }, { id: 'email', name: 'Email', selector: row => row.email }, - { id: 'role', name: 'Role', selector: row => row.role }, + { id: 'role', name: 'Role', selector: row => row.role }, ]; function MyTable({ data }: { data: Person[] }) { @@ -208,11 +208,11 @@ function MyTable({ data }: { data: Person[] }) { // 1. Column order + drag state const { tableColumns, defaultSortColumn, defaultSortDirection } = useColumns( columns, - () => {}, // onColumnOrderChange - undefined, // onColumnGroupOrderChange - undefined, // columnGroups - null, // defaultSortFieldId - true, // defaultSortAsc + () => {}, // onColumnOrderChange + undefined, // onColumnGroupOrderChange + undefined, // columnGroups + null, // defaultSortFieldId + true, // defaultSortAsc ); // 2. Sort / page / selection state @@ -269,13 +269,16 @@ function MyTable({ data }: { data: Person[] }) { {tableColumns.map(col => ( col.sortable && handleSort({ - type: 'SORT_CHANGE', - selectedColumn: col, - clearSelectedOnSort: false, - additive: false, - defaultSortDirection: SortOrder.ASC, - })} + onClick={() => + col.sortable && + handleSort({ + type: 'SORT_CHANGE', + selectedColumn: col, + clearSelectedOnSort: false, + additive: false, + defaultSortDirection: SortOrder.ASC, + }) + } > {col.name} @@ -310,7 +313,7 @@ function MyTable({ data }: { data: Person[] }) { columnGroups: ColumnGroup[] | undefined, defaultSortFieldId: string | number | null | undefined, defaultSortAsc: boolean, -): ColumnsHook`} /> +): ColumnsHook;`} />

Pass undefined for onColumnGroupOrderChange and columnGroups @@ -338,7 +341,7 @@ function MyTable({ data }: { data: Person[] }) {

useTableState<T>

- (props: UseTableStateProps): UseTableStateReturn`} /> + (props: UseTableStateProps): UseTableStateReturn;`} />

Props:

@@ -411,7 +414,7 @@ function MyTable({ data }: { data: Person[] }) {

useTableData<T>

- (props: UseTableDataProps): UseTableDataReturn`} /> + (props: UseTableDataProps): UseTableDataReturn;`} /> ( columns: TableColumn[], controlledFilterValues?: Record, onFilterChange?: (columnId: string | number, filter: FilterState) => void, -): UseColumnFilterResult`} /> +): UseColumnFilterResult;`} />

Omit both optional arguments to run in uncontrolled mode (internal state). @@ -490,7 +493,7 @@ function useColumnFilter( activeCell: ActiveCell | null; handleNavFocus: (e: React.FocusEvent) => void; handleNavKeyDown: (e: React.KeyboardEvent) => void; -}`} /> +};`} /> ; handleResizeStart: (columnId: string | number, e: React.MouseEvent) => void; -}`} /> +};`} /> ( columns: TableColumn[], @@ -606,19 +609,23 @@ function getPinnedTotalWidths( selectableRows: boolean, expandableRows: boolean, expandableRowsHideExpander: boolean, -): { left: number; right: number } +): { left: number; right: number }; function getPinnedCellMeta( column: TableColumn, pinnedOffsets: PinnedOffsets | undefined, zIndex?: number, -): PinnedCellMeta`} /> +): PinnedCellMeta;`} /> ; pinnedOffsets: PinnedOffsets }) { const meta = getPinnedCellMeta(column, pinnedOffsets, 1); - return {/* ... */}; + return ( + + {/* ... */} + + ); }`} />

@@ -692,7 +699,7 @@ useEffect(() => { user sees — sorted, filtered, and paginated — rather than the raw dataset.

- (options: UseTableExportOptions): UseTableExportResult`} /> + (options: UseTableExportOptions): UseTableExportResult;`} />

Options

@@ -722,20 +729,24 @@ useEffect(() => {

Example — export the current filtered view

[] = [ - { id: 'name', name: 'Name', selector: r => r.name }, - { id: 'dept', name: 'Department', selector: r => r.department }, - { id: 'salary', name: 'Salary', selector: r => r.salary }, + { id: 'name', name: 'Name', selector: r => r.name }, + { id: 'dept', name: 'Department', selector: r => r.department }, + { id: 'salary', name: 'Salary', selector: r => r.salary }, ]; function MyTable({ data }: { data: Employee[] }) { const { tableColumns } = useColumns(columns, () => {}, undefined, undefined, null, true); - const { tableState } = useTableState({ data, keyField: 'id', /* … */ }); - const { tableRows } = useTableData({ data, columns: tableColumns, /* … */ }); + const { tableState } = useTableState({ data, keyField: 'id' /* … */ }); + const { tableRows } = useTableData({ data, columns: tableColumns /* … */ }); const { filterValues, handleFilterChange, filteredData } = useColumnFilter(tableColumns); const rows = filteredData(tableRows); diff --git a/apps/docs/src/pages/docs/inline-editing.astro b/apps/docs/src/pages/docs/inline-editing.astro index 52d804c9..32570d16 100644 --- a/apps/docs/src/pages/docs/inline-editing.astro +++ b/apps/docs/src/pages/docs/inline-editing.astro @@ -48,47 +48,56 @@ export default function App() { const handleCellEdit = (row: Employee, value: string, column: TableColumn) => { const field = column.id as keyof Employee; setData(prev => - prev.map(r => - r.id === row.id - ? { ...r, [field]: field === 'salary' ? Number(value) || r.salary : value } - : r, - ), + prev.map(r => (r.id === row.id ? { ...r, [field]: field === 'salary' ? Number(value) || r.salary : value } : r)), ); }; const columns: TableColumn[] = [ // Text editor — editable: true is shorthand for { editor: { type: 'text' } } - { id: 'name', name: 'Name', selector: r => r.name, - editable: true, onCellEdit: handleCellEdit }, + { id: 'name', name: 'Name', selector: r => r.name, editable: true, onCellEdit: handleCellEdit }, // Dropdown editor - { id: 'department', name: 'Department', selector: r => r.department, + { + id: 'department', + name: 'Department', + selector: r => r.department, editor: { type: 'select', options: [ { value: 'Engineering', label: 'Engineering' }, - { value: 'Product', label: 'Product' }, - { value: 'Design', label: 'Design' }, + { value: 'Product', label: 'Product' }, + { value: 'Design', label: 'Design' }, ], }, - onCellEdit: handleCellEdit }, + onCellEdit: handleCellEdit, + }, // Custom cell renderer + dropdown editor compose freely - { id: 'status', name: 'Status', selector: r => r.status, + { + id: 'status', + name: 'Status', + selector: r => r.status, cell: row => , editor: { type: 'select', options: [ - { value: 'Active', label: 'Active' }, - { value: 'On Leave', label: 'On Leave' }, + { value: 'Active', label: 'Active' }, + { value: 'On Leave', label: 'On Leave' }, { value: 'Terminated', label: 'Terminated' }, ], }, - onCellEdit: handleCellEdit }, + onCellEdit: handleCellEdit, + }, - { id: 'salary', name: 'Salary', selector: r => r.salary, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, format: r => \`$\${r.salary.toLocaleString()}\`, - right: true, editable: true, onCellEdit: handleCellEdit }, + right: true, + editable: true, + onCellEdit: handleCellEdit, + }, ]; return ; @@ -181,7 +190,7 @@ export default function App() { editor: { type: 'select', options: [ - { value: 'active', label: 'Active' }, + { value: 'active', label: 'Active' }, { value: 'on_leave', label: 'On Leave' }, { value: 'terminated', label: 'Terminated' }, ], @@ -248,7 +257,7 @@ export default function App() { /> @@ -261,9 +270,7 @@ export default function App() {

{ - setData(prev => - prev.map(r => (r.id === row.id ? { ...r, [column.id as string]: value } : r)) - ); + setData(prev => prev.map(r => (r.id === row.id ? { ...r, [column.id as string]: value } : r))); };`} />

@@ -273,16 +280,14 @@ export default function App() { { // Optimistic update - setData(prev => - prev.map(r => (r.id === row.id ? { ...r, [column.id as string]: value } : r)) - ); + setData(prev => prev.map(r => (r.id === row.id ? { ...r, [column.id as string]: value } : r))); try { await api.updateEmployee(row.id, { [column.id as string]: value }); } catch (err) { // Roll back on failure setData(prev => - prev.map(r => (r.id === row.id ? { ...r, [column.id as string]: row[column.id as keyof typeof row] } : r)) + prev.map(r => (r.id === row.id ? { ...r, [column.id as string]: row[column.id as keyof typeof row] } : r)), ); console.error('Save failed:', err); } @@ -328,9 +333,8 @@ export default function App() { [] = [ - { id: 'name', name: 'Name', selector: r => r.name }, // never editable - { id: 'salary', name: 'Salary', selector: r => r.salary, - editable: canEdit, onCellEdit: handleCellEdit }, // editable only for admins + { id: 'name', name: 'Name', selector: r => r.name }, // never editable + { id: 'salary', name: 'Salary', selector: r => r.salary, editable: canEdit, onCellEdit: handleCellEdit }, // editable only for admins ];`} />

Combining with custom cell renderers

@@ -347,8 +351,8 @@ const columns: TableColumn[] = [ editor: { type: 'select', options: [ - { value: 'Active', label: 'Active' }, - { value: 'On Leave', label: 'On Leave' }, + { value: 'Active', label: 'Active' }, + { value: 'On Leave', label: 'On Leave' }, { value: 'Terminated', label: 'Terminated' }, ], }, @@ -408,7 +412,7 @@ const columns: TableColumn[] = [ covers header, selection, and expander cells, and works on read-only tables.

- `} /> + ;`} />

Limitations

    diff --git a/apps/docs/src/pages/docs/keyboard-navigation.astro b/apps/docs/src/pages/docs/keyboard-navigation.astro index bc16da67..31e6a542 100644 --- a/apps/docs/src/pages/docs/keyboard-navigation.astro +++ b/apps/docs/src/pages/docs/keyboard-navigation.astro @@ -23,7 +23,7 @@ import PropsTable from '../../components/PropsTable.astro'; read-only tables too.

    - `} /> + ;`} />

    All shortcuts

    @@ -58,7 +58,7 @@ import PropsTable from '../../components/PropsTable.astro'; `} + code={`;`} > @@ -81,7 +81,7 @@ import PropsTable from '../../components/PropsTable.astro'; { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, right: true }, ]; -`} +;`} > @@ -102,11 +102,10 @@ import PropsTable from '../../components/PropsTable.astro'; code={`const columns: TableColumn[] = [ { id: 'name', name: 'Name', selector: r => r.name, editable: true, onCellEdit: handleCellEdit }, { id: 'department', name: 'Department', selector: r => r.department }, // no editor - { id: 'salary', name: 'Salary', selector: r => r.salary, right: true, - editable: true, onCellEdit: handleCellEdit }, + { id: 'salary', name: 'Salary', selector: r => r.salary, right: true, editable: true, onCellEdit: handleCellEdit }, ]; -`} +;`} > @@ -130,7 +129,7 @@ import PropsTable from '../../components/PropsTable.astro'; selectableRows expandableRows expandableRowsComponent={RowDetail} -/>`} +/>;`} > diff --git a/apps/docs/src/pages/docs/loading.astro b/apps/docs/src/pages/docs/loading.astro index 135e5167..8349703b 100644 --- a/apps/docs/src/pages/docs/loading.astro +++ b/apps/docs/src/pages/docs/loading.astro @@ -32,20 +32,26 @@ import LoadingDemo from '../../components/demos/LoadingDemo'; import DataTable from 'react-data-table-component'; export default function UsersTable() { - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [loading, setLoading] = useState(true); // true on first render → skeleton immediately useEffect(() => { fetch('/api/users') .then(r => r.json()) - .then(json => { setData(json.rows); setLoading(false); }); + .then(json => { + setData(json.rows); + setLoading(false); + }); }, []); function refresh() { - setLoading(true); // has existing rows → overlay mode + setLoading(true); // has existing rows → overlay mode fetch('/api/users') .then(r => r.json()) - .then(json => { setData(json.rows); setLoading(false); }); + .then(json => { + setData(json.rows); + setLoading(false); + }); } return ; @@ -62,10 +68,7 @@ export default function UsersTable() { set progressSkeleton={false} to show your progressComponent there too.

    - } -/>`} lang="tsx" /> + } />;`} lang="tsx" />

    Empty state

    @@ -74,11 +77,7 @@ export default function UsersTable() { the noDataComponent is shown instead.

    - No results match your search.

    } -/>`} lang="tsx" /> + No results match your search.

    } />;`} lang="tsx" />

    Header visibility

    @@ -88,7 +87,7 @@ export default function UsersTable() {

    `} lang="tsx" /> +;`} lang="tsx" />

    Prop reference

    diff --git a/apps/docs/src/pages/docs/localization.astro b/apps/docs/src/pages/docs/localization.astro index 98e1168d..62a05711 100644 --- a/apps/docs/src/pages/docs/localization.astro +++ b/apps/docs/src/pages/docs/localization.astro @@ -23,7 +23,7 @@ import PropsTable from '../../components/PropsTable.astro'; `} /> +;`} />

    Built-in locales

    @@ -69,7 +69,7 @@ const myLocale: Localization = { }, }; -`} /> +;`} />

    Custom locale from scratch

    @@ -81,11 +81,11 @@ const myLocale: Localization = { const custom: Localization = { pagination: { - navigationAriaLabel: 'Paginación de la tabla', - firstPageAriaLabel: 'Primera página', + navigationAriaLabel: 'Paginación de la tabla', + firstPageAriaLabel: 'Primera página', previousPageAriaLabel: 'Página anterior', - nextPageAriaLabel: 'Página siguiente', - lastPageAriaLabel: 'Última página', + nextPageAriaLabel: 'Página siguiente', + lastPageAriaLabel: 'Última página', }, filter: { applyLabel: 'Aplicar', @@ -95,17 +95,17 @@ const custom: Localization = { orLabel: 'O', operators: { contains: 'Contiene', - equals: 'Igual a', - blank: 'Vacío', + equals: 'Igual a', + blank: 'Vacío', }, }, expandable: { - expandRowAriaLabel: 'Expandir fila', + expandRowAriaLabel: 'Expandir fila', collapseRowAriaLabel: 'Contraer fila', }, }; -`} /> +;`} />

    The Localization type

    @@ -114,67 +114,67 @@ const custom: Localization = { diff --git a/apps/docs/src/pages/docs/mobile.astro b/apps/docs/src/pages/docs/mobile.astro index 186db23e..21778c01 100644 --- a/apps/docs/src/pages/docs/mobile.astro +++ b/apps/docs/src/pages/docs/mobile.astro @@ -21,15 +21,13 @@ import MobileDemo from '../../components/demos/MobileDemo.tsx'; code={`import DataTable, { type TableColumn } from 'react-data-table-component'; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, - { name: 'Department', selector: r => r.department, hide: 'sm' }, - { name: 'Salary', selector: r => r.salary, right: true, hide: 'sm', - format: r => \`$\${r.salary.toLocaleString()}\` }, - { name: 'Status', selector: r => r.status, center: true, - cell: r => }, + { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Department', selector: r => r.department, hide: 'sm' }, + { name: 'Salary', selector: r => r.salary, right: true, hide: 'sm', format: r => \`$\${r.salary.toLocaleString()}\` }, + { name: 'Status', selector: r => r.status, center: true, cell: r => }, ]; -`} +;`} > @@ -50,12 +48,45 @@ const columns: TableColumn[] = [

    [] = [ - { name: 'Name', selector: r => r.name }, // always visible - { name: 'Email', selector: r => r.email, hide: 'sm' }, // hidden below 600px + { name: 'Name', selector: r => r.name }, // always visible + { name: 'Email', selector: r => r.email, hide: 'sm' }, // hidden below 600px { name: 'Salary', selector: r => r.salary, hide: 'md' }, // hidden below 960px { name: 'Joined', selector: r => r.joined, hide: 'lg' }, // hidden below 1280px ];`} /> +

    + hide only supports these three fixed breakpoints. For a custom pixel value, track + the viewport yourself with matchMedia and drive the column's omit{' '} + prop instead: +

    + + window.matchMedia(query).matches); + + useEffect(() => { + const mql = window.matchMedia(query); + const onChange = () => setMatches(mql.matches); + mql.addEventListener('change', onChange); + return () => mql.removeEventListener('change', onChange); + }, [query]); + + return matches; +} + +function MyTable() { + const hideNotes = useMediaQuery('(max-width: 480px)'); + + const columns: TableColumn[] = [ + { name: 'Name', selector: r => r.name }, + { name: 'Notes', selector: r => r.notes, omit: hideNotes }, // hidden below 480px + ]; + + return ; +}`} /> +

    Pagination on small screens

    @@ -85,15 +116,14 @@ const columns: TableColumn[] = [

    For apps where the primary use case on mobile is looking up a single record rather than comparing rows, consider rendering a completely different component below your breakpoint — - a simple list or card feed — and only mounting DataTable on wider viewports: + a simple list or card feed — and only mounting DataTable on wider viewports. + Reusing the useMediaQuery hook from above:

    - ; } diff --git a/apps/docs/src/pages/docs/pagination.astro b/apps/docs/src/pages/docs/pagination.astro index dd9387a2..31d365d8 100644 --- a/apps/docs/src/pages/docs/pagination.astro +++ b/apps/docs/src/pages/docs/pagination.astro @@ -27,19 +27,34 @@ import DataTable, { type TableColumn } from 'react-data-table-component'; const data = Array.from({ length: 30 }, (_, i) => ({ id: i + 1, - name: ['Aria Chen','Marcus Webb','Priya Kapoor','Jordan Ellis','Sam Rivera', - 'Taylor Brooks','Casey Morgan','Alex Kim','Morgan Lee','Drew Park'][i % 10], - department: ['Engineering','Product','Design','Analytics','Sales','HR'][i % 6], + name: [ + 'Aria Chen', + 'Marcus Webb', + 'Priya Kapoor', + 'Jordan Ellis', + 'Sam Rivera', + 'Taylor Brooks', + 'Casey Morgan', + 'Alex Kim', + 'Morgan Lee', + 'Drew Park', + ][i % 10], + department: ['Engineering', 'Product', 'Design', 'Analytics', 'Sales', 'HR'][i % 6], salary: 80000 + i * 3700, - status: ['Active','Remote','On Leave','Contractor'][i % 4], + status: ['Active', 'Remote', 'On Leave', 'Contractor'][i % 4], })); -const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, +const columns: TableColumn<(typeof data)[0]>[] = [ + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Salary', selector: r => r.salary, sortable: true, - format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, - { name: 'Status', selector: r => r.status }, + { + name: 'Salary', + selector: r => r.salary, + sortable: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + right: true, + }, + { name: 'Status', selector: r => r.status }, ]; export default function App() { @@ -48,7 +63,9 @@ export default function App() {
    {[5, 10, 15].map(n => ( - + ))}
    {/* key remounts the table so paginationPerPage takes effect */} @@ -82,8 +99,8 @@ export default function App() { columns={columns} data={data} pagination - paginationPosition="top" // "top" | "bottom" | "both" -/>`} + paginationPosition="top" // "top" | "bottom" | "both" +/>;`} > @@ -102,18 +119,23 @@ export default function App() { code={`import { useState, useEffect, useCallback } from 'react'; import DataTable, { type TableColumn } from 'react-data-table-component'; -interface Employee { id: number; name: string; department: string; salary: number; status: string; } +interface Employee { + id: number; + name: string; + department: string; + salary: number; + status: string; +} const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name }, + { name: 'Name', selector: r => r.name }, { name: 'Department', selector: r => r.department }, - { name: 'Salary', selector: r => r.salary, right: true, - format: r => \`$\${r.salary.toLocaleString()}\` }, - { name: 'Status', selector: r => r.status }, + { name: 'Salary', selector: r => r.salary, right: true, format: r => \`$\${r.salary.toLocaleString()}\` }, + { name: 'Status', selector: r => r.status }, ]; export default function App() { - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [totalRows, setTotal] = useState(0); const [perPage, setPerPage] = useState(10); @@ -127,7 +149,9 @@ export default function App() { setLoading(false); }, []); - useEffect(() => { load(1, perPage); }, []); + useEffect(() => { + load(1, perPage); + }, []); return ( load(page, perPage)} - onChangeRowsPerPage={(pp, page) => { setPerPage(pp); load(page, pp); }} + onChangeRowsPerPage={(pp, page) => { + setPerPage(pp); + load(page, pp); + }} highlightOnHover /> ); @@ -173,7 +200,7 @@ export default function App() { const field = col.sortField ?? String(col.id); setSortField(field); setSortDir(dir); - setPage(1); // keep local state in sync with the UI reset + setPage(1); // keep local state in sync with the UI reset fetchData({ page: 1, perPage, sortField: field, sortDir: dir }); }`} /> @@ -190,23 +217,26 @@ export default function App() { import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component'; export default function App() { - const [data, setData] = useState([]); - const [loading, setLoading] = useState(true); - const [totalRows, setTotal] = useState(0); - const [page, setPage] = useState(1); - const [perPage, setPerPage] = useState(10); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [totalRows, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); const [sortField, setSortField] = useState('name'); - const [sortDir, setSortDir] = useState(SortOrder.ASC); + const [sortDir, setSortDir] = useState(SortOrder.ASC); const [resetPage, setResetPage] = useState(false); - const load = useCallback(async (params) => { + const load = useCallback(async params => { setLoading(true); const result = await fetchFromServer(params); - setData(result.rows); setTotal(result.total); + setData(result.rows); + setTotal(result.total); setLoading(false); }, []); - useEffect(() => { load({ page, perPage, sortField, sortDir }); }, []); + useEffect(() => { + load({ page, perPage, sortField, sortDir }); + }, []); function handlePageChange(p: number) { setPage(p); @@ -220,8 +250,10 @@ export default function App() { function handleSort(col: TableColumn, dir: SortOrder) { const field = col.sortField ?? String(col.id); - setSortField(field); setSortDir(dir); - setPage(1); setResetPage(prev => !prev); + setSortField(field); + setSortDir(dir); + setPage(1); + setResetPage(prev => !prev); load({ page: 1, perPage, sortField: field, sortDir: dir }); } @@ -260,22 +292,23 @@ export default function App() { import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component'; export default function App() { - const [data, setData] = useState([]); - const [loading, setLoading] = useState(false); - const [totalRows, setTotal] = useState(0); - const [page, setPage] = useState(1); - const [perPage, setPerPage] = useState(10); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [totalRows, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); const [sortField, setSortField] = useState('name'); - const [sortDir, setSortDir] = useState(SortOrder.ASC); - const [search, setSearch] = useState(''); + const [sortDir, setSortDir] = useState(SortOrder.ASC); + const [search, setSearch] = useState(''); const [resetPage, setResetPage] = useState(false); - const load = useCallback(async (params) => { + const load = useCallback(async params => { setLoading(true); const qs = new URLSearchParams(params); const res = await fetch(\`/api/employees?\${qs}\`); const json = await res.json(); - setData(json.rows); setTotal(json.total); + setData(json.rows); + setTotal(json.total); setLoading(false); }, []); @@ -295,7 +328,8 @@ export default function App() { function handleSort(col: TableColumn, dir: SortOrder) { const field = col.sortField ?? 'name'; - setSortField(field); setSortDir(dir); + setSortField(field); + setSortDir(dir); setPage(1); load({ page: 1, perPage, sort: field, dir, q: search }); } @@ -309,12 +343,7 @@ export default function App() { return (
    - handleSearch(e.target.value)} - /> + handleSearch(e.target.value)} /> (SortOrder.ASC); + const [sortDir, setSortDir] = useState(SortOrder.ASC); const { data, isLoading } = useQuery({ queryKey: ['employees', page, perPage, sortField, sortDir], queryFn: () => - fetch(\`/api/employees?page=\${page}&limit=\${perPage}&sort=\${sortField}&dir=\${sortDir}\`) - .then(r => r.json()), + fetch(\`/api/employees?page=\${page}&limit=\${perPage}&sort=\${sortField}&dir=\${sortDir}\`).then(r => r.json()), placeholderData: keepPreviousData, }); @@ -371,7 +399,10 @@ export default function App() { paginationServer paginationTotalRows={data?.total ?? 0} onChangePage={setPage} - onChangeRowsPerPage={(pp) => { setPerPage(pp); setPage(1); }} + onChangeRowsPerPage={pp => { + setPerPage(pp); + setPage(1); + }} sortServer onSort={(col, dir) => { setSortField(col.sortField ?? String(col.id)); @@ -391,9 +422,9 @@ import DataTable, { type TableColumn, SortOrder } from 'react-data-table-compone const fetcher = (url: string) => fetch(url).then(r => r.json()); export default function App() { - const [page, setPage] = useState(1); + const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(10); - const [sort, setSort] = useState({ field: 'name', dir: SortOrder.ASC }); + const [sort, setSort] = useState({ field: 'name', dir: SortOrder.ASC }); const url = \`/api/employees?page=\${page}&limit=\${perPage}&sort=\${sort.field}&dir=\${sort.dir}\`; const { data, isLoading } = useSWR(url, fetcher); @@ -407,7 +438,10 @@ export default function App() { paginationServer paginationTotalRows={data?.total ?? 0} onChangePage={setPage} - onChangeRowsPerPage={(pp) => { setPerPage(pp); setPage(1); }} + onChangeRowsPerPage={pp => { + setPerPage(pp); + setPage(1); + }} sortServer onSort={(col, dir) => { setSort({ field: col.sortField ?? String(col.id), dir }); @@ -446,7 +480,7 @@ function handleSearch(q: string) { persistSelectedOnPageChange: true, persistSelectedOnSort: true, }} -/>`} /> +/>;`} />

    Custom pagination component

    @@ -465,7 +499,9 @@ function MyPagination({ rowsPerPage, rowCount, currentPage, onChangePage }: Pagi - Page {currentPage} of {pages} + + Page {currentPage} of {pages} + @@ -473,12 +509,7 @@ function MyPagination({ rowsPerPage, rowCount, currentPage, onChangePage }: Pagi ); } -`} /> +;`} />

    Localization

    @@ -493,7 +524,7 @@ function MyPagination({ rowsPerPage, rowCount, currentPage, onChangePage }: Pagi import DataTable from 'react-data-table-component'; import { fr } from 'react-data-table-component/locales'; -`} /> +;`} /> `} /> +;`} /> `} /> +;`} />

    Prop reference

    diff --git a/apps/docs/src/pages/docs/performance.astro b/apps/docs/src/pages/docs/performance.astro index e641e76f..418f3952 100644 --- a/apps/docs/src/pages/docs/performance.astro +++ b/apps/docs/src/pages/docs/performance.astro @@ -41,28 +41,26 @@ import DocsTable from '../../components/DocsTable.astro'; r.name }, // new function every render - ]} - data={items} - />; + return ( + r.name }, // new function every render + ]} + data={items} + /> + ); } // ❌ Inline conditional row styles → new array → all rows re-render r.flagged, style: { color: 'red' } }, - ]} + conditionalRowStyles={[{ when: r => r.flagged, style: { color: 'red' } }]} /* ... */ -/>`} /> +/>;`} />

    Fix: hoist or memoize the references.

    [] = [ - { name: 'Name', selector: r => r.name }, -]; +const columns: TableColumn[] = [{ name: 'Name', selector: r => r.name }]; function App({ items }) { return ; @@ -85,11 +83,14 @@ const columns = useMemo[]>( every render changes the handler identity and the row context updates downstream.

    - { - navigate(\`/employees/\${row.id}\`); -}, [navigate]); + { + navigate(\`/employees/\${row.id}\`); + }, + [navigate], +); -`} /> +;`} />

    Stabilize the data array

    @@ -101,7 +102,7 @@ const columns = useMemo[]>( s.items.map(transform)); // new array + const items = useStore(s => s.items.map(transform)); // new array return ; } @@ -125,7 +126,7 @@ function App() { data={pageOfData} sortServer onSort={(column, direction) => refetch({ sortBy: column.id, sortDir: direction })} -/>`} /> +/>;`} />

    Server-side pagination

    @@ -137,13 +138,13 @@ function App() { `} /> +/>;`} />

    Column filtering

    @@ -170,10 +171,8 @@ useEffect(() => { columns={columns} data={data} filterValues={filters} - onFilterChange={(columnId, next) => - setFilters(prev => ({ ...prev, [columnId]: next })) - } -/>`} /> + onFilterChange={(columnId, next) => setFilters(prev => ({ ...prev, [columnId]: next }))} +/>;`} />

    Cell renderers

    @@ -184,11 +183,15 @@ useEffect(() => {

    {row.name} } +{ + cell: row => {row.name}; +} // ✅ Static + conditional override const baseStyle = { padding: 8 }; -{ cell: row => {row.name} }`} /> +{ + cell: row => {row.name}; +}`} />

    Avoid expensive selectors

    @@ -198,14 +201,15 @@ const baseStyle = { padding: 8 };

    r.tags.find(t => t.primary)?.name ?? '' } +{ + selector: r => r.tags.find(t => t.primary)?.name ?? ''; +} // ✅ Compute upstream, store on the row -const enriched = useMemo( - () => rows.map(r => ({ ...r, primaryTag: r.tags.find(t => t.primary)?.name ?? '' })), - [rows], -); -{ selector: r => r.primaryTag }`} /> +const enriched = useMemo(() => rows.map(r => ({ ...r, primaryTag: r.tags.find(t => t.primary)?.name ?? '' })), [rows]); +{ + selector: r => r.primaryTag; +}`} />

    Animations

    diff --git a/apps/docs/src/pages/docs/resizable.astro b/apps/docs/src/pages/docs/resizable.astro index 0b4a476c..dd4165cb 100644 --- a/apps/docs/src/pages/docs/resizable.astro +++ b/apps/docs/src/pages/docs/resizable.astro @@ -22,14 +22,19 @@ import PropsTable from '../../components/PropsTable.astro'; code={`import DataTable, { type TableColumn } from 'react-data-table-component'; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, width: '180px', minWidth: '100px' }, - { name: 'Role', selector: r => r.role, minWidth: '120px' }, + { name: 'Name', selector: r => r.name, width: '180px', minWidth: '100px' }, + { name: 'Role', selector: r => r.role, minWidth: '120px' }, { name: 'Department', selector: r => r.department, width: '140px', minWidth: '80px' }, - { name: 'Salary', selector: r => r.salary, width: '110px', right: true, - format: r => \`$\${r.salary.toLocaleString()}\` }, + { + name: 'Salary', + selector: r => r.salary, + width: '110px', + right: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + }, ]; -`} +;`} > @@ -53,12 +58,12 @@ const columns: TableColumn[] = [

    [] = [ - { name: 'Name', selector: r => r.name, width: '200px', minWidth: '120px' }, + { name: 'Name', selector: r => r.name, width: '200px', minWidth: '120px' }, { name: 'Status', selector: r => r.status, width: '100px' }, - { name: 'Notes', selector: r => r.notes, /* grows to fill remaining space */ }, + { name: 'Notes', selector: r => r.notes /* grows to fill remaining space */ }, ]; -`} /> +;`} />

    The hard floor is 40 px. DataTable won't allow narrower than that regardless of minWidth.

    @@ -84,8 +89,11 @@ import DataTable, { type TableColumn } from 'react-data-table-component'; const STORAGE_KEY = 'employees-table-widths'; function loadWidths(): Record { - try { return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}'); } - catch { return {}; } + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}'); + } catch { + return {}; + } } export default function App() { @@ -112,10 +120,8 @@ export default function App() { }, []); const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, - width: saved['name'] ?? '180px' }, - { id: 'salary', name: 'Salary', selector: r => r.salary, right: true, - width: saved['salary'] ?? '110px' }, + { id: 'name', name: 'Name', selector: r => r.name, width: saved['name'] ?? '180px' }, + { id: 'salary', name: 'Salary', selector: r => r.salary, right: true, width: saved['salary'] ?? '110px' }, ]; return ( diff --git a/apps/docs/src/pages/docs/row-interactions.astro b/apps/docs/src/pages/docs/row-interactions.astro index 2398e85e..caab8597 100644 --- a/apps/docs/src/pages/docs/row-interactions.astro +++ b/apps/docs/src/pages/docs/row-interactions.astro @@ -21,13 +21,17 @@ import PropsTable from '../../components/PropsTable.astro'; import DataTable, { type TableColumn } from 'react-data-table-component'; interface Employee { - id: number; name: string; department: string; role: string; email: string; + id: number; + name: string; + department: string; + role: string; + email: string; } const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Role', selector: r => r.role }, ]; export default function App() { @@ -35,15 +39,11 @@ export default function App() { return (
    - setSelected(row)} - /> + setSelected(row)} /> {selected && ( -
    {selected.name} — {selected.role}, {selected.department}
    +
    + {selected.name} — {selected.role}, {selected.department} +
    )}
    ); @@ -79,9 +79,9 @@ export default function App() { import { useNavigate } from 'react-router-dom'; // or your router const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name }, + { name: 'Name', selector: r => r.name }, { name: 'Department', selector: r => r.department }, - { name: 'Role', selector: r => r.role }, + { name: 'Role', selector: r => r.role }, { name: 'Email', ignoreRowClick: true, @@ -109,7 +109,7 @@ export default function App() { navigate(\`/employees/\${row.id}\`); } }} - onRowMiddleClicked={(row) => { + onRowMiddleClicked={row => { window.open(\`/employees/\${row.id}\`, '_blank'); }} /> @@ -134,9 +134,9 @@ export default function App() { code={`import DataTable, { type TableColumn } from 'react-data-table-component'; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Name', selector: r => r.name, sortable: true }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Role', selector: r => r.role }, { name: 'Actions', button: true, diff --git a/apps/docs/src/pages/docs/row-pinning.astro b/apps/docs/src/pages/docs/row-pinning.astro index 2adba204..9606f8c6 100644 --- a/apps/docs/src/pages/docs/row-pinning.astro +++ b/apps/docs/src/pages/docs/row-pinning.astro @@ -47,9 +47,10 @@ export default function App() { const sortFunction: SortFunction = useMemo( () => (rows, selector, direction) => { const pinned = rows.filter(r => pinnedIds.has(r.id)); - const rest = rows.filter(r => !pinnedIds.has(r.id)); + const rest = rows.filter(r => !pinnedIds.has(r.id)); const sorted = [...rest].sort((a, b) => { - const av = selector(a), bv = selector(b); + const av = selector(a), + bv = selector(b); if (av === bv) return 0; const cmp = av > bv ? 1 : -1; return direction === SortOrder.ASC ? cmp : -cmp; @@ -67,7 +68,12 @@ export default function App() { sortable: true, cell: r => (
    - {r.name} @@ -75,8 +81,14 @@ export default function App() { ), }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, - format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + sortable: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + right: true, + }, ]; return ; @@ -102,13 +114,14 @@ export default function App() { the component so it's stable and doesn't need useMemo:

    - = (rows, selector, direction) => { const pinned = rows.filter(r => PINNED_IDS.has(r.id)); - const rest = rows.filter(r => !PINNED_IDS.has(r.id)); + const rest = rows.filter(r => !PINNED_IDS.has(r.id)); const sorted = [...rest].sort((a, b) => { - const av = selector(a), bv = selector(b); + const av = selector(a), + bv = selector(b); if (av === bv) return 0; const cmp = av > bv ? 1 : -1; return direction === SortOrder.ASC ? cmp : -cmp; @@ -116,7 +129,7 @@ const sortFunction: SortFunction = (rows, selector, direction) => { return [...pinned, ...sorted]; }; -`} /> +;`} />

    Pinning by a condition (not an ID)

    @@ -127,11 +140,12 @@ const sortFunction: SortFunction = (rows, selector, direction) => { = (rows, selector, direction) => { const pinned = rows.filter(r => r.status === 'Active'); - const rest = rows.filter(r => r.status !== 'Active'); + const rest = rows.filter(r => r.status !== 'Active'); const sort = (arr: Employee[]) => [...arr].sort((a, b) => { - const av = selector(a), bv = selector(b); + const av = selector(a), + bv = selector(b); if (av === bv) return 0; const cmp = av > bv ? 1 : -1; return direction === SortOrder.ASC ? cmp : -cmp; @@ -150,11 +164,12 @@ const sortFunction: SortFunction = (rows, selector, direction) => { const sortFunction: SortFunction = (rows, selector, direction) => { const critical = rows.filter(r => r.priority === 'critical'); const archived = rows.filter(r => r.archived); - const normal = rows.filter(r => r.priority !== 'critical' && !r.archived); + const normal = rows.filter(r => r.priority !== 'critical' && !r.archived); const sort = (arr: Employee[]) => [...arr].sort((a, b) => { - const av = selector(a), bv = selector(b); + const av = selector(a), + bv = selector(b); if (av === bv) return 0; const cmp = av > bv ? 1 : -1; return direction === SortOrder.ASC ? cmp : -cmp; @@ -175,7 +190,7 @@ const sortFunction: SortFunction = (rows, selector, direction) => { // Re-apply pin order on top of server result const pinned = sorted.filter(r => pinnedIds.has(r.id)); - const rest = sorted.filter(r => !pinnedIds.has(r.id)); + const rest = sorted.filter(r => !pinnedIds.has(r.id)); setData([...pinned, ...rest]); }`} /> @@ -199,7 +214,7 @@ const sortFunction: SortFunction = (rows, selector, direction) => { }, }, ]} -/>`} /> +/>;`} />

    Limitations

      diff --git a/apps/docs/src/pages/docs/rtl.astro b/apps/docs/src/pages/docs/rtl.astro index 14f56975..fd2b41c7 100644 --- a/apps/docs/src/pages/docs/rtl.astro +++ b/apps/docs/src/pages/docs/rtl.astro @@ -20,12 +20,7 @@ import PropsTable from '../../components/PropsTable.astro'; description="Toggle between LTR, RTL, and AUTO. In RTL mode the pagination arrows reverse, column text aligns right-to-left, and the resize handles mirror to each column's left edge. Drag a column boundary to try it." code={`import DataTable, { Direction } from 'react-data-table-component'; -`} +;`} > @@ -63,7 +58,7 @@ import PropsTable from '../../components/PropsTable.astro'; // // Then let the table pick it up automatically -`} /> +;`} />

      What flips in RTL

        @@ -84,24 +79,14 @@ import PropsTable from '../../components/PropsTable.astro'; `} /> +;`} />

        If your app already sets <html dir="rtl">, use Direction.AUTO so the table follows the page without an explicit prop:

        - `} /> + ;`} />

        Prop reference

        @@ -110,7 +95,7 @@ import { ar } from 'react-data-table-component/locales'; +Direction.LTR; // "ltr" +Direction.RTL; // "rtl" +Direction.AUTO; // "auto"`} /> diff --git a/apps/docs/src/pages/docs/selection.astro b/apps/docs/src/pages/docs/selection.astro index 379de913..58aecaf3 100644 --- a/apps/docs/src/pages/docs/selection.astro +++ b/apps/docs/src/pages/docs/selection.astro @@ -50,19 +50,19 @@ interface Employee { } const data: Employee[] = [ - { id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', status: 'Active' }, - { id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', status: 'Active' }, - { id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', status: 'On Leave' }, - { id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', status: 'Active' }, - { id: 5, name: 'Sam Rivera', role: 'DevOps Engineer', department: 'Engineering', status: 'On Leave' }, - { id: 6, name: 'Taylor Brooks', role: 'Account Manager', department: 'Sales', status: 'Active' }, + { id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering', status: 'Active' }, + { id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product', status: 'Active' }, + { id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design', status: 'On Leave' }, + { id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics', status: 'Active' }, + { id: 5, name: 'Sam Rivera', role: 'DevOps Engineer', department: 'Engineering', status: 'On Leave' }, + { id: 6, name: 'Taylor Brooks', role: 'Account Manager', department: 'Sales', status: 'Active' }, ]; const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name, sortable: true }, - { name: 'Role', selector: r => r.role }, + { name: 'Name', selector: r => r.name, sortable: true }, + { name: 'Role', selector: r => r.role }, { name: 'Department', selector: r => r.department, sortable: true }, - { name: 'Status', selector: r => r.status }, + { name: 'Status', selector: r => r.status }, ]; export default function App() { @@ -74,16 +74,17 @@ export default function App() { return (
        {selectedRows.length > 0 && ( - {selectedRows.length} selected: {selectedRows.map(r => r.name).join(', ')} + + {selectedRows.length} selected: {selectedRows.map(r => r.name).join(', ')} + )} r.status === 'On Leave') : undefined} + selectableRowDisabled={disableOnLeave ? r => r.status === 'On Leave' : undefined} onSelectedRowsChange={({ selectedRows }) => setSelectedRows(selectedRows)} highlightOnHover /> @@ -164,16 +165,10 @@ export default function App() { Toggle "Disable 'On Leave' rows" in the demo above to see this in action.

        row.status === 'On Leave'} -/>`} /> + row.status === 'On Leave'} />;`} />

        Pre-select rows

        - row.status === 'Active'} -/>`} /> + row.status === 'Active'} />;`} />

        onSelectedRowsChange

        The callback receives {`{ allSelected, selectedCount, selectedRows }`}.

        @@ -182,7 +177,7 @@ export default function App() { onSelectedRowsChange={({ selectedCount, selectedRows }) => { console.log(\`\${selectedCount} rows selected\`, selectedRows); }} -/>`} /> +/>;`} />

        Clearing selection (imperative API)

        @@ -197,21 +192,21 @@ tableRef.current?.clearSelectedRows();`} /> Add selectableRowsHighlight to apply the theme's selected-row background to checked rows, giving an obvious visual confirmation of selection state.

        - `} /> + ;`} />

        Hide "select all" checkbox

        Pass selectableRowsNoSelectAll to remove the header checkbox entirely. Useful when you want per-row selection without a bulk-select affordance.

        - `} /> + ;`} />

        Select only visible rows

        When pagination is enabled, selectableRowsVisibleOnly makes the "select all" checkbox operate only on the current page rather than the full dataset.

        - `} /> + ;`} />

        Custom checkbox component

        @@ -220,11 +215,7 @@ tableRef.current?.clearSelectedRows();`} />

        `} /> +;`} />

        A prop value can also be a function — it is called with the checkbox's indeterminate state (true for the header checkbox when only some rows are selected) and its return @@ -237,7 +228,7 @@ tableRef.current?.clearSelectedRows();`} /> selectableRowsComponentProps={{ indeterminate: (isIndeterminate: boolean) => isIndeterminate, }} -/>`} /> +/>;`} />

        Prop reference

        diff --git a/apps/docs/src/pages/docs/sorting.astro b/apps/docs/src/pages/docs/sorting.astro index c632c9d9..90daf76b 100644 --- a/apps/docs/src/pages/docs/sorting.astro +++ b/apps/docs/src/pages/docs/sorting.astro @@ -32,12 +32,18 @@ import MultiSortDemo from '../../components/demos/MultiSortDemo.tsx'; code={`import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component'; const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, - { id: 'dept', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, - format: r => \`$\${r.salary.toLocaleString()}\`, right: true }, - { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true }, - { id: 'id', name: 'ID', selector: r => r.id }, // not sortable + { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, + { id: 'dept', name: 'Department', selector: r => r.department, sortable: true }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + sortable: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + right: true, + }, + { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true }, + { id: 'id', name: 'ID', selector: r => r.id }, // not sortable ]; [] = [ defaultSortAsc={true} onSort={(column, direction) => console.log(column.id, direction)} highlightOnHover -/>`} +/>;`} > @@ -89,10 +95,10 @@ const columns: TableColumn[] = [ import DataTable, { type TableColumn, type SortColumn } from 'react-data-table-component'; const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, right: true }, - { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true }, + { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, right: true }, + { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true }, ]; export default function App() { @@ -127,12 +133,7 @@ export default function App() {

        `} /> +;`} />

        Listening to sort events

        @@ -162,7 +163,7 @@ export default function App() { // sortColumns holds every active sort column in priority order: // [{ column, sortDirection }, ...] }} -/>`} /> +/>;`} />

        Custom sort function: per column

        @@ -208,7 +209,7 @@ const customSort: SortFunction = (rows, selector, direction) => { }); }; -`} /> +;`} />

        Priority ordering: sort certain rows first

        @@ -231,7 +232,7 @@ const columns: TableColumn[] = [ { id: 'status', name: 'Status', - selector: r => STATUS_ORDER[r.status], // numeric selector drives the sort key + selector: r => STATUS_ORDER[r.status], // numeric selector drives the sort key cell: r => , sortable: true, sortFunction: (a, b) => STATUS_ORDER[a.status] - STATUS_ORDER[b.status], @@ -243,9 +244,10 @@ const columns: TableColumn[] = [ // tiebreaker. Wire this as the table-level sortFunction. const withSecondarySalary: SortFunction = (rows, selector, direction) => [...rows].sort((a, b) => { - const av = selector(a), bv = selector(b); + const av = selector(a), + bv = selector(b); const primary = av === bv ? 0 : (av > bv ? 1 : -1) * (direction === SortOrder.ASC ? 1 : -1); - return primary !== 0 ? primary : b.salary - a.salary; // salary desc as tiebreaker + return primary !== 0 ? primary : b.salary - a.salary; // salary desc as tiebreaker });`} > @@ -271,7 +273,7 @@ const withSecondarySalary: SortFunction = (rows, selector, direction) return direction === SortOrder.ASC ? cmp : -cmp; }); -`} /> +;`} />

        Custom sort icon

        @@ -291,7 +293,7 @@ const SortIcon = () => ( // Flip the icon for descending with CSS: // .rdt_sortIcon:not(.rdt_sortIconAsc) svg { transform: rotate(180deg); } -} />`} /> +} />;`} />

        Server-side sorting

        @@ -338,7 +340,7 @@ const SortIcon = () => ( load({ orderBy }); // e.g. ORDER BY department ASC, salary DESC } -`} /> +;`} /> ( import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component'; const columns: TableColumn[] = [ - { id: 'name', name: 'Name', selector: r => r.name, sortable: true, sortField: 'name' }, + { id: 'name', name: 'Name', selector: r => r.name, sortable: true, sortField: 'name' }, { id: 'department', name: 'Department', selector: r => r.department, sortable: true, sortField: 'department' }, - { id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, sortField: 'salary', - right: true, format: r => \`$\${r.salary.toLocaleString()}\` }, - { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true, sortField: 'hired' }, + { + id: 'salary', + name: 'Salary', + selector: r => r.salary, + sortable: true, + sortField: 'salary', + right: true, + format: r => \`$\${r.salary.toLocaleString()}\`, + }, + { id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true, sortField: 'hired' }, ]; export default function App() { - const [data, setData] = useState(initialData); + const [data, setData] = useState(initialData); const [loading, setLoading] = useState(false); async function handleSort(col: TableColumn, dir: SortOrder) { @@ -398,16 +407,16 @@ export default function App() { import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component'; export default function App() { - const [data, setData] = useState([]); - const [loading, setLoading] = useState(true); - const [totalRows, setTotal] = useState(0); - const [page, setPage] = useState(1); - const [perPage, setPerPage] = useState(10); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [totalRows, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); const [sortField, setSortField] = useState('name'); - const [sortDir, setSortDir] = useState(SortOrder.ASC); + const [sortDir, setSortDir] = useState(SortOrder.ASC); const [resetPage, setResetPage] = useState(false); - const load = useCallback(async (params) => { + const load = useCallback(async params => { setLoading(true); const result = await fetchFromServer(params); setData(result.rows); @@ -415,12 +424,16 @@ export default function App() { setLoading(false); }, []); - useEffect(() => { load({ page, perPage, sortField, sortDir }); }, []); + useEffect(() => { + load({ page, perPage, sortField, sortDir }); + }, []); function handleSort(col: TableColumn, dir: SortOrder) { const field = col.sortField ?? String(col.id); - setSortField(field); setSortDir(dir); - setPage(1); setResetPage(prev => !prev); // reset pagination to page 1 on sort + setSortField(field); + setSortDir(dir); + setPage(1); + setResetPage(prev => !prev); // reset pagination to page 1 on sort load({ page: 1, perPage, sortField: field, sortDir: dir }); } @@ -435,8 +448,14 @@ export default function App() { paginationServer paginationTotalRows={totalRows} paginationResetDefaultPage={resetPage} - onChangePage={p => { setPage(p); load({ page: p, perPage, sortField, sortDir }); }} - onChangeRowsPerPage={(pp, p) => { setPerPage(pp); load({ page: p, perPage: pp, sortField, sortDir }); }} + onChangePage={p => { + setPage(p); + load({ page: p, perPage, sortField, sortDir }); + }} + onChangeRowsPerPage={(pp, p) => { + setPerPage(pp); + load({ page: p, perPage: pp, sortField, sortDir }); + }} highlightOnHover /> ); @@ -465,13 +484,7 @@ function App() { return ( <> - + ); }`} diff --git a/apps/docs/src/pages/docs/ssr.astro b/apps/docs/src/pages/docs/ssr.astro index 41437f0c..de42cc15 100644 --- a/apps/docs/src/pages/docs/ssr.astro +++ b/apps/docs/src/pages/docs/ssr.astro @@ -38,7 +38,7 @@ import DataTable from 'react-data-table-component'; import { getUsers } from '@/lib/db'; const columns = [ - { name: 'Name', selector: (r: User) => r.name }, + { name: 'Name', selector: (r: User) => r.name }, { name: 'Email', selector: (r: User) => r.email }, ]; @@ -72,10 +72,14 @@ export default function RootLayout({ children }: { children: React.ReactNode }) [] = [ - { name: 'Name', selector: r => r.name }, + { name: 'Name', selector: r => r.name }, { name: 'Email', selector: r => r.email }, ]; @@ -100,10 +104,14 @@ import DataTable, { type TableColumn } from 'react-data-table-component'; import { json } from '@remix-run/node'; import { useLoaderData } from '@remix-run/react'; -interface User { id: number; name: string; email: string; } +interface User { + id: number; + name: string; + email: string; +} const columns: TableColumn[] = [ - { name: 'Name', selector: r => r.name }, + { name: 'Name', selector: r => r.name }, { name: 'Email', selector: r => r.email }, ]; diff --git a/apps/docs/src/pages/docs/themes.astro b/apps/docs/src/pages/docs/themes.astro index 8e1d982f..fc6bde66 100644 --- a/apps/docs/src/pages/docs/themes.astro +++ b/apps/docs/src/pages/docs/themes.astro @@ -41,7 +41,7 @@ import PropsTable from '../../components/PropsTable.astro'; `} lang="tsx" /> +;`} lang="tsx" /> `} lang="ts" /> +;`} lang="ts" />

        Theme shape

        @@ -137,33 +137,33 @@ createTheme('violet', { }, background: { default: '#f5f0ff', - header: '#ede7f6', // distinct header background + header: '#ede7f6', // distinct header background }, - divider: { default: '#d1c4e9' }, - selected: { default: '#ede7f6', text: '#1a1a2e' }, + divider: { default: '#d1c4e9' }, + selected: { default: '#ede7f6', text: '#1a1a2e' }, highlightOnHover: { default: '#ede7f6', text: '#1a1a2e' }, - striped: { default: '#f3eeff', text: '#1a1a2e' }, + striped: { default: '#f3eeff', text: '#1a1a2e' }, button: { default: '#6200EE', - focus: 'rgba(98,0,238,0.12)', - hover: 'rgba(98,0,238,0.08)', + focus: 'rgba(98,0,238,0.12)', + hover: 'rgba(98,0,238,0.08)', disabled: '#d1c4e9', }, context: { background: '#6200EE', text: '#ffffff' }, // Dark-mode overrides — only what changes darkMode: { primary: '#BB86FC', - text: { primary: '#e0e0e0', secondary: '#b0b0b0' }, + text: { primary: '#e0e0e0', secondary: '#b0b0b0' }, background: { default: '#1a0533', header: '#240844' }, - divider: { default: '#3d1f6e' }, - selected: { default: '#2d1060', text: '#e0e0e0' }, + divider: { default: '#3d1f6e' }, + selected: { default: '#2d1060', text: '#e0e0e0' }, highlightOnHover: { default: '#2d1060', text: '#e0e0e0' }, - striped: { default: '#1e0a40', text: '#e0e0e0' }, + striped: { default: '#1e0a40', text: '#e0e0e0' }, }, // Structural - spacing: { rowHeight: '48px', headerHeight: '56px', cellPaddingX: '16px' }, + spacing: { rowHeight: '48px', headerHeight: '56px', cellPaddingX: '16px' }, typography: { fontSize: '14px', fontSizeHeader: '12px' }, - shape: { borderRadius: '8px' }, + shape: { borderRadius: '8px' }, });`} lang="ts" />

        Icons

        @@ -229,14 +229,14 @@ createTheme('icon-theme', { // expanded: omitted — falls back to the built-in default }, pagination: { - next: , + next: , previous: , // first/last: omitted — fall back to defaults }, }, }); -`} lang="tsx" /> +;`} lang="tsx" />

        Custom checkbox component

        @@ -248,11 +248,7 @@ createTheme('icon-theme', { `} lang="tsx" /> +;`} lang="tsx" />

        If you build your own checkbox, use React.forwardRef and forward the ref to the @@ -262,22 +258,12 @@ createTheme('icon-theme', { ->((props, ref) => ( - +const MyCheckbox = React.forwardRef>((props, ref) => ( + )); MyCheckbox.displayName = 'MyCheckbox'; -`} lang="tsx" /> +;`} lang="tsx" />

        Header & column separators in themes

        @@ -286,14 +272,18 @@ MyCheckbox.displayName = 'MyCheckbox'; takes precedence when explicitly passed.

        - `} lang="tsx" /> +;`} lang="tsx" />

        CSS variable reference

        diff --git a/apps/docs/src/pages/docs/typescript.astro b/apps/docs/src/pages/docs/typescript.astro index 6644b1f1..6ca1b94f 100644 --- a/apps/docs/src/pages/docs/typescript.astro +++ b/apps/docs/src/pages/docs/typescript.astro @@ -30,13 +30,12 @@ interface Employee { } const columns: TableColumn[] = [ - { name: 'Name', selector: row => row.name }, // row: Employee - { name: 'Salary', selector: row => row.salary, - format: (row, idx) => \`$\${row.salary.toLocaleString()}\` }, - { name: 'Status', selector: row => row.status, // typed as the union, not string - conditionalCellStyles: [ - { when: r => r.status === 'Terminated', style: { color: 'red' } }, - ], + { name: 'Name', selector: row => row.name }, // row: Employee + { name: 'Salary', selector: row => row.salary, format: (row, idx) => \`$\${row.salary.toLocaleString()}\` }, + { + name: 'Status', + selector: row => row.status, // typed as the union, not string + conditionalCellStyles: [{ when: r => r.status === 'Terminated', style: { color: 'red' } }], }, ];`} /> @@ -60,9 +59,7 @@ export default function App() { return ( <> - + ); @@ -83,7 +80,7 @@ function handleSort(column: TableColumn, direction: SortOrder) { } } -`} /> +;`} />

        Conditional row styles

        @@ -94,7 +91,7 @@ const conditionalRowStyles: ConditionalStyles[] = [ { when: row => row.status === 'Terminated', style: { opacity: 0.5 } }, ]; -`} /> +;`} />

        Custom expander component

        @@ -108,7 +105,7 @@ function EmployeeDetail({ data: row }: ExpanderComponentProps) { ); } -`} /> +;`} />

        Filter values

        @@ -127,7 +124,7 @@ const [filters, setFilters] = useState>({}); data={data} filterValues={filters} onFilterChange={(columnId, next) => setFilters(prev => ({ ...prev, [columnId]: next }))} -/>`} /> +/>;`} />

        Headless hooks

        @@ -153,7 +150,7 @@ import type { UseColumnVisibilityResult } from 'react-data-table-component';`} / onRowClicked={(row, e) => { // row is typed as Employee }} -/>`} /> +/>;`} />

        Common type-related issues

        @@ -164,11 +161,11 @@ import type { UseColumnVisibilityResult } from 'react-data-table-component';`} /

        row.tags } // row.tags: Tag[] +{ name: 'Tags', selector: row => row.tags } // row.tags: Tag[] // ✅ Either format or render { name: 'Tags', selector: row => row.tags.join(', ') } -{ name: 'Tags', cell: row => }`} /> +{ name: 'Tags', cell: row => }`} />

        Column id type

        @@ -178,7 +175,7 @@ import type { UseColumnVisibilityResult } from 'react-data-table-component';`} / ) => { const field = column.id as keyof Employee; // narrow string | number → known field - setData(prev => prev.map(r => r.id === row.id ? { ...r, [field]: value } : r)); + setData(prev => prev.map(r => (r.id === row.id ? { ...r, [field]: value } : r))); };`} />

        Imported types — full list

        diff --git a/package-lock.json b/package-lock.json index 4e14f0c8..b0674016 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-data-table-component", - "version": "8.5.2", + "version": "8.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-data-table-component", - "version": "8.5.2", + "version": "8.6.2", "funding": [ { "type": "github", @@ -2225,17 +2225,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz", - "integrity": "sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.63.0", - "@typescript-eslint/type-utils": "8.63.0", - "@typescript-eslint/utils": "8.63.0", - "@typescript-eslint/visitor-keys": "8.63.0", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2248,23 +2248,23 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.63.0", + "@typescript-eslint/parser": "^8.64.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz", - "integrity": "sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==", + "node_modules/@typescript-eslint/parser": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/typescript-estree": "8.63.0", - "@typescript-eslint/utils": "8.63.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2278,22 +2278,16 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz", - "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.63.0", - "@typescript-eslint/tsconfig-utils": "8.63.0", - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/visitor-keys": "8.63.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2306,16 +2300,15 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz", - "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.63.0", - "@typescript-eslint/types": "^8.63.0", - "debug": "^4.4.3" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2323,15 +2316,12 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz", - "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { @@ -2345,45 +2335,17 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.63.0.tgz", - "integrity": "sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.63.0", - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/typescript-estree": "8.63.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz", - "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.63.0", - "@typescript-eslint/tsconfig-utils": "8.63.0", - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/visitor-keys": "8.63.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "engines": { @@ -2394,84 +2356,35 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz", - "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.63.0", - "@typescript-eslint/types": "^8.63.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz", - "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.63.0.tgz", - "integrity": "sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==", + "node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.63.0", - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/typescript-estree": "8.63.0", - "@typescript-eslint/visitor-keys": "8.63.0", - "debug": "^4.4.3" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz", - "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.63.0", - "@typescript-eslint/tsconfig-utils": "8.63.0", - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/visitor-keys": "8.63.0", + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2489,34 +2402,18 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz", - "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.63.0", - "@typescript-eslint/types": "^8.63.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz", - "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==", - "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2525,49 +2422,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz", - "integrity": "sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.63.0", - "@typescript-eslint/visitor-keys": "8.63.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.63.0.tgz", - "integrity": "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.63.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz", - "integrity": "sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/types": "8.64.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2981,9 +2847,9 @@ "license": "MIT" }, "node_modules/ast-v8-to-istanbul": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", - "integrity": "sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz", + "integrity": "sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==", "dev": true, "license": "MIT", "dependencies": { @@ -3092,9 +2958,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz", - "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==", + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", "dev": true, "funding": [ { @@ -3113,9 +2979,9 @@ "license": "MIT", "dependencies": { "baseline-browser-mapping": "^2.10.42", - "caniuse-lite": "^1.0.30001800", - "electron-to-chromium": "^1.5.387", - "node-releases": "^2.0.50", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -3212,9 +3078,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001803", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz", - "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==", + "version": "1.0.30001805", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", + "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==", "dev": true, "funding": [ { @@ -3631,9 +3497,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.389", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", - "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", + "version": "1.5.391", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.391.tgz", + "integrity": "sha512-YmCu4856jkgKT1Nh6fwRdeVrM6Ydf/fBnq51tpmSfX+jOcUMTxh31yH6hjKScRenhB2oDSvA9oooxcpjogPeig==", "dev": true, "license": "ISC" }, @@ -3794,9 +3660,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.0.tgz", - "integrity": "sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", "dev": true, "license": "MIT" }, @@ -6071,9 +5937,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { @@ -6438,9 +6304,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", + "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", "dev": true, "funding": [ {