# Griddy A powerful, keyboard-first data grid component built on **TanStack Table** and **TanStack Virtual** with full TypeScript support. ## Features ✨ **Core Features** - 🎹 **Keyboard-first navigation** - Arrow keys, Page Up/Down, Home/End, Ctrl+F - 🚀 **Virtual scrolling** - Handle 10,000+ rows smoothly - 📝 **Inline editing** - 5 built-in editors (text, number, date, select, checkbox) - 🔍 **Search** - Ctrl+F overlay with highlighting - 🎯 **Row selection** - Single and multi-select modes with keyboard support - 📊 **Sorting** - Single and multi-column sorting - 🔎 **Filtering** - Text, number, date, enum, boolean filters with operators - 📄 **Pagination** - Client-side and server-side pagination - 💾 **CSV Export** - Export filtered data to CSV - 👁️ **Column visibility** - Show/hide columns dynamically 🎨 **Advanced Features** - Server-side filtering/sorting/pagination - Customizable cell renderers - Custom editors - Theme system with CSS variables - Fully accessible (ARIA compliant) ## Installation ```bash pnpm add @warkypublic/oranguru @tanstack/react-table @tanstack/react-virtual @mantine/core @mantine/dates ``` ## Quick Start ```typescript import { Griddy } from '@warkypublic/oranguru' import type { GriddyColumn } from '@warkypublic/oranguru' interface Person { id: number name: string age: number email: string } const columns: GriddyColumn[] = [ { id: 'id', accessor: 'id', header: 'ID', width: 60 }, { id: 'name', accessor: 'name', header: 'Name', width: 150, sortable: true }, { id: 'age', accessor: 'age', header: 'Age', width: 80, sortable: true }, { id: 'email', accessor: 'email', header: 'Email', width: 250 }, ] const data: Person[] = [ { id: 1, name: 'Alice', age: 28, email: 'alice@example.com' }, { id: 2, name: 'Bob', age: 32, email: 'bob@example.com' }, ] function MyGrid() { return ( String(row.id)} /> ) } ``` ## API Reference ### GriddyProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `columns` | `GriddyColumn[]` | **required** | Column definitions | | `data` | `T[]` | **required** | Data array | | `height` | `number \| string` | `'100%'` | Container height | | `getRowId` | `(row: T, index: number) => string` | `(_, i) => String(i)` | Row ID function | | `rowHeight` | `number` | `36` | Row height in pixels | | `overscan` | `number` | `10` | Overscan row count | | `keyboardNavigation` | `boolean` | `true` | Enable keyboard shortcuts | | `selection` | `SelectionConfig` | - | Row selection config | | `search` | `SearchConfig` | - | Search config | | `pagination` | `PaginationConfig` | - | Pagination config | | `showToolbar` | `boolean` | `false` | Show toolbar (export + column visibility) | | `exportFilename` | `string` | `'export.csv'` | CSV export filename | | `manualSorting` | `boolean` | `false` | Server-side sorting | | `manualFiltering` | `boolean` | `false` | Server-side filtering | | `dataCount` | `number` | - | Total row count (for server-side pagination) | ### Column Definition ```typescript interface GriddyColumn { id: string accessor: keyof T | ((row: T) => any) header: string | ReactNode width?: number minWidth?: number maxWidth?: number sortable?: boolean filterable?: boolean filterConfig?: FilterConfig editable?: boolean editorConfig?: EditorConfig renderer?: CellRenderer hidden?: boolean pinned?: 'left' | 'right' } ``` ### Keyboard Shortcuts | Key | Action | |-----|--------| | `Arrow Up/Down` | Move focus between rows | | `Page Up/Down` | Jump by visible page size | | `Home / End` | Jump to first/last row | | `Space` | Toggle row selection | | `Shift + Arrow` | Extend selection (multi-select) | | `Ctrl + A` | Select all rows | | `Ctrl + F` | Open search overlay | | `Ctrl + E` / `Enter` | Start editing | | `Escape` | Cancel edit / close search / clear selection | ## Examples ### With Editing ```typescript const editableColumns: GriddyColumn[] = [ { id: 'name', accessor: 'name', header: 'Name', editable: true, editorConfig: { type: 'text' }, }, { id: 'age', accessor: 'age', header: 'Age', editable: true, editorConfig: { type: 'number', min: 0, max: 120 }, }, ] { // Update your data setData(prev => prev.map(row => row.id === rowId ? { ...row, [columnId]: value } : row )) }} /> ``` ### With Filtering ```typescript const filterableColumns: GriddyColumn[] = [ { id: 'name', accessor: 'name', header: 'Name', filterable: true, filterConfig: { type: 'text' }, }, { id: 'age', accessor: 'age', header: 'Age', filterable: true, filterConfig: { type: 'number' }, }, ] ``` ### With Pagination ```typescript ``` ### Server-Side Mode ```typescript const [serverData, setServerData] = useState([]) const [filters, setFilters] = useState([]) const [sorting, setSorting] = useState([]) useEffect(() => { // Fetch from server when filters/sorting change fetchData({ filters, sorting }).then(setServerData) }, [filters, sorting]) ``` ## Theming Griddy uses CSS variables for theming: ```css .griddy { --griddy-font-family: inherit; --griddy-font-size: 14px; --griddy-border-color: #e0e0e0; --griddy-header-bg: #f8f9fa; --griddy-header-color: #212529; --griddy-row-bg: #ffffff; --griddy-row-hover-bg: #f1f3f5; --griddy-row-even-bg: #f8f9fa; --griddy-focus-color: #228be6; --griddy-selection-bg: rgba(34, 139, 230, 0.1); } ``` Override in your CSS: ```css .my-custom-grid { --griddy-focus-color: #ff6b6b; --griddy-header-bg: #1a1b1e; --griddy-header-color: #ffffff; } ``` ## Performance - ✅ Handles **10,000+ rows** with virtual scrolling - ✅ **60 fps** scrolling performance - ✅ Optimized with React.memo and useMemo - ✅ Only visible rows rendered (TanStack Virtual) - ✅ Bundle size: ~45KB gzipped (excluding peer deps) ## Accessibility Griddy follows WAI-ARIA grid pattern: - ✅ Full keyboard navigation - ✅ ARIA roles: `grid`, `row`, `gridcell`, `columnheader` - ✅ `aria-selected` on selected rows - ✅ `aria-activedescendant` for focused row - ✅ Screen reader compatible - ✅ Focus indicators ## Browser Support - Chrome/Edge: Latest 2 versions - Firefox: Latest 2 versions - Safari: Latest 2 versions ## License MIT ## Credits Built with: - [TanStack Table](https://tanstack.com/table) - Headless table logic - [TanStack Virtual](https://tanstack.com/virtual) - Virtualization - [Mantine](https://mantine.dev/) - UI components