
Row selection, keyboard navigation, context menus, column visibility, dual resize modes, and 6 exported hooks — one release, zero compromises.
Introduction
Picture the macOS Finder: you click a file, Shift+click to select a range, right-click for a context menu, double-click a column divider to auto-fit, and hide columns you don't need. Now picture doing all of that in a React table component — with full Mantine integration and zero external dependencies. That's what @gfazioli/mantine-list-view-table 2.0.0 delivers. This isn't an incremental update; it's a rewrite that transforms a sortable table into a complete Finder-style list view, with every piece of logic extracted into reusable, publicly exported hooks.
What's New
✨ Row Selection
Select rows exactly like macOS Finder. Single click selects one row, Cmd/Ctrl+Click toggles, Shift+Click selects a range. Both single and multiple modes are supported, with controlled and uncontrolled state.
<ListViewTablecolumns={columns}data={data}rowKey="id"selectionMode="multiple"onSelectionChange={(keys, records) => setSelected(keys)}selectedRowColor="blue"/>Enter fullscreen mode Exit fullscreen mode
Selected rows get a visible highlight that desaturates when the component loses focus — matching Finder's behavior pixel for pixel.
⌨️ Keyboard Navigation
When selectionMode is set, keyboard navigation activates automatically:
Key | Action |
|---|---|
Arrow Up/Down | Move focus between rows |
Enter | Activate row ( |
Space | Toggle selection |
Home / End | Jump to first / last row |
Cmd/Ctrl+A | Select all (multiple mode) |
<ListViewTableselectionMode="multiple"enableKeyboardNavigationonRowActivate={(record) => openFile(record)}/>Enter fullscreen mode Exit fullscreen mode
🖱️ Context Menu
Right-click any row to show a context menu powered by Mantine's Menu component. The clicked row is automatically selected before the menu appears. You get Mantine's full accessibility, keyboard support, and dark mode out of the box.
<ListViewTableselectionMode="single"renderContextMenu={({ record }) => (
<>
<Menu.Item leftSection={<IconCopy size={14} />}>Copy</Menu.Item>
<Menu.Item leftSection={<IconDownload size={14} />}>Download</Menu.Item>
<Menu.Divider />
<Menu.Item color="red" leftSection={<IconTrash size={14} />}>Delete</Menu.Item>
</>)}/>Enter fullscreen mode Exit fullscreen mode
👁️ Column Visibility
Hide and show columns programmatically or let users toggle them by right-clicking the table header. Supports both controlled and uncontrolled modes.
<ListViewTablehiddenColumns={['size', 'modified']}onHiddenColumnsChange={setHiddenColumns}enableColumnVisibilityToggle/>Enter fullscreen mode Exit fullscreen mode
↔️ Dual Resize Modes
The new resizeMode prop gives you two column resize behaviors:
standard(default) — width is traded between the dragged column and its right neighbor. Total table width stays fixed. Great for fixed-width layouts.finder— only the dragged column changes width. The table grows freely, just like Finder. Pair withTable.ScrollContainerfor horizontal scrolling.
<ListViewTable enableColumnResizing resizeMode="finder" />Enter fullscreen mode Exit fullscreen mode
🎯 Double-Click Auto-Fit
Double-click any resize handle to auto-fit the column to its content — measured accurately using off-screen DOM clones. In standard mode, the adjacent column compensates to keep the total width constant.
🪝 6 Exported Hooks
Every piece of internal logic is now a standalone, reusable hook:
Hook | Purpose |
|---|---|
| Sort state with controlled/uncontrolled modes |
| Drag-and-drop column reordering |
| Column resize with standard/finder modes |
| Row selection (single, multiple, range) |
| Arrow/Enter/Space keyboard navigation |
| Column show/hide management |
Import them directly for advanced compositions:
import { useRowSelection, useKeyboardNavigation } from '@gfazioli/mantine-list-view-table';Enter fullscreen mode Exit fullscreen mode
📋 New Props Summary
Prop | Type | Default | Description |
|---|---|---|---|
| `'single' \ | 'multiple'` | — |
|
| — | Controlled selected row keys |
|
| — | Default selected keys (uncontrolled) |
|
| — | Selection change callback |
|
| — | Selected row background color |
|
|
| Enable keyboard nav |
|
| — | Enter key callback |
|
| — | Context menu content (use |
|
| — | Right-click callback |
|
| — | Controlled hidden column keys |
|
| — | Default hidden columns (uncontrolled) |
|
| — | Hidden columns change callback |
|
|
| Right-click header to toggle columns |
| `'standard' \ | 'finder'` |
|
|
| — | Props passed to inner |
|
|
| Sticky table header |
| `number \ | string` |
|
|
|
| Tabular number alignment |
🎨 New Styles API
New selectors: selectedRow, focusedRow
New CSS variables:
Variable | Description |
|---|---|
| Background color of selected rows |
| Backdrop blur for sticky column overlay |
💥 Breaking Changes
[!CAUTION]
This release contains breaking changes. Review the migration guide below before upgrading.
1. Props interface no longer extends TableProps
Table-level props (variant, layout, etc.) must now be passed through tableProps:
// Before<ListViewTable variant="vertical" />
// After<ListViewTable tableProps={{ variant: 'vertical' }} />Enter fullscreen mode Exit fullscreen mode
2. enableColumnReordering and enableColumnResizing default to false
Previously both defaulted to true. Now you must opt in:
<ListViewTable enableColumnReordering enableColumnResizing />Enter fullscreen mode Exit fullscreen mode
3. Component ref type changed
// Beforeconst ref = useRef<HTMLTableElement>(null);
// Afterconst ref = useRef<HTMLDivElement>(null);Enter fullscreen mode Exit fullscreen mode
4. Context menu requires Menu.Item elements
The renderContextMenu prop must now return Mantine Menu.Item / Menu.Divider components instead of arbitrary JSX:
// BeforerenderContextMenu={() => (<Stack gap={0}>
<UnstyledButton>Copy</UnstyledButton></Stack>)}
// AfterrenderContextMenu={() => (<>
<Menu.Item leftSection={<IconCopy size={14} />}>Copy</Menu.Item></>)}Enter fullscreen mode Exit fullscreen mode
5. visibleMediaQuery removed from column config
Use the new hiddenColumns prop instead for programmatic column visibility.
6. contextMenu Styles API selector removed
The context menu is now powered by Mantine's Menu, which has its own styling system.
🐛 Bug Fixes
Double-click no longer triggers drag resize —
event.detail >= 2guard prevents mousedown from starting a drag during double-clickAuto-fit preserves table width — adjacent column compensates the exact delta in standard mode
Controlled sort respects external data order — no internal re-sorting when
sortStatus+onSortare both providedKeyboard navigation bounds check — Enter/Space no longer fire with invalid indices after data filtering
Range selection clamped — Shift+click clamps to
[0, data.length - 1]when data changes between clicks
🔧 Improvements
Resize handle UX overhaul — 14px hit area (20px touch), animated indicator line with spring curve
Finder-style header focus — inset bottom border instead of background highlight
Accessible focus indicator —
:focus-visibleoutline on root element for keyboard usersSticky columns without
!important— cleaner CSS specificityVertical variant — now supports dot-notation keys (
getNestedValue) andrenderCell
Migration Guide
Step 1: Install the update
yarn add @gfazioli/mantine-list-view-table@^2.0.0
Enter fullscreen mode Exit fullscreen mode
Step 2: Wrap Table props in tableProps
If you passed Mantine Table props directly, move them:
// Before<ListViewTable variant="vertical" layout="fixed" />
// After<ListViewTable tableProps={{ variant: 'vertical', layout: 'fixed' }} />Enter fullscreen mode Exit fullscreen mode
Step 3: Opt in to reordering/resizing
<ListViewTable enableColumnReordering enableColumnResizing />Enter fullscreen mode Exit fullscreen mode
Step 4: Update ref type
const ref = useRef<HTMLDivElement>(null); // was HTMLTableElementEnter fullscreen mode Exit fullscreen mode
Step 5: Update context menu
Replace custom JSX with Menu.Item from @mantine/core:
import { Menu } from '@mantine/core';
renderContextMenu={({ record }) => (<>
<Menu.Item leftSection={<IconCopy size={14} />}>Copy</Menu.Item>
<Menu.Divider />
<Menu.Item color="red" leftSection={<IconTrash size={14} />}>Delete</Menu.Item></>)}Enter fullscreen mode Exit fullscreen mode
Step 6: Replace visibleMediaQuery
// Before (on column){ key: 'size', visibleMediaQuery: '(min-width: 768px)' }
// After (on component)<ListViewTable hiddenColumns={isMobile ? ['size'] : []} />Enter fullscreen mode Exit fullscreen mode
Compatibility
Mantine: 8.x
React: 18.x / 19.x
@tabler/icons-react: ^3.34.0
License: MIT
Comments (0)
Login to post a comment.