- Implement column pinning functionality allowing users to pin columns to the left or right. - Introduce data grouping capabilities for better data organization. - Enhance the theming guide with new styles for pinned columns and loading indicators. - Add infinite scroll support with loading indicators for improved user experience. - Update CSS styles to accommodate new features and improve visual feedback.
117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
import type { ColumnDef } from '@tanstack/react-table'
|
|
|
|
import type { GriddyColumn, SelectionConfig } from './types'
|
|
|
|
import { createOperatorFilter } from '../features/filtering'
|
|
import { DEFAULTS, SELECTION_COLUMN_ID, SELECTION_COLUMN_SIZE } from './constants'
|
|
|
|
/**
|
|
* Retrieves the original GriddyColumn from a TanStack column's meta.
|
|
*/
|
|
export function getGriddyColumn<T>(column: { columnDef: ColumnDef<T> }): GriddyColumn<T> | undefined {
|
|
return (column.columnDef.meta as { griddy?: GriddyColumn<T> })?.griddy
|
|
}
|
|
|
|
/**
|
|
* Converts a single GriddyColumn to a TanStack ColumnDef
|
|
*/
|
|
function mapSingleColumn<T>(col: GriddyColumn<T>): ColumnDef<T> {
|
|
const isStringAccessor = typeof col.accessor !== 'function'
|
|
|
|
const def: ColumnDef<T> = {
|
|
id: col.id,
|
|
// Use accessorKey for string keys (enables TanStack auto-detection of sort/filter),
|
|
// accessorFn for function accessors
|
|
...(isStringAccessor
|
|
? { accessorKey: col.accessor as string }
|
|
: { accessorFn: col.accessor as (row: T) => unknown }),
|
|
aggregationFn: col.aggregationFn,
|
|
enableColumnFilter: col.filterable ?? false,
|
|
enableGrouping: col.groupable ?? false,
|
|
enableHiding: true,
|
|
enablePinning: true,
|
|
enableResizing: true,
|
|
enableSorting: col.sortable ?? true,
|
|
header: () => col.header,
|
|
maxSize: col.maxWidth ?? DEFAULTS.maxColumnWidth,
|
|
meta: { griddy: col },
|
|
minSize: col.minWidth ?? DEFAULTS.minColumnWidth,
|
|
size: col.width,
|
|
}
|
|
|
|
// For function accessors, TanStack can't auto-detect the sort type, so provide a default
|
|
if (col.sortFn) {
|
|
def.sortingFn = col.sortFn
|
|
} else if (!isStringAccessor && col.sortable !== false) {
|
|
// Use alphanumeric sorting for function accessors
|
|
def.sortingFn = 'alphanumeric'
|
|
}
|
|
|
|
if (col.filterFn) {
|
|
def.filterFn = col.filterFn
|
|
} else if (col.filterable) {
|
|
def.filterFn = createOperatorFilter()
|
|
}
|
|
return def
|
|
}
|
|
|
|
/**
|
|
* Maps Griddy's user-facing GriddyColumn<T> definitions to TanStack Table ColumnDef<T>[].
|
|
* Supports header grouping and optionally prepends a selection checkbox column.
|
|
*/
|
|
export function mapColumns<T>(
|
|
columns: GriddyColumn<T>[],
|
|
selection?: SelectionConfig,
|
|
): ColumnDef<T>[] {
|
|
// Group columns by headerGroup
|
|
const grouped = new Map<string, GriddyColumn<T>[]>()
|
|
const ungrouped: GriddyColumn<T>[] = []
|
|
|
|
columns.forEach(col => {
|
|
if (col.headerGroup) {
|
|
const existing = grouped.get(col.headerGroup) || []
|
|
existing.push(col)
|
|
grouped.set(col.headerGroup, existing)
|
|
} else {
|
|
ungrouped.push(col)
|
|
}
|
|
})
|
|
|
|
// Build column definitions
|
|
const mapped: ColumnDef<T>[] = []
|
|
|
|
// Add ungrouped columns first
|
|
ungrouped.forEach(col => {
|
|
mapped.push(mapSingleColumn(col))
|
|
})
|
|
|
|
// Add grouped columns
|
|
grouped.forEach((groupColumns, groupName) => {
|
|
const groupDef: ColumnDef<T> = {
|
|
header: groupName,
|
|
id: `group-${groupName}`,
|
|
columns: groupColumns.map(col => mapSingleColumn(col)),
|
|
}
|
|
mapped.push(groupDef)
|
|
})
|
|
|
|
// Prepend checkbox column if selection is enabled
|
|
if (selection && selection.mode !== 'none' && selection.showCheckbox !== false) {
|
|
const checkboxCol: ColumnDef<T> = {
|
|
cell: 'select-row', // Rendered by TableCell with actual checkbox
|
|
enableColumnFilter: false,
|
|
enableHiding: false,
|
|
enableResizing: false,
|
|
enableSorting: false,
|
|
header: selection.mode === 'multi'
|
|
? 'select-all' // Rendered by TableHeader with actual checkbox
|
|
: '',
|
|
id: SELECTION_COLUMN_ID,
|
|
size: SELECTION_COLUMN_SIZE,
|
|
}
|
|
mapped.unshift(checkboxCol)
|
|
}
|
|
|
|
return mapped
|
|
}
|