feat(core): add column pinning and grouping features to Griddy table
- 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.
This commit is contained in:
@@ -12,49 +12,87 @@ export function getGriddyColumn<T>(column: { columnDef: ColumnDef<T> }): GriddyC
|
||||
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>[].
|
||||
* Optionally prepends a selection checkbox column.
|
||||
* Supports header grouping and optionally prepends a selection checkbox column.
|
||||
*/
|
||||
export function mapColumns<T>(
|
||||
columns: GriddyColumn<T>[],
|
||||
selection?: SelectionConfig,
|
||||
): ColumnDef<T>[] {
|
||||
const mapped: ColumnDef<T>[] = columns.map((col) => {
|
||||
const isStringAccessor = typeof col.accessor !== 'function'
|
||||
// Group columns by headerGroup
|
||||
const grouped = new Map<string, GriddyColumn<T>[]>()
|
||||
const ungrouped: GriddyColumn<T>[] = []
|
||||
|
||||
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 }),
|
||||
enableColumnFilter: col.filterable ?? false,
|
||||
enableHiding: 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,
|
||||
columns.forEach(col => {
|
||||
if (col.headerGroup) {
|
||||
const existing = grouped.get(col.headerGroup) || []
|
||||
existing.push(col)
|
||||
grouped.set(col.headerGroup, existing)
|
||||
} else {
|
||||
ungrouped.push(col)
|
||||
}
|
||||
})
|
||||
|
||||
// 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'
|
||||
}
|
||||
// Build column definitions
|
||||
const mapped: ColumnDef<T>[] = []
|
||||
|
||||
if (col.filterFn) {
|
||||
def.filterFn = col.filterFn
|
||||
} else if (col.filterable) {
|
||||
def.filterFn = createOperatorFilter()
|
||||
// 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)),
|
||||
}
|
||||
return def
|
||||
mapped.push(groupDef)
|
||||
})
|
||||
|
||||
// Prepend checkbox column if selection is enabled
|
||||
|
||||
Reference in New Issue
Block a user