@gp-grid/core 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/index.d.ts +40 -2
- package/dist/index.js +43 -4154
- package/package.json +9 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["getFieldValue","setFieldValue","getFieldValue","getFieldValue","getFieldValue","compareValues","result","stringToSortableNumber","compareValues","getFieldValue","getFieldValue","getFieldValue","getFieldValue","data"],"sources":["../src/utils/positioning.ts","../src/utils/classNames.ts","../src/utils/field-accessor.ts","../src/utils/event-emitter.ts","../src/selection.ts","../src/fill.ts","../src/slot-pool.ts","../src/edit-manager.ts","../src/input-handler.ts","../src/highlight-manager.ts","../src/sort-filter-manager.ts","../src/row-mutation-manager.ts","../src/grid-core.ts","../src/sorting/worker-pool.ts","../src/sorting/sort-worker.ts","../src/sorting/k-way-merge.ts","../src/sorting/parallel-sort-manager.ts","../src/data-source/sorting.ts","../src/filtering/index.ts","../src/data-source/client-data-source.ts","../src/data-source/server-data-source.ts","../src/indexed-data-store/field-helpers.ts","../src/indexed-data-store/sorting.ts","../src/indexed-data-store/indexed-data-store.ts","../src/transaction-manager.ts","../src/data-source/mutable-data-source.ts","../src/styles/variables.ts","../src/styles/container.ts","../src/styles/header.ts","../src/styles/cells.ts","../src/styles/states.ts","../src/styles/scrollbar.ts","../src/styles/filters.ts","../src/styles/index.ts"],"sourcesContent":["// packages/core/src/utils/positioning.ts\n\nimport type { ColumnDefinition } from \"../types\";\n\n/**\n * Calculate cumulative column positions (prefix sums)\n * Returns an array where positions[i] is the left position of column i\n * positions[columns.length] is the total width\n */\nexport const calculateColumnPositions = (columns: ColumnDefinition[]): number[] => {\n const positions = [0];\n let pos = 0;\n for (const col of columns) {\n pos += col.width;\n positions.push(pos);\n }\n return positions;\n};\n\n/**\n * Get total width from column positions\n */\nexport const getTotalWidth = (columnPositions: number[]): number =>\n columnPositions[columnPositions.length - 1] ?? 0;\n\n/**\n * Calculate scaled column positions when container is wider than total column widths.\n * Columns expand proportionally based on their original width ratios.\n *\n * @param columns - Column definitions with original widths\n * @param containerWidth - Available container width\n * @returns Object with positions array and widths array\n */\nexport const calculateScaledColumnPositions = (\n columns: ColumnDefinition[],\n containerWidth: number,\n): { positions: number[]; widths: number[] } => {\n const originalPositions = calculateColumnPositions(columns);\n const totalOriginalWidth = getTotalWidth(originalPositions);\n\n // If container is not wider than content, return original values\n if (containerWidth <= totalOriginalWidth || totalOriginalWidth === 0) {\n return {\n positions: originalPositions,\n widths: columns.map((col) => col.width),\n };\n }\n\n // Calculate scale factor\n const scaleFactor = containerWidth / totalOriginalWidth;\n\n // Scale each column proportionally\n const scaledWidths = columns.map((col) => col.width * scaleFactor);\n\n // Calculate new positions\n const scaledPositions = [0];\n let pos = 0;\n for (const width of scaledWidths) {\n pos += width;\n scaledPositions.push(pos);\n }\n\n return { positions: scaledPositions, widths: scaledWidths };\n};\n\n/**\n * Find column index at a given X coordinate\n */\nexport const findColumnAtX = (x: number, columnPositions: number[]): number => {\n for (let i = 0; i < columnPositions.length - 1; i++) {\n if (x >= columnPositions[i]! && x < columnPositions[i + 1]!) {\n return i;\n }\n }\n // If beyond last column, return last column\n if (x >= columnPositions[columnPositions.length - 1]!) {\n return columnPositions.length - 2;\n }\n return 0;\n};\n","// packages/core/src/utils/classNames.ts\n\nimport type { CellPosition, CellRange } from \"../types\";\n\n// =============================================================================\n// Range Normalization\n// =============================================================================\n\n/**\n * Normalized range with guaranteed min/max values\n */\nexport interface NormalizedRange {\n minRow: number;\n maxRow: number;\n minCol: number;\n maxCol: number;\n}\n\n/**\n * Normalize a cell range to ensure min/max values are correct\n * Handles ranges where start > end\n */\nexport const normalizeRange = (range: CellRange): NormalizedRange => ({\n minRow: Math.min(range.startRow, range.endRow),\n maxRow: Math.max(range.startRow, range.endRow),\n minCol: Math.min(range.startCol, range.endCol),\n maxCol: Math.max(range.startCol, range.endCol),\n});\n\n/**\n * Check if a cell position is within a normalized range\n */\nexport const isCellInRange = (\n row: number,\n col: number,\n range: NormalizedRange,\n): boolean =>\n row >= range.minRow &&\n row <= range.maxRow &&\n col >= range.minCol &&\n col <= range.maxCol;\n\n// =============================================================================\n// Cell State Checks\n// =============================================================================\n\n/**\n * Check if a cell is within the selection range\n */\nexport const isCellSelected = (\n row: number,\n col: number,\n selectionRange: CellRange | null,\n): boolean => {\n if (!selectionRange) return false;\n const range = normalizeRange(selectionRange);\n return isCellInRange(row, col, range);\n};\n\n/**\n * Check if a cell is the active cell\n */\nexport const isCellActive = (\n row: number,\n col: number,\n activeCell: CellPosition | null,\n): boolean => activeCell?.row === row && activeCell?.col === col;\n\n/**\n * Check if a row is within the visible range (not in overscan)\n */\nexport const isRowVisible = (\n row: number,\n visibleRowRange: { start: number; end: number } | null,\n): boolean => {\n // No range or invalid range means show everything (permissive default)\n if (!visibleRowRange) return true;\n // If end is negative or less than start, the range is invalid - show everything\n if (visibleRowRange.end < 0 || visibleRowRange.start > visibleRowRange.end) return true;\n return row >= visibleRowRange.start && row <= visibleRowRange.end;\n};\n\n/**\n * Check if a cell is being edited\n */\nexport const isCellEditing = (\n row: number,\n col: number,\n editingCell: { row: number; col: number } | null,\n): boolean => editingCell?.row === row && editingCell?.col === col;\n\n/**\n * Check if a cell is in the fill preview range (vertical-only fill)\n */\nexport const isCellInFillPreview = (\n row: number,\n col: number,\n isDraggingFill: boolean,\n fillSourceRange: CellRange | null,\n fillTarget: { row: number; col: number } | null,\n): boolean => {\n if (!isDraggingFill || !fillSourceRange || !fillTarget) return false;\n\n const { minRow, maxRow, minCol, maxCol } = normalizeRange(fillSourceRange);\n\n // Determine fill direction (vertical only)\n const fillDown = fillTarget.row > maxRow;\n const fillUp = fillTarget.row < minRow;\n\n // Check if cell is in the fill preview area (not the source area)\n if (fillDown) {\n return row > maxRow && row <= fillTarget.row && col >= minCol && col <= maxCol;\n }\n if (fillUp) {\n return row < minRow && row >= fillTarget.row && col >= minCol && col <= maxCol;\n }\n\n return false;\n};\n\n/**\n * Build cell CSS classes based on state\n */\nexport const buildCellClasses = (\n isActive: boolean,\n isSelected: boolean,\n isEditing: boolean,\n inFillPreview: boolean,\n): string => {\n const classes = [\"gp-grid-cell\"];\n\n if (isActive) {\n classes.push(\"gp-grid-cell--active\");\n }\n if (isSelected && !isActive) {\n classes.push(\"gp-grid-cell--selected\");\n }\n if (isEditing) {\n classes.push(\"gp-grid-cell--editing\");\n }\n if (inFillPreview) {\n classes.push(\"gp-grid-cell--fill-preview\");\n }\n\n return classes.join(\" \");\n};\n\n// =============================================================================\n// Highlighting Helpers\n// =============================================================================\n\n/**\n * Check if a row overlaps the selection range\n */\nexport const isRowInSelectionRange = (\n rowIndex: number,\n range: CellRange | null,\n): boolean => {\n if (!range) return false;\n const { minRow, maxRow } = normalizeRange(range);\n return rowIndex >= minRow && rowIndex <= maxRow;\n};\n\n/**\n * Check if a column overlaps the selection range\n */\nexport const isColumnInSelectionRange = (\n colIndex: number,\n range: CellRange | null,\n): boolean => {\n if (!range) return false;\n const { minCol, maxCol } = normalizeRange(range);\n return colIndex >= minCol && colIndex <= maxCol;\n};\n","// packages/core/src/utils/field-accessor.ts\n\nimport type { CellValue } from \"../types\";\n\n/**\n * Get a nested value from an object using dot notation path\n * @param data - The object to read from\n * @param field - Dot-separated path (e.g., \"address.city\")\n * @returns The value at the path or null if not found\n */\nexport const getFieldValue = <T>(data: T, field: string): CellValue => {\n const parts = field.split(\".\");\n let value: unknown = data;\n\n for (const part of parts) {\n if (value == null || typeof value !== \"object\") {\n return null;\n }\n value = (value as Record<string, unknown>)[part];\n }\n\n return (value ?? null) as CellValue;\n};\n\n/**\n * Set a nested value in an object using dot notation path\n * Creates intermediate objects if they don't exist\n * @param data - The object to modify\n * @param field - Dot-separated path (e.g., \"address.city\")\n * @param value - The value to set\n */\nexport const setFieldValue = (\n data: Record<string, unknown>,\n field: string,\n value: CellValue,\n): void => {\n const parts = field.split(\".\");\n let obj = data;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (!(part in obj)) {\n obj[part] = {};\n }\n obj = obj[part] as Record<string, unknown>;\n }\n\n const lastPart = parts[parts.length - 1]!;\n obj[lastPart] = value;\n};\n","// packages/core/src/utils/event-emitter.ts\n\nimport type { GridInstruction, InstructionListener } from \"../types\";\n\n/**\n * Batch instruction listener for efficient state updates\n */\nexport type BatchInstructionListener = (instructions: GridInstruction[]) => void;\n\n/**\n * Event emitter interface for instruction-based communication\n */\nexport interface InstructionEmitter {\n onInstruction: (listener: InstructionListener) => () => void;\n emit: (instruction: GridInstruction) => void;\n clearListeners: () => void;\n}\n\n/**\n * Extended event emitter with batch support\n */\nexport interface BatchInstructionEmitter extends InstructionEmitter {\n onBatchInstruction: (listener: BatchInstructionListener) => () => void;\n emitBatch: (instructions: GridInstruction[]) => void;\n}\n\n/**\n * Create a simple instruction emitter for managers\n * Eliminates boilerplate listener management code\n */\nexport const createInstructionEmitter = (): InstructionEmitter => {\n let listeners: InstructionListener[] = [];\n\n const onInstruction = (listener: InstructionListener): (() => void) => {\n listeners.push(listener);\n return () => {\n listeners = listeners.filter((l) => l !== listener);\n };\n };\n\n const emit = (instruction: GridInstruction): void => {\n for (const listener of listeners) {\n listener(instruction);\n }\n };\n\n const clearListeners = (): void => {\n listeners = [];\n };\n\n return { onInstruction, emit, clearListeners };\n};\n\n/**\n * Create an instruction emitter with batch support\n * Used by GridCore and SlotPoolManager for efficient updates\n */\nexport const createBatchInstructionEmitter = (): BatchInstructionEmitter => {\n let listeners: InstructionListener[] = [];\n let batchListeners: BatchInstructionListener[] = [];\n\n const onInstruction = (listener: InstructionListener): (() => void) => {\n listeners.push(listener);\n return () => {\n listeners = listeners.filter((l) => l !== listener);\n };\n };\n\n const onBatchInstruction = (listener: BatchInstructionListener): (() => void) => {\n batchListeners.push(listener);\n return () => {\n batchListeners = batchListeners.filter((l) => l !== listener);\n };\n };\n\n const emit = (instruction: GridInstruction): void => {\n // Emit to individual listeners\n for (const listener of listeners) {\n listener(instruction);\n }\n // Also emit as a single-item batch\n for (const listener of batchListeners) {\n listener([instruction]);\n }\n };\n\n const emitBatch = (instructions: GridInstruction[]): void => {\n if (instructions.length === 0) return;\n // Emit to batch listeners as a single batch\n for (const listener of batchListeners) {\n listener(instructions);\n }\n // Also emit to individual listeners for backwards compatibility\n for (const instruction of instructions) {\n for (const listener of listeners) {\n listener(instruction);\n }\n }\n };\n\n const clearListeners = (): void => {\n listeners = [];\n batchListeners = [];\n };\n\n return { onInstruction, onBatchInstruction, emit, emitBatch, clearListeners };\n};\n","// gp-grid-core/src/selection.ts\n\nimport type {\n CellPosition,\n CellRange,\n SelectionState,\n CellValue,\n ColumnDefinition,\n Row,\n} from \"./types\";\nimport { createInstructionEmitter, normalizeRange } from \"./utils\";\n\nexport type Direction = \"up\" | \"down\" | \"left\" | \"right\";\n\nexport interface SelectionManagerOptions {\n getRowCount: () => number;\n getColumnCount: () => number;\n getCellValue: (row: number, col: number) => CellValue;\n getRowData: (row: number) => Row | undefined;\n getColumn: (col: number) => ColumnDefinition | undefined;\n}\n\n/**\n * Manages Excel-style cell selection, keyboard navigation, and clipboard operations.\n */\nexport class SelectionManager {\n private state: SelectionState = {\n activeCell: null,\n range: null,\n anchor: null,\n selectionMode: false,\n };\n\n private options: SelectionManagerOptions;\n private emitter = createInstructionEmitter();\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n private emit = this.emitter.emit;\n\n constructor(options: SelectionManagerOptions) {\n this.options = options;\n }\n\n // ===========================================================================\n // State Accessors\n // ===========================================================================\n\n getState(): SelectionState {\n return { ...this.state };\n }\n\n getActiveCell(): CellPosition | null {\n return this.state.activeCell;\n }\n\n getSelectionRange(): CellRange | null {\n return this.state.range;\n }\n\n isSelected(row: number, col: number): boolean {\n const { range } = this.state;\n if (!range) return false;\n\n const { minRow, maxRow, minCol, maxCol } = normalizeRange(range);\n return row >= minRow && row <= maxRow && col >= minCol && col <= maxCol;\n }\n\n isActiveCell(row: number, col: number): boolean {\n const { activeCell } = this.state;\n return activeCell?.row === row && activeCell?.col === col;\n }\n\n // ===========================================================================\n // Selection Operations\n // ===========================================================================\n\n /**\n * Start a selection at the given cell.\n * @param cell - The cell to select\n * @param opts.shift - Extend selection from anchor (range select)\n * @param opts.ctrl - Toggle selection mode\n */\n startSelection(\n cell: CellPosition,\n opts: { shift?: boolean; ctrl?: boolean } = {}\n ): void {\n const { shift = false, ctrl = false } = opts;\n const { row, col } = this.clampPosition(cell);\n\n if (shift && this.state.anchor) {\n // Extend selection from anchor to current cell\n this.state.range = {\n startRow: this.state.anchor.row,\n startCol: this.state.anchor.col,\n endRow: row,\n endCol: col,\n };\n this.state.activeCell = { row, col };\n } else {\n // Start new selection\n this.state.activeCell = { row, col };\n this.state.anchor = { row, col };\n this.state.range = null;\n }\n\n this.state.selectionMode = ctrl;\n\n this.emit({ type: \"SET_ACTIVE_CELL\", position: this.state.activeCell });\n this.emit({ type: \"SET_SELECTION_RANGE\", range: this.state.range });\n }\n\n /**\n * Move focus in a direction, optionally extending the selection.\n */\n moveFocus(direction: Direction, extend: boolean = false): void {\n if (!this.state.activeCell) {\n // No active cell, select first cell\n this.startSelection({ row: 0, col: 0 });\n return;\n }\n\n const { row, col } = this.state.activeCell;\n let newRow = row;\n let newCol = col;\n\n switch (direction) {\n case \"up\":\n newRow = Math.max(0, row - 1);\n break;\n case \"down\":\n newRow = Math.min(this.options.getRowCount() - 1, row + 1);\n break;\n case \"left\":\n newCol = Math.max(0, col - 1);\n break;\n case \"right\":\n newCol = Math.min(this.options.getColumnCount() - 1, col + 1);\n break;\n }\n\n if (extend) {\n // Extend selection (Shift+Arrow)\n if (!this.state.anchor) {\n this.state.anchor = { row, col };\n }\n\n this.state.range = {\n startRow: this.state.anchor.row,\n startCol: this.state.anchor.col,\n endRow: newRow,\n endCol: newCol,\n };\n this.state.activeCell = { row: newRow, col: newCol };\n\n this.emit({ type: \"SET_ACTIVE_CELL\", position: this.state.activeCell });\n this.emit({ type: \"SET_SELECTION_RANGE\", range: this.state.range });\n } else {\n // Move without extending\n this.state.activeCell = { row: newRow, col: newCol };\n this.state.anchor = { row: newRow, col: newCol };\n this.state.range = null;\n\n this.emit({ type: \"SET_ACTIVE_CELL\", position: this.state.activeCell });\n this.emit({ type: \"SET_SELECTION_RANGE\", range: null });\n }\n }\n\n /**\n * Select all cells in the grid (Ctrl+A).\n */\n selectAll(): void {\n const rowCount = this.options.getRowCount();\n const colCount = this.options.getColumnCount();\n\n if (rowCount === 0 || colCount === 0) return;\n\n this.state.range = {\n startRow: 0,\n startCol: 0,\n endRow: rowCount - 1,\n endCol: colCount - 1,\n };\n\n // Keep active cell if exists, otherwise set to first cell\n if (!this.state.activeCell) {\n this.state.activeCell = { row: 0, col: 0 };\n this.emit({ type: \"SET_ACTIVE_CELL\", position: this.state.activeCell });\n }\n\n this.emit({ type: \"SET_SELECTION_RANGE\", range: this.state.range });\n }\n\n /**\n * Clear the current selection.\n */\n clearSelection(): void {\n this.state.activeCell = null;\n this.state.range = null;\n this.state.anchor = null;\n this.state.selectionMode = false;\n\n this.emit({ type: \"SET_ACTIVE_CELL\", position: null });\n this.emit({ type: \"SET_SELECTION_RANGE\", range: null });\n }\n\n /**\n * Set the active cell directly.\n */\n setActiveCell(row: number, col: number): void {\n const clamped = this.clampPosition({ row, col });\n this.state.activeCell = clamped;\n this.state.anchor = clamped;\n this.state.range = null;\n\n this.emit({ type: \"SET_ACTIVE_CELL\", position: this.state.activeCell });\n this.emit({ type: \"SET_SELECTION_RANGE\", range: null });\n }\n\n /**\n * Set the selection range directly.\n */\n setSelectionRange(range: CellRange): void {\n this.state.range = range;\n this.emit({ type: \"SET_SELECTION_RANGE\", range: this.state.range });\n }\n\n // ===========================================================================\n // Data Extraction\n // ===========================================================================\n\n /**\n * Get the data from the currently selected cells as a 2D array.\n */\n getSelectedData(): CellValue[][] {\n const { range, activeCell } = this.state;\n\n if (!range && !activeCell) {\n return [];\n }\n\n const effectiveRange = range || {\n startRow: activeCell!.row,\n startCol: activeCell!.col,\n endRow: activeCell!.row,\n endCol: activeCell!.col,\n };\n\n const { minRow, maxRow, minCol, maxCol } = normalizeRange(effectiveRange);\n const data: CellValue[][] = [];\n\n for (let r = minRow; r <= maxRow; r++) {\n const row: CellValue[] = [];\n for (let c = minCol; c <= maxCol; c++) {\n row.push(this.options.getCellValue(r, c));\n }\n data.push(row);\n }\n\n return data;\n }\n\n /**\n * Copy the selected data to the clipboard (Ctrl+C).\n */\n async copySelectionToClipboard(): Promise<void> {\n // Guard for SSR - clipboard APIs not available in Node.js\n if (typeof navigator === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n const data = this.getSelectedData();\n if (data.length === 0) return;\n\n // Convert to tab-separated values (Excel-compatible)\n const tsv = data\n .map((row) =>\n row.map((cell) => (cell == null ? \"\" : String(cell))).join(\"\\t\")\n )\n .join(\"\\n\");\n\n try {\n await navigator.clipboard.writeText(tsv);\n } catch (err) {\n // Fallback for older browsers\n const textarea = document.createElement(\"textarea\");\n textarea.value = tsv;\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand(\"copy\");\n document.body.removeChild(textarea);\n }\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n /**\n * Clean up resources for garbage collection.\n */\n destroy(): void {\n this.emitter.clearListeners();\n this.state = {\n activeCell: null,\n range: null,\n anchor: null,\n selectionMode: false,\n };\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n private clampPosition(pos: CellPosition): CellPosition {\n const rowCount = this.options.getRowCount();\n const colCount = this.options.getColumnCount();\n\n return {\n row: Math.max(0, Math.min(pos.row, rowCount - 1)),\n col: Math.max(0, Math.min(pos.col, colCount - 1)),\n };\n }\n}\n\n","// gp-grid-core/src/fill.ts\n\nimport type {\n CellRange,\n CellValue,\n FillHandleState,\n ColumnDefinition,\n} from \"./types\";\nimport { createInstructionEmitter, normalizeRange } from \"./utils\";\n\nexport interface FillManagerOptions {\n getRowCount: () => number;\n getColumnCount: () => number;\n getCellValue: (row: number, col: number) => CellValue;\n getColumn: (col: number) => ColumnDefinition | undefined;\n setCellValue: (row: number, col: number, value: CellValue) => void;\n}\n\n/**\n * Manages fill handle operations including pattern detection and auto-fill.\n */\nexport class FillManager {\n private state: FillHandleState | null = null;\n private options: FillManagerOptions;\n private emitter = createInstructionEmitter();\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n private emit = this.emitter.emit;\n\n constructor(options: FillManagerOptions) {\n this.options = options;\n }\n\n // ===========================================================================\n // State Accessors\n // ===========================================================================\n\n getState(): FillHandleState | null {\n return this.state ? { ...this.state } : null;\n }\n\n isActive(): boolean {\n return this.state !== null;\n }\n\n // ===========================================================================\n // Fill Handle Operations\n // ===========================================================================\n\n /**\n * Start a fill drag operation from a source range.\n */\n startFillDrag(sourceRange: CellRange): void {\n this.state = {\n sourceRange,\n targetRow: sourceRange.endRow,\n targetCol: sourceRange.endCol,\n };\n\n this.emit({ type: \"START_FILL\", sourceRange });\n }\n\n /**\n * Update the fill drag target position.\n */\n updateFillDrag(targetRow: number, targetCol: number): void {\n if (!this.state) return;\n\n // Clamp to valid bounds\n const rowCount = this.options.getRowCount();\n const colCount = this.options.getColumnCount();\n\n targetRow = Math.max(0, Math.min(targetRow, rowCount - 1));\n targetCol = Math.max(0, Math.min(targetCol, colCount - 1));\n\n this.state.targetRow = targetRow;\n this.state.targetCol = targetCol;\n\n this.emit({ type: \"UPDATE_FILL\", targetRow, targetCol });\n }\n\n /**\n * Commit the fill operation - apply pattern to target cells.\n */\n commitFillDrag(): void {\n if (!this.state) return;\n\n const { sourceRange, targetRow } = this.state;\n const filledCells = this.calculateFilledCells(sourceRange, targetRow);\n\n // Apply values\n for (const { row, col, value } of filledCells) {\n this.options.setCellValue(row, col, value);\n }\n\n this.emit({ type: \"COMMIT_FILL\", filledCells });\n\n this.state = null;\n }\n\n /**\n * Cancel the fill operation.\n */\n cancelFillDrag(): void {\n if (!this.state) return;\n\n this.state = null;\n this.emit({ type: \"CANCEL_FILL\" });\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n /**\n * Clean up resources for garbage collection.\n */\n destroy(): void {\n this.emitter.clearListeners();\n this.state = null;\n }\n\n // ===========================================================================\n // Pattern Detection & Fill Logic\n // ===========================================================================\n\n /**\n * Calculate the values to fill based on source pattern.\n */\n private calculateFilledCells(\n sourceRange: CellRange,\n targetRow: number,\n ): Array<{ row: number; col: number; value: CellValue }> {\n const result: Array<{ row: number; col: number; value: CellValue }> = [];\n\n const { minRow: srcMinRow, maxRow: srcMaxRow, minCol: srcMinCol, maxCol: srcMaxCol } =\n normalizeRange(sourceRange);\n\n // Determine fill direction (vertical only)\n const fillDown = targetRow > srcMaxRow;\n const fillUp = targetRow < srcMinRow;\n\n // Only vertical fills are supported\n if (fillDown || fillUp) {\n for (let col = srcMinCol; col <= srcMaxCol; col++) {\n const sourceValues = this.getSourceColumnValues(\n srcMinRow,\n srcMaxRow,\n col,\n );\n const pattern = this.detectPattern(sourceValues);\n\n if (fillDown) {\n for (let row = srcMaxRow + 1; row <= targetRow; row++) {\n const fillIndex = row - srcMaxRow - 1;\n const value = this.applyPattern(pattern, sourceValues, fillIndex);\n result.push({ row, col, value });\n }\n } else if (fillUp) {\n for (let row = srcMinRow - 1; row >= targetRow; row--) {\n const fillIndex = srcMinRow - row - 1;\n const value = this.applyPattern(\n pattern,\n sourceValues,\n fillIndex,\n true,\n );\n result.push({ row, col, value });\n }\n }\n }\n }\n\n return result;\n }\n\n private getSourceColumnValues(\n minRow: number,\n maxRow: number,\n col: number,\n ): CellValue[] {\n const values: CellValue[] = [];\n for (let row = minRow; row <= maxRow; row++) {\n values.push(this.options.getCellValue(row, col));\n }\n return values;\n }\n\n // ===========================================================================\n // Pattern Types\n // ===========================================================================\n\n private detectPattern(values: CellValue[]): FillPattern {\n if (values.length === 0) {\n return { type: \"constant\", value: null };\n }\n\n if (values.length === 1) {\n return { type: \"constant\", value: values[0] ?? null };\n }\n\n // Check for numeric sequence\n const numbers = values.map((v) => (typeof v === \"number\" ? v : Number(v)));\n if (numbers.every((n) => !isNaN(n))) {\n // Check for arithmetic sequence\n const diffs: number[] = [];\n for (let i = 1; i < numbers.length; i++) {\n diffs.push(numbers[i]! - numbers[i - 1]!);\n }\n\n const allSameDiff = diffs.every((d) => d === diffs[0]);\n if (allSameDiff && diffs[0] !== undefined) {\n return { type: \"arithmetic\", start: numbers[0]!, step: diffs[0] };\n }\n }\n\n // Check for repeating pattern\n return { type: \"repeat\", values };\n }\n\n private applyPattern(\n pattern: FillPattern,\n sourceValues: CellValue[],\n fillIndex: number,\n reverse: boolean = false,\n ): CellValue {\n switch (pattern.type) {\n case \"constant\":\n return pattern.value;\n\n case \"arithmetic\": {\n const multiplier = reverse ? -(fillIndex + 1) : fillIndex + 1;\n const lastValue = reverse\n ? pattern.start\n : pattern.start + pattern.step * (sourceValues.length - 1);\n return lastValue + pattern.step * multiplier;\n }\n\n case \"repeat\": {\n const len = pattern.values.length;\n if (len === 0) return null;\n if (reverse) {\n // For reverse, cycle backwards\n const idx = (len - 1 - (fillIndex % len) + len) % len;\n return pattern.values[idx] ?? null;\n }\n return pattern.values[fillIndex % len] ?? null;\n }\n }\n }\n}\n\n// ===========================================================================\n// Pattern Types\n// ===========================================================================\n\ntype FillPattern =\n | { type: \"constant\"; value: CellValue }\n | { type: \"arithmetic\"; start: number; step: number }\n | { type: \"repeat\"; values: CellValue[] };\n","// packages/core/src/slot-pool.ts\n\nimport type { SlotState, Row, GridInstruction } from \"./types\";\nimport { createBatchInstructionEmitter } from \"./utils\";\n\n// Re-export for backwards compatibility\nexport type { BatchInstructionListener } from \"./utils\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SlotPoolManagerOptions {\n /** Get current row height */\n getRowHeight: () => number;\n /** Get current header height */\n getHeaderHeight: () => number;\n /** Get overscan count */\n getOverscan: () => number;\n /** Get current scroll top position (natural, not virtual) */\n getScrollTop: () => number;\n /** Get viewport height */\n getViewportHeight: () => number;\n /** Get total row count */\n getTotalRows: () => number;\n /** Get scroll ratio for virtualization (1 = no virtualization) */\n getScrollRatio: () => number;\n /** Get virtual content height */\n getVirtualContentHeight: () => number;\n /** Get row data by index */\n getRowData: (rowIndex: number) => Row | undefined;\n}\n\ninterface SlotPoolState {\n slots: Map<string, SlotState>;\n /** Maps rowIndex to slotId for quick lookup */\n rowToSlot: Map<number, string>;\n nextSlotId: number;\n}\n\n// =============================================================================\n// SlotPoolManager\n// =============================================================================\n\n/**\n * Manages the slot pool for virtual scrolling.\n * Handles slot creation, recycling, positioning, and destruction.\n */\nexport class SlotPoolManager {\n private state: SlotPoolState = {\n slots: new Map(),\n rowToSlot: new Map(),\n nextSlotId: 0,\n };\n\n private options: SlotPoolManagerOptions;\n private emitter = createBatchInstructionEmitter();\n private isDestroyed: boolean = false;\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n onBatchInstruction = this.emitter.onBatchInstruction;\n private emit = this.emitter.emit;\n private emitBatch = this.emitter.emitBatch;\n\n constructor(options: SlotPoolManagerOptions) {\n this.options = options;\n }\n\n // ===========================================================================\n // State Accessors\n // ===========================================================================\n\n /**\n * Get the slot ID for a given row index.\n */\n getSlotForRow(rowIndex: number): string | undefined {\n return this.state.rowToSlot.get(rowIndex);\n }\n\n /**\n * Get all current slots.\n */\n getSlots(): Map<string, SlotState> {\n return this.state.slots;\n }\n\n // ===========================================================================\n // Slot Synchronization\n // ===========================================================================\n\n /**\n * Synchronize slots with current viewport position.\n * This implements the slot recycling strategy.\n */\n syncSlots(): void {\n const scrollTop = this.options.getScrollTop();\n const rowHeight = this.options.getRowHeight();\n const viewportHeight = this.options.getViewportHeight();\n const totalRows = this.options.getTotalRows();\n const overscan = this.options.getOverscan();\n\n const visibleStartRow = Math.max(\n 0,\n Math.floor(scrollTop / rowHeight) - overscan\n );\n const visibleEndRow = Math.min(\n totalRows - 1,\n Math.ceil((scrollTop + viewportHeight) / rowHeight) + overscan\n );\n\n if (totalRows === 0 || visibleEndRow < visibleStartRow) {\n // No rows to display - destroy all slots\n this.destroyAllSlots();\n return;\n }\n\n const requiredRows = new Set<number>();\n for (let row = visibleStartRow; row <= visibleEndRow; row++) {\n requiredRows.add(row);\n }\n\n const instructions: GridInstruction[] = [];\n\n // Find slots that are no longer needed\n const slotsToRecycle: string[] = [];\n for (const [slotId, slot] of this.state.slots) {\n if (!requiredRows.has(slot.rowIndex)) {\n slotsToRecycle.push(slotId);\n this.state.rowToSlot.delete(slot.rowIndex);\n } else {\n requiredRows.delete(slot.rowIndex);\n }\n }\n\n // Assign recycled slots to new rows\n const rowsNeedingSlots = Array.from(requiredRows);\n for (let i = 0; i < rowsNeedingSlots.length; i++) {\n const rowIndex = rowsNeedingSlots[i]!;\n const rowData = this.options.getRowData(rowIndex);\n\n if (i < slotsToRecycle.length) {\n // Recycle existing slot\n const slotId = slotsToRecycle[i]!;\n const slot = this.state.slots.get(slotId)!;\n const translateY = this.getRowTranslateY(rowIndex);\n\n slot.rowIndex = rowIndex;\n slot.rowData = rowData ?? {};\n slot.translateY = translateY;\n\n this.state.rowToSlot.set(rowIndex, slotId);\n\n instructions.push({\n type: \"ASSIGN_SLOT\",\n slotId,\n rowIndex,\n rowData: rowData ?? {},\n });\n instructions.push({\n type: \"MOVE_SLOT\",\n slotId,\n translateY,\n });\n } else {\n // Create new slot\n const slotId = `slot-${this.state.nextSlotId++}`;\n const translateY = this.getRowTranslateY(rowIndex);\n\n const newSlot: SlotState = {\n slotId,\n rowIndex,\n rowData: rowData ?? {},\n translateY,\n };\n\n this.state.slots.set(slotId, newSlot);\n this.state.rowToSlot.set(rowIndex, slotId);\n\n instructions.push({ type: \"CREATE_SLOT\", slotId });\n instructions.push({\n type: \"ASSIGN_SLOT\",\n slotId,\n rowIndex,\n rowData: rowData ?? {},\n });\n instructions.push({\n type: \"MOVE_SLOT\",\n slotId,\n translateY,\n });\n }\n }\n\n // Destroy excess slots\n for (let i = rowsNeedingSlots.length; i < slotsToRecycle.length; i++) {\n const slotId = slotsToRecycle[i]!;\n this.state.slots.delete(slotId);\n instructions.push({ type: \"DESTROY_SLOT\", slotId });\n }\n\n // Update positions of existing slots that haven't moved\n for (const [slotId, slot] of this.state.slots) {\n const expectedY = this.getRowTranslateY(slot.rowIndex);\n if (slot.translateY !== expectedY) {\n slot.translateY = expectedY;\n instructions.push({\n type: \"MOVE_SLOT\",\n slotId,\n translateY: expectedY,\n });\n }\n }\n\n this.emitBatch(instructions);\n }\n\n /**\n * Destroy all slots.\n */\n destroyAllSlots(): void {\n const instructions: GridInstruction[] = [];\n for (const slotId of this.state.slots.keys()) {\n instructions.push({ type: \"DESTROY_SLOT\", slotId });\n }\n this.state.slots.clear();\n this.state.rowToSlot.clear();\n this.emitBatch(instructions);\n }\n\n /**\n * Clean up resources for garbage collection.\n * This method is idempotent - safe to call multiple times.\n */\n destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n // Clear slots without emitting (no listeners to notify during cleanup)\n this.state.slots.clear();\n this.state.rowToSlot.clear();\n this.emitter.clearListeners();\n }\n\n /**\n * Refresh all slot data without changing which rows are displayed.\n * Used after filtering/sorting when data changes.\n */\n refreshAllSlots(): void {\n const instructions: GridInstruction[] = [];\n const totalRows = this.options.getTotalRows();\n\n for (const [slotId, slot] of this.state.slots) {\n // Check if row index is still valid\n if (slot.rowIndex >= 0 && slot.rowIndex < totalRows) {\n const rowData = this.options.getRowData(slot.rowIndex);\n const translateY = this.getRowTranslateY(slot.rowIndex);\n\n slot.rowData = rowData ?? {};\n slot.translateY = translateY;\n\n instructions.push({\n type: \"ASSIGN_SLOT\",\n slotId,\n rowIndex: slot.rowIndex,\n rowData: rowData ?? {},\n });\n instructions.push({\n type: \"MOVE_SLOT\",\n slotId,\n translateY,\n });\n }\n }\n\n this.emitBatch(instructions);\n\n // Also sync slots to handle any rows that went out of bounds\n this.syncSlots();\n }\n\n /**\n * Update a single slot's data.\n */\n updateSlot(rowIndex: number): void {\n const slotId = this.state.rowToSlot.get(rowIndex);\n if (slotId) {\n const rowData = this.options.getRowData(rowIndex);\n if (rowData) {\n this.emit({\n type: \"ASSIGN_SLOT\",\n slotId,\n rowIndex,\n rowData,\n });\n }\n }\n }\n\n // ===========================================================================\n // Position Calculation\n // ===========================================================================\n\n /**\n * Calculate the translateY position for a row.\n * Handles scroll virtualization for very large datasets.\n */\n private getRowTranslateY(rowIndex: number): number {\n const rowHeight = this.options.getRowHeight();\n const headerHeight = this.options.getHeaderHeight();\n const scrollRatio = this.options.getScrollRatio();\n const virtualContentHeight = this.options.getVirtualContentHeight();\n const scrollTop = this.options.getScrollTop();\n\n // Calculate the natural position for this row\n const naturalY = rowIndex * rowHeight + headerHeight;\n\n if (scrollRatio >= 1) {\n return naturalY;\n }\n\n // With scroll virtualization, we need to position rows relative to the viewport\n // so they appear at the correct location within the capped container height.\n //\n // scrollTop is where we are in \"real\" content space (already mapped from virtual)\n // virtualScrollTop is where the browser thinks we are in the DOM\n // offset = the difference we need to subtract to keep rows within bounds\n const naturalScrollTop = scrollTop;\n const virtualScrollTop = naturalScrollTop * scrollRatio;\n const offset = naturalScrollTop - virtualScrollTop;\n\n // Position row at its natural Y minus the offset\n // Clamp to ensure it stays within virtual container bounds (0 to virtualContentHeight)\n // This prevents floating point precision issues at extreme scroll positions\n const translateY = naturalY - offset;\n return Math.max(0, Math.min(translateY, virtualContentHeight));\n }\n}\n","// packages/core/src/edit-manager.ts\n\nimport type { EditState, CellValue, ColumnDefinition } from \"./types\";\nimport { createInstructionEmitter } from \"./utils\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface EditManagerOptions {\n /** Get column definition by index */\n getColumn: (colIndex: number) => ColumnDefinition | undefined;\n /** Get cell value */\n getCellValue: (row: number, col: number) => CellValue;\n /** Set cell value */\n setCellValue: (row: number, col: number, value: CellValue) => void;\n /** Callback when edit is committed (to update slot display) */\n onCommit?: (row: number, col: number, value: CellValue) => void;\n}\n\n// =============================================================================\n// EditManager\n// =============================================================================\n\n/**\n * Manages cell editing state and operations.\n */\nexport class EditManager {\n private editState: EditState | null = null;\n private options: EditManagerOptions;\n private emitter = createInstructionEmitter();\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n private emit = this.emitter.emit;\n\n constructor(options: EditManagerOptions) {\n this.options = options;\n }\n\n // ===========================================================================\n // State Accessors\n // ===========================================================================\n\n /**\n * Get the current edit state.\n */\n getState(): EditState | null {\n return this.editState ? { ...this.editState } : null;\n }\n\n /**\n * Check if currently editing.\n */\n isEditing(): boolean {\n return this.editState !== null;\n }\n\n /**\n * Check if a specific cell is being edited.\n */\n isEditingCell(row: number, col: number): boolean {\n return (\n this.editState !== null &&\n this.editState.row === row &&\n this.editState.col === col\n );\n }\n\n // ===========================================================================\n // Edit Operations\n // ===========================================================================\n\n /**\n * Start editing a cell.\n * Returns true if edit was started, false if cell is not editable.\n */\n startEdit(row: number, col: number): boolean {\n const column = this.options.getColumn(col);\n if (!column || column.editable !== true) {\n return false;\n }\n\n const initialValue = this.options.getCellValue(row, col);\n this.editState = {\n row,\n col,\n initialValue,\n currentValue: initialValue,\n };\n\n this.emit({\n type: \"START_EDIT\",\n row,\n col,\n initialValue,\n });\n\n return true;\n }\n\n /**\n * Update the current edit value.\n */\n updateValue(value: CellValue): void {\n if (this.editState) {\n this.editState.currentValue = value;\n }\n }\n\n /**\n * Commit the current edit.\n * Saves the value and closes the editor.\n */\n commit(): void {\n if (!this.editState) return;\n\n const { row, col, currentValue } = this.editState;\n\n // Update the cell value\n this.options.setCellValue(row, col, currentValue);\n\n // Emit commit instruction\n this.emit({\n type: \"COMMIT_EDIT\",\n row,\n col,\n value: currentValue,\n });\n\n // Clear edit state\n this.editState = null;\n this.emit({ type: \"STOP_EDIT\" });\n\n // Notify that edit was committed (for slot update)\n this.options.onCommit?.(row, col, currentValue);\n }\n\n /**\n * Cancel the current edit.\n * Discards changes and closes the editor.\n */\n cancel(): void {\n this.editState = null;\n this.emit({ type: \"STOP_EDIT\" });\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n /**\n * Clean up resources for garbage collection.\n */\n destroy(): void {\n this.emitter.clearListeners();\n this.editState = null;\n }\n}\n","// packages/core/src/input-handler.ts\n// Framework-agnostic input handler containing all input business logic\n\nimport type { GridCore } from \"./grid-core\";\nimport type { Row, CellPosition, CellRange } from \"./types\";\nimport type {\n PointerEventData,\n KeyEventData,\n ContainerBounds,\n InputResult,\n KeyboardResult,\n DragMoveResult,\n InputHandlerDeps,\n DragState,\n} from \"./types/input\";\nimport type { Direction } from \"./selection\";\nimport { findColumnAtX } from \"./utils\";\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst AUTO_SCROLL_THRESHOLD = 40;\nconst AUTO_SCROLL_SPEED = 10;\n\n// =============================================================================\n// InputHandler Class\n// =============================================================================\n\nexport class InputHandler<TData extends Row = Row> {\n private core: GridCore<TData>;\n private deps: InputHandlerDeps;\n\n // Drag state\n private isDraggingSelection = false;\n private isDraggingFill = false;\n private fillSourceRange: CellRange | null = null;\n private fillTarget: { row: number; col: number } | null = null;\n\n constructor(core: GridCore<TData>, deps: InputHandlerDeps) {\n this.core = core;\n this.deps = deps;\n }\n\n /**\n * Update dependencies (called when options change)\n */\n updateDeps(deps: Partial<InputHandlerDeps>): void {\n this.deps = { ...this.deps, ...deps };\n }\n\n // ===========================================================================\n // State Accessors (for UI rendering)\n // ===========================================================================\n\n /**\n * Get current drag state for UI rendering\n */\n getDragState(): DragState {\n return {\n isDragging: this.isDraggingSelection || this.isDraggingFill,\n dragType: this.isDraggingFill\n ? \"fill\"\n : this.isDraggingSelection\n ? \"selection\"\n : null,\n fillSourceRange: this.fillSourceRange,\n fillTarget: this.fillTarget,\n };\n }\n\n // ===========================================================================\n // Cell Mouse Down\n // ===========================================================================\n\n /**\n * Handle cell mouse down event\n */\n handleCellMouseDown(\n rowIndex: number,\n colIndex: number,\n event: PointerEventData\n ): InputResult {\n // Only handle left mouse button\n if (event.button !== 0) {\n return { preventDefault: false, stopPropagation: false };\n }\n\n // Don't start selection while editing\n if (this.core.getEditState() !== null) {\n return { preventDefault: false, stopPropagation: false };\n }\n\n this.core.selection.startSelection(\n { row: rowIndex, col: colIndex },\n { shift: event.shiftKey, ctrl: event.ctrlKey || event.metaKey }\n );\n\n return {\n preventDefault: false,\n stopPropagation: false,\n focusContainer: true,\n startDrag: event.shiftKey ? undefined : \"selection\",\n };\n }\n\n /**\n * Handle cell double click event (start editing)\n */\n handleCellDoubleClick(rowIndex: number, colIndex: number): void {\n this.core.startEdit(rowIndex, colIndex);\n }\n\n // ===========================================================================\n // Hover Tracking (for highlighting)\n // ===========================================================================\n\n /**\n * Handle cell mouse enter event (for hover highlighting)\n */\n handleCellMouseEnter(rowIndex: number, colIndex: number): void {\n this.core.highlight?.setHoverPosition({ row: rowIndex, col: colIndex });\n }\n\n /**\n * Handle cell mouse leave event (for hover highlighting)\n */\n handleCellMouseLeave(): void {\n this.core.highlight?.setHoverPosition(null);\n }\n\n // ===========================================================================\n // Fill Handle Mouse Down\n // ===========================================================================\n\n /**\n * Handle fill handle mouse down event\n */\n handleFillHandleMouseDown(\n activeCell: CellPosition | null,\n selectionRange: CellRange | null,\n _event: PointerEventData\n ): InputResult {\n if (!activeCell && !selectionRange) {\n return { preventDefault: false, stopPropagation: false };\n }\n\n // Create source range from selection or active cell\n const sourceRange: CellRange = selectionRange ?? {\n startRow: activeCell!.row,\n startCol: activeCell!.col,\n endRow: activeCell!.row,\n endCol: activeCell!.col,\n };\n\n this.core.fill.startFillDrag(sourceRange);\n this.fillSourceRange = sourceRange;\n this.fillTarget = {\n row: Math.max(sourceRange.startRow, sourceRange.endRow),\n col: Math.max(sourceRange.startCol, sourceRange.endCol),\n };\n this.isDraggingFill = true;\n\n return {\n preventDefault: true,\n stopPropagation: true,\n startDrag: \"fill\",\n };\n }\n\n // ===========================================================================\n // Header Click\n // ===========================================================================\n\n /**\n * Handle header click event (cycle sort direction)\n */\n handleHeaderClick(colId: string, addToExisting: boolean): void {\n const currentDirection = this.core\n .getSortModel()\n .find((s) => s.colId === colId)?.direction;\n\n const nextDirection =\n currentDirection === undefined || currentDirection === null\n ? \"asc\"\n : currentDirection === \"asc\"\n ? \"desc\"\n : null;\n\n this.core.setSort(colId, nextDirection, addToExisting);\n }\n\n // ===========================================================================\n // Drag Operations\n // ===========================================================================\n\n /**\n * Start selection drag (called by framework after handleCellMouseDown returns startDrag: 'selection')\n */\n startSelectionDrag(): void {\n this.isDraggingSelection = true;\n }\n\n /**\n * Handle drag move event (selection or fill)\n */\n handleDragMove(\n event: PointerEventData,\n bounds: ContainerBounds\n ): DragMoveResult | null {\n if (!this.isDraggingSelection && !this.isDraggingFill) {\n return null;\n }\n\n const { top, left, width, height, scrollTop, scrollLeft } = bounds;\n const headerHeight = this.deps.getHeaderHeight();\n const columnPositions = this.deps.getColumnPositions();\n const columnCount = this.deps.getColumnCount();\n\n // Calculate mouse position relative to grid content\n const mouseX = event.clientX - left + scrollLeft;\n // Viewport-relative Y (physical pixels below header, NOT including scroll)\n const viewportY = event.clientY - top - headerHeight;\n\n // Find target row (core method handles scroll and scaling)\n const targetRow = Math.max(\n 0,\n Math.min(\n this.core.getRowIndexAtDisplayY(viewportY, scrollTop),\n this.core.getRowCount() - 1\n )\n );\n\n // Find target column (visible index first, then convert to original)\n const visibleColIndex = Math.max(\n 0,\n Math.min(findColumnAtX(mouseX, columnPositions), columnCount - 1)\n );\n // Convert visible index to original column index (for hidden column support)\n const targetCol = this.deps.getOriginalColumnIndex\n ? this.deps.getOriginalColumnIndex(visibleColIndex)\n : visibleColIndex;\n\n // Handle selection drag\n if (this.isDraggingSelection) {\n this.core.selection.startSelection(\n { row: targetRow, col: targetCol },\n { shift: true }\n );\n }\n\n // Handle fill drag\n if (this.isDraggingFill) {\n this.core.fill.updateFillDrag(targetRow, targetCol);\n this.fillTarget = { row: targetRow, col: targetCol };\n }\n\n // Calculate auto-scroll\n const mouseYInContainer = event.clientY - top;\n const mouseXInContainer = event.clientX - left;\n const autoScroll = this.calculateAutoScroll(\n mouseYInContainer,\n mouseXInContainer,\n height,\n width,\n headerHeight\n );\n\n return { targetRow, targetCol, autoScroll };\n }\n\n /**\n * Handle drag end event\n */\n handleDragEnd(): void {\n if (this.isDraggingFill) {\n this.core.fill.commitFillDrag();\n this.core.refreshSlotData();\n }\n\n this.isDraggingSelection = false;\n this.isDraggingFill = false;\n this.fillSourceRange = null;\n this.fillTarget = null;\n }\n\n // ===========================================================================\n // Wheel\n // ===========================================================================\n\n /**\n * Handle wheel event with dampening for large datasets\n * Returns scroll deltas or null if no dampening needed\n */\n handleWheel(\n deltaY: number,\n deltaX: number,\n dampening: number\n ): { dy: number; dx: number } | null {\n if (!this.core.isScalingActive()) {\n return null;\n }\n return { dy: deltaY * dampening, dx: deltaX * dampening };\n }\n\n // ===========================================================================\n // Keyboard\n // ===========================================================================\n\n /**\n * Handle keyboard event\n */\n handleKeyDown(\n event: KeyEventData,\n activeCell: CellPosition | null,\n editingCell: { row: number; col: number } | null,\n filterPopupOpen: boolean\n ): KeyboardResult {\n // Don't handle keyboard events when filter popup is open\n if (filterPopupOpen) {\n return { preventDefault: false };\n }\n\n // Don't handle most keys while editing (except special keys)\n if (\n editingCell &&\n event.key !== \"Enter\" &&\n event.key !== \"Escape\" &&\n event.key !== \"Tab\"\n ) {\n return { preventDefault: false };\n }\n\n const { selection } = this.core;\n const isShift = event.shiftKey;\n const isCtrl = event.ctrlKey || event.metaKey;\n\n // Helper to get key direction\n const keyToDirection = (key: string): Direction | null => {\n switch (key) {\n case \"ArrowUp\":\n return \"up\";\n case \"ArrowDown\":\n return \"down\";\n case \"ArrowLeft\":\n return \"left\";\n case \"ArrowRight\":\n return \"right\";\n default:\n return null;\n }\n };\n\n // Arrow key navigation\n const direction = keyToDirection(event.key);\n if (direction) {\n selection.moveFocus(direction, isShift);\n const newActiveCell = selection.getActiveCell();\n return {\n preventDefault: true,\n scrollToCell: newActiveCell ?? undefined,\n };\n }\n\n switch (event.key) {\n case \"Enter\":\n if (editingCell) {\n this.core.commitEdit();\n } else if (activeCell) {\n this.core.startEdit(activeCell.row, activeCell.col);\n }\n return { preventDefault: true };\n\n case \"Escape\":\n if (editingCell) {\n this.core.cancelEdit();\n } else {\n selection.clearSelection();\n }\n return { preventDefault: true };\n\n case \"Tab\":\n if (editingCell) {\n this.core.commitEdit();\n }\n selection.moveFocus(isShift ? \"left\" : \"right\", false);\n return { preventDefault: true };\n\n case \"a\":\n if (isCtrl) {\n selection.selectAll();\n return { preventDefault: true };\n }\n break;\n\n case \"c\":\n if (isCtrl) {\n selection.copySelectionToClipboard();\n return { preventDefault: true };\n }\n break;\n\n case \"F2\":\n if (activeCell && !editingCell) {\n this.core.startEdit(activeCell.row, activeCell.col);\n }\n return { preventDefault: true };\n\n case \"Delete\":\n case \"Backspace\":\n // Start editing with empty value on delete/backspace\n if (activeCell && !editingCell) {\n this.core.startEdit(activeCell.row, activeCell.col);\n return { preventDefault: true };\n }\n break;\n\n default:\n // Start editing on any printable character\n if (activeCell && !editingCell && !isCtrl && event.key.length === 1) {\n this.core.startEdit(activeCell.row, activeCell.col);\n }\n break;\n }\n\n return { preventDefault: false };\n }\n\n // ===========================================================================\n // Helper Methods\n // ===========================================================================\n\n /**\n * Calculate auto-scroll deltas based on mouse position\n */\n private calculateAutoScroll(\n mouseYInContainer: number,\n mouseXInContainer: number,\n containerHeight: number,\n containerWidth: number,\n headerHeight: number\n ): { dx: number; dy: number } | null {\n let dx = 0;\n let dy = 0;\n\n // Vertical scrolling\n if (mouseYInContainer < AUTO_SCROLL_THRESHOLD + headerHeight) {\n dy = -AUTO_SCROLL_SPEED;\n } else if (mouseYInContainer > containerHeight - AUTO_SCROLL_THRESHOLD) {\n dy = AUTO_SCROLL_SPEED;\n }\n\n // Horizontal scrolling\n if (mouseXInContainer < AUTO_SCROLL_THRESHOLD) {\n dx = -AUTO_SCROLL_SPEED;\n } else if (mouseXInContainer > containerWidth - AUTO_SCROLL_THRESHOLD) {\n dx = AUTO_SCROLL_SPEED;\n }\n\n return dx !== 0 || dy !== 0 ? { dx, dy } : null;\n }\n}\n","// packages/core/src/highlight-manager.ts\n\nimport type {\n CellPosition,\n CellRange,\n ColumnDefinition,\n HighlightingOptions,\n HighlightContext,\n} from \"./types\";\nimport {\n createInstructionEmitter,\n normalizeRange,\n isRowInSelectionRange,\n isColumnInSelectionRange,\n} from \"./utils\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface HighlightManagerOptions {\n getActiveCell: () => CellPosition | null;\n getSelectionRange: () => CellRange | null;\n getColumn: (colIndex: number) => ColumnDefinition | undefined;\n}\n\n// =============================================================================\n// HighlightManager\n// =============================================================================\n\n/**\n * Manages row/column/cell highlighting state and class computation.\n * Emits SET_HOVER_POSITION instructions when hover position changes.\n */\nexport class HighlightManager<TData = Record<string, unknown>> {\n private options: HighlightManagerOptions;\n private highlightingOptions: HighlightingOptions<TData>;\n private hoverPosition: CellPosition | null = null;\n private emitter = createInstructionEmitter();\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n private emit = this.emitter.emit;\n\n // Caches (cleared on state change: hover or selection)\n private rowClassCache: Map<number, string[]> = new Map();\n private columnClassCache: Map<number, string[]> = new Map();\n private cellClassCache: Map<string, string[]> = new Map();\n\n constructor(\n options: HighlightManagerOptions,\n highlightingOptions: HighlightingOptions<TData> = {},\n ) {\n this.options = options;\n this.highlightingOptions = highlightingOptions;\n }\n\n // ===========================================================================\n // Configuration\n // ===========================================================================\n\n /**\n * Check if highlighting is enabled (any callback defined).\n * Hover tracking is automatically enabled when highlighting is enabled.\n */\n isEnabled(): boolean {\n return !!(\n this.highlightingOptions.computeRowClasses ||\n this.highlightingOptions.computeColumnClasses ||\n this.highlightingOptions.computeCellClasses\n );\n }\n\n /**\n * Update highlighting options. Clears all caches.\n */\n updateOptions(options: HighlightingOptions<TData>): void {\n this.highlightingOptions = options;\n this.clearAllCaches();\n }\n\n // ===========================================================================\n // Hover State Management\n // ===========================================================================\n\n /**\n * Set the current hover position. Clears caches and emits instruction.\n * Hover tracking is automatically enabled when any highlighting callback is defined.\n */\n setHoverPosition(position: CellPosition | null): void {\n // Skip if highlighting is not enabled\n if (!this.isEnabled()) return;\n\n // Skip if position unchanged\n if (\n this.hoverPosition?.row === position?.row &&\n this.hoverPosition?.col === position?.col\n ) {\n return;\n }\n\n // Clear caches (hover and cell caches depend on hover position)\n this.rowClassCache.clear();\n this.columnClassCache.clear();\n this.cellClassCache.clear();\n\n this.hoverPosition = position;\n this.emit({ type: \"SET_HOVER_POSITION\", position });\n }\n\n /**\n * Get the current hover position\n */\n getHoverPosition(): CellPosition | null {\n return this.hoverPosition;\n }\n\n // ===========================================================================\n // Selection Change Notification\n // ===========================================================================\n\n /**\n * Called when selection changes. Clears all caches.\n */\n onSelectionChange(): void {\n this.rowClassCache.clear();\n this.columnClassCache.clear();\n this.cellClassCache.clear();\n }\n\n // ===========================================================================\n // Context Builders\n // ===========================================================================\n\n /**\n * Build context for row highlighting callback.\n * Returns context with `rowIndex` set, `colIndex` is null.\n * `isHovered` is true when the mouse is on any cell in this row.\n */\n buildRowContext(\n rowIndex: number,\n rowData?: TData,\n ): HighlightContext<TData> {\n const activeCell = this.options.getActiveCell();\n const selectionRange = this.options.getSelectionRange();\n\n return {\n rowIndex,\n colIndex: null,\n column: undefined,\n rowData,\n hoverPosition: this.hoverPosition,\n activeCell,\n selectionRange,\n isHovered: this.hoverPosition?.row === rowIndex,\n isActive: activeCell?.row === rowIndex,\n isSelected: isRowInSelectionRange(rowIndex, selectionRange),\n };\n }\n\n /**\n * Build context for column highlighting callback.\n * Returns context with `colIndex` set, `rowIndex` is null.\n * `isHovered` is true when the mouse is on any cell in this column.\n */\n buildColumnContext(\n colIndex: number,\n column: ColumnDefinition,\n ): HighlightContext<TData> {\n const activeCell = this.options.getActiveCell();\n const selectionRange = this.options.getSelectionRange();\n\n return {\n rowIndex: null,\n colIndex,\n column,\n rowData: undefined,\n hoverPosition: this.hoverPosition,\n activeCell,\n selectionRange,\n isHovered: this.hoverPosition?.col === colIndex,\n isActive: activeCell?.col === colIndex,\n isSelected: isColumnInSelectionRange(colIndex, selectionRange),\n };\n }\n\n /**\n * Build context for cell highlighting callback.\n * Returns context with both `rowIndex` and `colIndex` set.\n * `isHovered` is true only when the mouse is on this exact cell.\n */\n buildCellContext(\n rowIndex: number,\n colIndex: number,\n column: ColumnDefinition,\n rowData?: TData,\n ): HighlightContext<TData> {\n const activeCell = this.options.getActiveCell();\n const selectionRange = this.options.getSelectionRange();\n\n // isHovered is true only for the exact cell\n const isHovered =\n this.hoverPosition?.row === rowIndex &&\n this.hoverPosition?.col === colIndex;\n\n // Check if cell is in selection\n let isSelected = false;\n if (selectionRange) {\n const { minRow, maxRow, minCol, maxCol } = normalizeRange(selectionRange);\n isSelected =\n rowIndex >= minRow &&\n rowIndex <= maxRow &&\n colIndex >= minCol &&\n colIndex <= maxCol;\n }\n\n return {\n rowIndex,\n colIndex,\n column,\n rowData,\n hoverPosition: this.hoverPosition,\n activeCell,\n selectionRange,\n isHovered,\n isActive: activeCell?.row === rowIndex && activeCell?.col === colIndex,\n isSelected,\n };\n }\n\n // ===========================================================================\n // Class Computation\n // ===========================================================================\n\n /**\n * Compute row classes using cache and user callback\n */\n computeRowClasses(rowIndex: number, rowData?: TData): string[] {\n const callback = this.highlightingOptions.computeRowClasses;\n if (!callback) return [];\n\n const cached = this.rowClassCache.get(rowIndex);\n if (cached !== undefined) return cached;\n\n const context = this.buildRowContext(rowIndex, rowData);\n const result = callback(context);\n this.rowClassCache.set(rowIndex, result);\n return result;\n }\n\n /**\n * Compute column classes using cache and user callback (or per-column override)\n */\n computeColumnClasses(\n colIndex: number,\n column: ColumnDefinition,\n ): string[] {\n const cached = this.columnClassCache.get(colIndex);\n if (cached !== undefined) return cached;\n\n const context = this.buildColumnContext(colIndex, column);\n\n // Per-column override takes precedence, then grid-level callback\n let result: string[];\n if (column.computeColumnClasses) {\n // Cast needed: column callbacks use HighlightContext without generic\n result = column.computeColumnClasses(context as HighlightContext);\n } else if (this.highlightingOptions.computeColumnClasses) {\n result = this.highlightingOptions.computeColumnClasses(context);\n } else {\n return [];\n }\n\n this.columnClassCache.set(colIndex, result);\n return result;\n }\n\n /**\n * Compute cell classes using cache and user callback (or per-column override)\n */\n computeCellClasses(\n rowIndex: number,\n colIndex: number,\n column: ColumnDefinition,\n rowData?: TData,\n ): string[] {\n const cacheKey = `${rowIndex},${colIndex}`;\n const cached = this.cellClassCache.get(cacheKey);\n if (cached !== undefined) return cached;\n\n const context = this.buildCellContext(rowIndex, colIndex, column, rowData);\n\n // Per-column override takes precedence, then grid-level callback\n let result: string[];\n if (column.computeCellClasses) {\n // Cast needed: column callbacks use HighlightContext without generic\n result = column.computeCellClasses(context as HighlightContext);\n } else if (this.highlightingOptions.computeCellClasses) {\n result = this.highlightingOptions.computeCellClasses(context);\n } else {\n return [];\n }\n\n this.cellClassCache.set(cacheKey, result);\n return result;\n }\n\n /**\n * Compute combined cell classes (column + cell classes flattened)\n */\n computeCombinedCellClasses(\n rowIndex: number,\n colIndex: number,\n column: ColumnDefinition,\n rowData?: TData,\n ): string[] {\n const columnClasses = this.computeColumnClasses(colIndex, column);\n const cellClasses = this.computeCellClasses(rowIndex, colIndex, column, rowData);\n return [...columnClasses, ...cellClasses];\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n /**\n * Clear all caches\n */\n clearAllCaches(): void {\n this.rowClassCache.clear();\n this.columnClassCache.clear();\n this.cellClassCache.clear();\n }\n\n /**\n * Destroy the manager and release resources\n */\n destroy(): void {\n this.emitter.clearListeners();\n this.clearAllCaches();\n this.hoverPosition = null;\n }\n}\n","// packages/core/src/sort-filter-manager.ts\n\nimport type {\n ColumnDefinition,\n CellValue,\n SortModel,\n SortDirection,\n FilterModel,\n ColumnFilterModel,\n} from \"./types\";\nimport { createInstructionEmitter, getFieldValue } from \"./utils\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SortFilterManagerOptions<TData> {\n /** Get all columns */\n getColumns: () => ColumnDefinition[];\n /** Check if sorting is enabled globally */\n isSortingEnabled: () => boolean;\n /** Get cached rows for distinct value computation */\n getCachedRows: () => Map<number, TData>;\n /** Called when sort/filter changes to trigger data refresh */\n onSortFilterChange: () => Promise<void>;\n /** Called after data refresh to update UI */\n onDataRefreshed: () => void;\n}\n\n// =============================================================================\n// SortFilterManager\n// =============================================================================\n\n/**\n * Manages sorting and filtering state and operations.\n */\nexport class SortFilterManager<TData = Record<string, unknown>> {\n private options: SortFilterManagerOptions<TData>;\n private emitter = createInstructionEmitter();\n\n // Sort & Filter state\n private sortModel: SortModel[] = [];\n private filterModel: FilterModel = {};\n private openFilterColIndex: number | null = null;\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n private emit = this.emitter.emit;\n\n constructor(options: SortFilterManagerOptions<TData>) {\n this.options = options;\n }\n\n // ===========================================================================\n // Sort Operations\n // ===========================================================================\n\n async setSort(\n colId: string,\n direction: SortDirection | null,\n addToExisting: boolean = false\n ): Promise<void> {\n // Check if sorting is enabled globally\n if (!this.options.isSortingEnabled()) return;\n\n // Check if sorting is enabled for this column\n const columns = this.options.getColumns();\n const column = columns.find(c => (c.colId ?? c.field) === colId);\n if (column?.sortable === false) return;\n\n const existingIndex = this.sortModel.findIndex((s) => s.colId === colId);\n\n if (!addToExisting) {\n this.sortModel = direction === null ? [] : [{ colId, direction }];\n } else {\n if (direction === null) {\n if (existingIndex >= 0) {\n this.sortModel.splice(existingIndex, 1);\n }\n } else if (existingIndex >= 0) {\n this.sortModel[existingIndex]!.direction = direction;\n } else {\n this.sortModel.push({ colId, direction });\n }\n }\n\n await this.options.onSortFilterChange();\n this.options.onDataRefreshed();\n }\n\n getSortModel(): SortModel[] {\n return [...this.sortModel];\n }\n\n // ===========================================================================\n // Filter Operations\n // ===========================================================================\n\n async setFilter(colId: string, filter: ColumnFilterModel | string | null): Promise<void> {\n const columns = this.options.getColumns();\n const column = columns.find(c => (c.colId ?? c.field) === colId);\n if (column?.filterable === false) return;\n\n // Handle null, empty string, or empty conditions\n const isEmpty = filter === null ||\n (typeof filter === \"string\" && filter.trim() === \"\") ||\n (typeof filter === \"object\" && filter.conditions && filter.conditions.length === 0);\n\n if (isEmpty) {\n delete this.filterModel[colId];\n } else if (typeof filter === \"string\") {\n // Convert old string format to new ColumnFilterModel format\n this.filterModel[colId] = {\n conditions: [{ type: \"text\", operator: \"contains\", value: filter }],\n combination: \"and\",\n };\n } else {\n this.filterModel[colId] = filter;\n }\n\n await this.options.onSortFilterChange();\n this.options.onDataRefreshed();\n }\n\n getFilterModel(): FilterModel {\n return { ...this.filterModel };\n }\n\n /**\n * Check if a column has an active filter\n */\n hasActiveFilter(colId: string): boolean {\n const filter = this.filterModel[colId];\n if (!filter) return false;\n return filter.conditions.length > 0;\n }\n\n // ===========================================================================\n // Column Checks\n // ===========================================================================\n\n /**\n * Check if a column is sortable\n */\n isColumnSortable(colIndex: number): boolean {\n if (!this.options.isSortingEnabled()) return false;\n const columns = this.options.getColumns();\n const column = columns[colIndex];\n return column?.sortable !== false;\n }\n\n /**\n * Check if a column is filterable\n */\n isColumnFilterable(colIndex: number): boolean {\n const columns = this.options.getColumns();\n const column = columns[colIndex];\n return column?.filterable !== false;\n }\n\n // ===========================================================================\n // Distinct Values\n // ===========================================================================\n\n /**\n * Get distinct values for a column (for filter dropdowns)\n * For array-type columns (like tags), each unique array combination is returned.\n * Arrays are sorted internally for consistent comparison.\n * Limited to maxValues to avoid performance issues with large datasets.\n */\n getDistinctValuesForColumn(colId: string, maxValues: number = 500): CellValue[] {\n const columns = this.options.getColumns();\n const column = columns.find(c => (c.colId ?? c.field) === colId);\n if (!column) return [];\n\n const cachedRows = this.options.getCachedRows();\n const valuesMap = new Map<string, CellValue>();\n\n for (const row of cachedRows.values()) {\n const value = getFieldValue(row, column.field);\n\n if (Array.isArray(value)) {\n // Sort array items internally for consistent comparison\n const sortedArray = [...value].sort((a, b) =>\n String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' })\n );\n const key = JSON.stringify(sortedArray);\n if (!valuesMap.has(key)) {\n valuesMap.set(key, sortedArray);\n if (valuesMap.size >= maxValues) break;\n }\n } else {\n const key = JSON.stringify(value);\n if (!valuesMap.has(key)) {\n valuesMap.set(key, value);\n if (valuesMap.size >= maxValues) break;\n }\n }\n }\n\n // Sort the results\n const results = Array.from(valuesMap.values());\n results.sort((a, b) => {\n const strA = Array.isArray(a) ? a.join(', ') : String(a ?? '');\n const strB = Array.isArray(b) ? b.join(', ') : String(b ?? '');\n return strA.localeCompare(strB, undefined, { numeric: true, sensitivity: 'base' });\n });\n\n return results;\n }\n\n // ===========================================================================\n // Filter Popup\n // ===========================================================================\n\n /**\n * Open filter popup for a column (toggles if already open for same column)\n */\n openFilterPopup(\n colIndex: number,\n anchorRect: { top: number; left: number; width: number; height: number }\n ): void {\n // If clicking on the same column's filter icon, close the popup\n if (this.openFilterColIndex === colIndex) {\n this.closeFilterPopup();\n return;\n }\n\n const columns = this.options.getColumns();\n const column = columns[colIndex];\n if (!column || !this.isColumnFilterable(colIndex)) return;\n\n const colId = column.colId ?? column.field;\n const distinctValues = this.getDistinctValuesForColumn(colId);\n\n this.openFilterColIndex = colIndex;\n this.emit({\n type: \"OPEN_FILTER_POPUP\",\n colIndex,\n column,\n anchorRect,\n distinctValues,\n currentFilter: this.filterModel[colId],\n });\n }\n\n /**\n * Close filter popup\n */\n closeFilterPopup(): void {\n this.openFilterColIndex = null;\n this.emit({ type: \"CLOSE_FILTER_POPUP\" });\n }\n\n // ===========================================================================\n // Header Info\n // ===========================================================================\n\n /**\n * Get sort info map for header rendering\n */\n getSortInfoMap(): Map<string, { direction: SortDirection; index: number }> {\n const sortInfoMap = new Map<string, { direction: SortDirection; index: number }>();\n this.sortModel.forEach((sort, index) => {\n sortInfoMap.set(sort.colId, { direction: sort.direction, index: index + 1 });\n });\n return sortInfoMap;\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n destroy(): void {\n this.emitter.clearListeners();\n this.sortModel = [];\n this.filterModel = {};\n this.openFilterColIndex = null;\n }\n}\n","// packages/core/src/row-mutation-manager.ts\n\nimport type { Row } from \"./types\";\nimport { createInstructionEmitter } from \"./utils\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface RowMutationManagerOptions<TData> {\n /** Get the cached rows map */\n getCachedRows: () => Map<number, TData>;\n /** Set the cached rows map (for bulk operations) */\n setCachedRows: (rows: Map<number, TData>) => void;\n /** Get total row count */\n getTotalRows: () => number;\n /** Set total row count */\n setTotalRows: (count: number) => void;\n /** Update a single slot after row change */\n updateSlot: (rowIndex: number) => void;\n /** Refresh all slots after bulk changes */\n refreshAllSlots: () => void;\n /** Emit content size change */\n emitContentSize: () => void;\n /** Clear selection if it references invalid rows */\n clearSelectionIfInvalid: (maxValidRow: number) => void;\n}\n\n// =============================================================================\n// RowMutationManager\n// =============================================================================\n\n/**\n * Manages row CRUD operations and cache management.\n */\nexport class RowMutationManager<TData extends Row = Row> {\n private options: RowMutationManagerOptions<TData>;\n private emitter = createInstructionEmitter();\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n private emit = this.emitter.emit;\n\n constructor(options: RowMutationManagerOptions<TData>) {\n this.options = options;\n }\n\n // ===========================================================================\n // Read Operations\n // ===========================================================================\n\n /**\n * Get a row by index.\n */\n getRow(index: number): TData | undefined {\n return this.options.getCachedRows().get(index);\n }\n\n // ===========================================================================\n // Write Operations\n // ===========================================================================\n\n /**\n * Add rows to the grid at the specified index.\n * If no index is provided, rows are added at the end.\n */\n addRows(rows: TData[], index?: number): void {\n if (rows.length === 0) return;\n\n const cachedRows = this.options.getCachedRows();\n const totalRows = this.options.getTotalRows();\n const insertIndex = index ?? totalRows;\n const newTotalRows = totalRows + rows.length;\n\n // Shift existing rows if inserting in the middle\n if (insertIndex < totalRows) {\n const newCache = new Map<number, TData>();\n for (const [rowIndex, rowData] of cachedRows) {\n if (rowIndex >= insertIndex) {\n newCache.set(rowIndex + rows.length, rowData);\n } else {\n newCache.set(rowIndex, rowData);\n }\n }\n this.options.setCachedRows(newCache);\n }\n\n // Insert new rows\n const currentCache = this.options.getCachedRows();\n rows.forEach((row, i) => {\n currentCache.set(insertIndex + i, row);\n });\n\n this.options.setTotalRows(newTotalRows);\n\n // Emit instruction and update UI\n const addedIndices = rows.map((_, i) => insertIndex + i);\n this.emit({\n type: \"ROWS_ADDED\",\n indices: addedIndices,\n count: addedIndices.length,\n totalRows: newTotalRows,\n });\n\n this.options.emitContentSize();\n this.options.refreshAllSlots();\n }\n\n /**\n * Update existing rows with partial data.\n */\n updateRows(updates: Array<{ index: number; data: Partial<TData> }>): void {\n if (updates.length === 0) return;\n\n const cachedRows = this.options.getCachedRows();\n const updatedIndices: number[] = [];\n\n for (const update of updates) {\n const existing = cachedRows.get(update.index);\n if (existing) {\n // Merge the update into existing row\n cachedRows.set(update.index, { ...existing, ...update.data });\n updatedIndices.push(update.index);\n }\n }\n\n if (updatedIndices.length === 0) return;\n\n // Emit instruction\n this.emit({\n type: \"ROWS_UPDATED\",\n indices: updatedIndices,\n });\n\n // Refresh only the affected slots\n for (const index of updatedIndices) {\n this.options.updateSlot(index);\n }\n }\n\n /**\n * Delete rows at the specified indices.\n */\n deleteRows(indices: number[]): void {\n if (indices.length === 0) return;\n\n const cachedRows = this.options.getCachedRows();\n let totalRows = this.options.getTotalRows();\n\n // Sort indices in descending order to handle shifts correctly\n const sortedIndices = [...indices].sort((a, b) => b - a);\n\n for (const index of sortedIndices) {\n if (index < 0 || index >= totalRows) continue;\n\n // Remove the row and shift subsequent rows\n cachedRows.delete(index);\n\n // Shift all rows after the deleted one\n const newCache = new Map<number, TData>();\n for (const [rowIndex, rowData] of cachedRows) {\n if (rowIndex > index) {\n newCache.set(rowIndex - 1, rowData);\n } else {\n newCache.set(rowIndex, rowData);\n }\n }\n this.options.setCachedRows(newCache);\n totalRows--;\n }\n\n this.options.setTotalRows(totalRows);\n\n // Clear selection if it references deleted rows\n this.options.clearSelectionIfInvalid(totalRows);\n\n // Emit instruction and update UI\n this.emit({\n type: \"ROWS_REMOVED\",\n indices: sortedIndices,\n totalRows,\n });\n\n this.options.emitContentSize();\n this.options.refreshAllSlots();\n }\n\n /**\n * Set a complete row at the specified index.\n * Use this for complete row replacement. For partial updates, use updateRows.\n */\n setRow(index: number, data: TData): void {\n const totalRows = this.options.getTotalRows();\n if (index < 0 || index >= totalRows) return;\n\n const cachedRows = this.options.getCachedRows();\n cachedRows.set(index, data);\n\n this.emit({\n type: \"ROWS_UPDATED\",\n indices: [index],\n });\n\n this.options.updateSlot(index);\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n destroy(): void {\n this.emitter.clearListeners();\n }\n}\n","// gp-grid-core/src/grid-core.ts\n\nimport type {\n GridCoreOptions,\n ColumnDefinition,\n CellValue,\n DataSource,\n DataSourceRequest,\n Row,\n SortModel,\n SortDirection,\n FilterModel,\n ColumnFilterModel,\n EditState,\n} from \"./types\";\nimport { SelectionManager } from \"./selection\";\nimport { FillManager } from \"./fill\";\nimport { SlotPoolManager } from \"./slot-pool\";\nimport { EditManager } from \"./edit-manager\";\nimport { InputHandler } from \"./input-handler\";\nimport { HighlightManager } from \"./highlight-manager\";\nimport { SortFilterManager } from \"./sort-filter-manager\";\nimport { RowMutationManager } from \"./row-mutation-manager\";\nimport {\n createBatchInstructionEmitter,\n getFieldValue,\n setFieldValue,\n} from \"./utils\";\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n// Maximum safe scroll height across browsers (conservative value)\n// Chrome/Edge: ~33.5M, Firefox: ~17.9M, Safari: ~33.5M\n// We use 10M to be safe and leave room for other content\nconst MAX_SCROLL_HEIGHT = 10_000_000;\n\n// =============================================================================\n// GridCore\n// =============================================================================\n\nexport class GridCore<TData extends Row = Row> {\n // Configuration\n private columns: ColumnDefinition[];\n private dataSource: DataSource<TData>;\n private rowHeight: number;\n private headerHeight: number;\n private overscan: number;\n private sortingEnabled: boolean;\n\n // Viewport state\n private scrollTop: number = 0;\n private scrollLeft: number = 0;\n private viewportWidth: number = 800;\n private viewportHeight: number = 600;\n\n // Data state\n private cachedRows: Map<number, TData> = new Map();\n private totalRows: number = 0;\n private currentPageIndex: number = 0;\n // Use large page size to avoid excessive pagination for client-side data sources\n private pageSize: number = 1000000;\n\n // Managers\n public readonly selection: SelectionManager;\n public readonly fill: FillManager;\n public readonly input: InputHandler<TData>;\n public readonly highlight: HighlightManager<TData> | null;\n public readonly sortFilter: SortFilterManager<TData>;\n public readonly rowMutation: RowMutationManager<TData>;\n private readonly slotPool: SlotPoolManager;\n private readonly editManager: EditManager;\n\n // Column positions (computed)\n private columnPositions: number[] = [];\n\n // Instruction emitter\n private emitter = createBatchInstructionEmitter();\n\n // Public API delegates to emitter\n onInstruction = this.emitter.onInstruction;\n onBatchInstruction = this.emitter.onBatchInstruction;\n private emit = this.emitter.emit;\n private emitBatch = this.emitter.emitBatch;\n\n // Scroll virtualization state\n private naturalContentHeight: number = 0;\n private virtualContentHeight: number = 0;\n private scrollRatio: number = 1;\n\n // Lifecycle state\n private isDestroyed: boolean = false;\n\n constructor(options: GridCoreOptions<TData>) {\n this.columns = options.columns;\n this.dataSource = options.dataSource;\n this.rowHeight = options.rowHeight;\n this.headerHeight = options.headerHeight ?? options.rowHeight;\n this.overscan = options.overscan ?? 3;\n this.sortingEnabled = options.sortingEnabled ?? true;\n\n this.computeColumnPositions();\n\n // Initialize selection manager\n this.selection = new SelectionManager({\n getRowCount: () => this.totalRows,\n getColumnCount: () => this.columns.length,\n getCellValue: (row, col) => this.getCellValue(row, col),\n getRowData: (row) => this.cachedRows.get(row),\n getColumn: (col) => this.columns[col],\n });\n\n // Forward selection instructions\n this.selection.onInstruction((instruction) => {\n this.emit(instruction);\n // Notify highlight manager of selection changes\n this.highlight?.onSelectionChange();\n });\n\n // Initialize highlight manager (only if highlighting options provided)\n if (options.highlighting) {\n this.highlight = new HighlightManager<TData>(\n {\n getActiveCell: () => this.selection.getActiveCell(),\n getSelectionRange: () => this.selection.getSelectionRange(),\n getColumn: (colIndex) => this.columns[colIndex],\n },\n options.highlighting,\n );\n\n // Forward highlight instructions\n this.highlight.onInstruction((instruction) => this.emit(instruction));\n } else {\n this.highlight = null;\n }\n\n // Initialize fill manager\n this.fill = new FillManager({\n getRowCount: () => this.totalRows,\n getColumnCount: () => this.columns.length,\n getCellValue: (row, col) => this.getCellValue(row, col),\n getColumn: (col) => this.columns[col],\n setCellValue: (row, col, value) => this.setCellValue(row, col, value),\n });\n\n // Forward fill instructions\n this.fill.onInstruction((instruction) => this.emit(instruction));\n\n // Initialize slot pool manager\n this.slotPool = new SlotPoolManager({\n getRowHeight: () => this.rowHeight,\n getHeaderHeight: () => this.headerHeight,\n getOverscan: () => this.overscan,\n getScrollTop: () => this.scrollTop,\n getViewportHeight: () => this.viewportHeight,\n getTotalRows: () => this.totalRows,\n getScrollRatio: () => this.scrollRatio,\n getVirtualContentHeight: () => this.virtualContentHeight,\n getRowData: (rowIndex) => this.cachedRows.get(rowIndex),\n });\n\n // Forward slot pool instructions (use batch listener for performance)\n this.slotPool.onBatchInstruction((instructions) => this.emitBatch(instructions));\n\n // Initialize edit manager\n this.editManager = new EditManager({\n getColumn: (col) => this.columns[col],\n getCellValue: (row, col) => this.getCellValue(row, col),\n setCellValue: (row, col, value) => this.setCellValue(row, col, value),\n onCommit: (row) => {\n // Update the slot displaying this row after edit commit\n this.slotPool.updateSlot(row);\n },\n });\n\n // Forward edit manager instructions\n this.editManager.onInstruction((instruction) => this.emit(instruction));\n\n // Initialize sort/filter manager\n this.sortFilter = new SortFilterManager<TData>({\n getColumns: () => this.columns,\n isSortingEnabled: () => this.sortingEnabled,\n getCachedRows: () => this.cachedRows,\n onSortFilterChange: async () => {\n await this.fetchData();\n this.highlight?.clearAllCaches();\n this.slotPool.refreshAllSlots();\n },\n onDataRefreshed: () => {\n this.emitContentSize();\n this.emitHeaders();\n },\n });\n\n // Forward sort/filter instructions\n this.sortFilter.onInstruction((instruction) => this.emit(instruction));\n\n // Initialize row mutation manager\n this.rowMutation = new RowMutationManager<TData>({\n getCachedRows: () => this.cachedRows,\n setCachedRows: (rows) => { this.cachedRows = rows; },\n getTotalRows: () => this.totalRows,\n setTotalRows: (count) => { this.totalRows = count; },\n updateSlot: (rowIndex) => this.slotPool.updateSlot(rowIndex),\n refreshAllSlots: () => this.slotPool.refreshAllSlots(),\n emitContentSize: () => this.emitContentSize(),\n clearSelectionIfInvalid: (maxValidRow) => {\n const activeCell = this.selection.getActiveCell();\n if (activeCell && activeCell.row >= maxValidRow) {\n this.selection.clearSelection();\n }\n },\n });\n\n // Forward row mutation instructions\n this.rowMutation.onInstruction((instruction) => this.emit(instruction));\n\n // Initialize input handler\n this.input = new InputHandler(this, {\n getHeaderHeight: () => this.headerHeight,\n getRowHeight: () => this.rowHeight,\n getColumnPositions: () => this.columnPositions,\n getColumnCount: () => this.columns.length,\n });\n }\n\n // ===========================================================================\n // Initialization\n // ===========================================================================\n\n /**\n * Initialize the grid and load initial data.\n */\n async initialize(): Promise<void> {\n await this.fetchData();\n this.slotPool.syncSlots();\n this.emitContentSize();\n this.emitHeaders();\n }\n\n // ===========================================================================\n // Viewport Management\n // ===========================================================================\n\n /**\n * Update viewport measurements and sync slots.\n * When scroll virtualization is active, maps the DOM scroll position to the actual row position.\n */\n setViewport(\n scrollTop: number,\n scrollLeft: number,\n width: number,\n height: number\n ): void {\n // When scroll ratio < 1, map the visual scroll position to actual content position\n // scrollTop is the browser's reported scroll position within the virtual container\n // We need to convert this to determine which row should be at the top of the viewport\n const effectiveScrollTop = this.scrollRatio < 1\n ? scrollTop / this.scrollRatio\n : scrollTop;\n\n const viewportSizeChanged =\n this.viewportWidth !== width ||\n this.viewportHeight !== height;\n\n const changed =\n this.scrollTop !== effectiveScrollTop ||\n this.scrollLeft !== scrollLeft ||\n viewportSizeChanged;\n\n if (!changed) return;\n\n this.scrollTop = effectiveScrollTop;\n this.scrollLeft = scrollLeft;\n this.viewportWidth = width;\n this.viewportHeight = height;\n\n this.slotPool.syncSlots();\n\n // Emit visible range update so React (or other frameworks) can track it\n const visibleRange = this.getVisibleRowRange();\n this.emit({\n type: \"UPDATE_VISIBLE_RANGE\",\n start: visibleRange.start,\n end: visibleRange.end,\n });\n\n // Emit content size when viewport size changes (for column scaling)\n if (viewportSizeChanged) {\n this.emitContentSize();\n }\n }\n\n // ===========================================================================\n // Data Fetching\n // ===========================================================================\n\n private async fetchData(): Promise<void> {\n this.emit({ type: \"DATA_LOADING\" });\n\n try {\n const sortModel = this.sortFilter.getSortModel();\n const filterModel = this.sortFilter.getFilterModel();\n\n const request: DataSourceRequest = {\n pagination: {\n pageIndex: this.currentPageIndex,\n pageSize: this.pageSize,\n },\n sort: sortModel.length > 0 ? sortModel : undefined,\n filter: Object.keys(filterModel).length > 0 ? filterModel : undefined,\n };\n\n const response = await this.dataSource.fetch(request);\n\n // Cache the fetched rows\n this.cachedRows.clear();\n response.rows.forEach((row, index) => {\n this.cachedRows.set(this.currentPageIndex * this.pageSize + index, row);\n });\n\n this.totalRows = response.totalRows;\n\n // For client-side data source, fetch all data for simplicity\n // (the client data source handles filtering/sorting internally)\n if (response.totalRows > response.rows.length && this.currentPageIndex === 0) {\n // Fetch all remaining pages for client-side mode\n await this.fetchAllData();\n }\n\n this.emit({ type: \"DATA_LOADED\", totalRows: this.totalRows });\n } catch (error) {\n this.emit({\n type: \"DATA_ERROR\",\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n private async fetchAllData(): Promise<void> {\n // Fetch all data in chunks for client-side data source\n const totalPages = Math.ceil(this.totalRows / this.pageSize);\n const sortModel = this.sortFilter.getSortModel();\n const filterModel = this.sortFilter.getFilterModel();\n\n for (let page = 1; page < totalPages; page++) {\n const request: DataSourceRequest = {\n pagination: {\n pageIndex: page,\n pageSize: this.pageSize,\n },\n sort: sortModel.length > 0 ? sortModel : undefined,\n filter: Object.keys(filterModel).length > 0 ? filterModel : undefined,\n };\n\n const response = await this.dataSource.fetch(request);\n response.rows.forEach((row, index) => {\n this.cachedRows.set(page * this.pageSize + index, row);\n });\n }\n }\n\n // ===========================================================================\n // Sort & Filter (delegates to SortFilterManager)\n // ===========================================================================\n\n async setSort(\n colId: string,\n direction: SortDirection | null,\n addToExisting: boolean = false\n ): Promise<void> {\n return this.sortFilter.setSort(colId, direction, addToExisting);\n }\n\n async setFilter(colId: string, filter: ColumnFilterModel | string | null): Promise<void> {\n return this.sortFilter.setFilter(colId, filter);\n }\n\n hasActiveFilter(colId: string): boolean {\n return this.sortFilter.hasActiveFilter(colId);\n }\n\n isColumnSortable(colIndex: number): boolean {\n return this.sortFilter.isColumnSortable(colIndex);\n }\n\n isColumnFilterable(colIndex: number): boolean {\n return this.sortFilter.isColumnFilterable(colIndex);\n }\n\n getDistinctValuesForColumn(colId: string, maxValues: number = 500): CellValue[] {\n return this.sortFilter.getDistinctValuesForColumn(colId, maxValues);\n }\n\n openFilterPopup(colIndex: number, anchorRect: { top: number; left: number; width: number; height: number }): void {\n this.sortFilter.openFilterPopup(colIndex, anchorRect);\n }\n\n closeFilterPopup(): void {\n this.sortFilter.closeFilterPopup();\n }\n\n getSortModel(): SortModel[] {\n return this.sortFilter.getSortModel();\n }\n\n getFilterModel(): FilterModel {\n return this.sortFilter.getFilterModel();\n }\n\n // ===========================================================================\n // Editing\n // ===========================================================================\n\n startEdit(row: number, col: number): void {\n this.editManager.startEdit(row, col);\n }\n\n updateEditValue(value: CellValue): void {\n this.editManager.updateValue(value);\n }\n\n commitEdit(): void {\n this.editManager.commit();\n }\n\n cancelEdit(): void {\n this.editManager.cancel();\n }\n\n getEditState(): EditState | null {\n return this.editManager.getState();\n }\n\n // ===========================================================================\n // Cell Value Access\n // ===========================================================================\n\n getCellValue(row: number, col: number): CellValue {\n const rowData = this.cachedRows.get(row);\n if (!rowData) return null;\n\n const column = this.columns[col];\n if (!column) return null;\n\n return getFieldValue(rowData, column.field);\n }\n\n setCellValue(row: number, col: number, value: CellValue): void {\n const rowData = this.cachedRows.get(row);\n if (!rowData || typeof rowData !== \"object\") return;\n\n const column = this.columns[col];\n if (!column) return;\n\n setFieldValue(rowData as Record<string, unknown>, column.field, value);\n }\n\n // ===========================================================================\n // Layout Helpers\n // ===========================================================================\n\n private computeColumnPositions(): void {\n this.columnPositions = [0];\n let pos = 0;\n for (const col of this.columns) {\n // Only include visible columns in content width calculation\n if (!col.hidden) {\n pos += col.width;\n this.columnPositions.push(pos);\n }\n }\n }\n\n private emitContentSize(): void {\n const width = this.columnPositions[this.columnPositions.length - 1] ?? 0;\n \n // Calculate natural (real) content height\n this.naturalContentHeight = this.totalRows * this.rowHeight + this.headerHeight;\n \n // Apply scroll virtualization if content exceeds browser limits\n if (this.naturalContentHeight > MAX_SCROLL_HEIGHT) {\n this.virtualContentHeight = MAX_SCROLL_HEIGHT;\n this.scrollRatio = MAX_SCROLL_HEIGHT / this.naturalContentHeight;\n } else {\n this.virtualContentHeight = this.naturalContentHeight;\n this.scrollRatio = 1;\n }\n \n this.emit({\n type: \"SET_CONTENT_SIZE\",\n width,\n height: this.virtualContentHeight,\n viewportWidth: this.viewportWidth,\n });\n }\n\n private emitHeaders(): void {\n const sortInfoMap = this.sortFilter.getSortInfoMap();\n\n for (let i = 0; i < this.columns.length; i++) {\n const column = this.columns[i]!;\n const colId = column.colId ?? column.field;\n const sortInfo = sortInfoMap.get(colId);\n\n this.emit({\n type: \"UPDATE_HEADER\",\n colIndex: i,\n column,\n sortDirection: sortInfo?.direction,\n sortIndex: sortInfo?.index,\n sortable: this.sortFilter.isColumnSortable(i),\n filterable: this.sortFilter.isColumnFilterable(i),\n hasFilter: this.sortFilter.hasActiveFilter(colId),\n });\n }\n }\n\n // ===========================================================================\n // Public Accessors\n // ===========================================================================\n\n getColumns(): ColumnDefinition[] {\n return this.columns;\n }\n\n getColumnPositions(): number[] {\n return [...this.columnPositions];\n }\n\n getRowCount(): number {\n return this.totalRows;\n }\n\n getRowHeight(): number {\n return this.rowHeight;\n }\n\n getHeaderHeight(): number {\n return this.headerHeight;\n }\n\n getTotalWidth(): number {\n return this.columnPositions[this.columnPositions.length - 1] ?? 0;\n }\n\n getTotalHeight(): number {\n // Return the virtual (capped) height for external use\n return this.virtualContentHeight || (this.totalRows * this.rowHeight + this.headerHeight);\n }\n\n /**\n * Check if scroll scaling is active (large datasets exceeding browser scroll limits).\n * When scaling is active, scrollRatio < 1 and scroll positions are compressed.\n */\n isScalingActive(): boolean {\n return this.scrollRatio < 1;\n }\n\n /**\n * Get the natural (uncapped) content height.\n * Useful for debugging or displaying actual content size.\n */\n getNaturalHeight(): number {\n return this.naturalContentHeight || (this.totalRows * this.rowHeight + this.headerHeight);\n }\n\n /**\n * Get the scroll ratio used for scroll virtualization.\n * Returns 1 when no virtualization is needed, < 1 when content exceeds browser limits.\n */\n getScrollRatio(): number {\n return this.scrollRatio;\n }\n\n /**\n * Get the visible row range (excluding overscan).\n * Returns the first and last row indices that are actually visible in the viewport.\n * Includes partially visible rows to avoid false positives when clicking on edge rows.\n */\n getVisibleRowRange(): { start: number; end: number } {\n // viewportHeight includes header, so subtract it to get content area\n const contentHeight = this.viewportHeight - this.headerHeight;\n const firstVisibleRow = Math.max(0, Math.floor(this.scrollTop / this.rowHeight));\n // Use ceil and subtract 1 to include any partially visible row at the bottom\n const lastVisibleRow = Math.min(\n this.totalRows - 1,\n Math.ceil((this.scrollTop + contentHeight) / this.rowHeight) - 1\n );\n return { start: firstVisibleRow, end: Math.max(firstVisibleRow, lastVisibleRow) };\n }\n\n /**\n * Get the scroll position needed to bring a row into view.\n * Accounts for scroll scaling when active.\n */\n getScrollTopForRow(rowIndex: number): number {\n const naturalScrollTop = rowIndex * this.rowHeight;\n // Apply scroll ratio to convert natural position to virtual scroll position\n return naturalScrollTop * this.scrollRatio;\n }\n\n /**\n * Get the row index at a given viewport Y position.\n * Accounts for scroll scaling when active.\n * @param viewportY Y position in viewport (physical pixels below header, NOT including scroll)\n * @param virtualScrollTop Current scroll position from container.scrollTop (virtual/scaled)\n */\n getRowIndexAtDisplayY(viewportY: number, virtualScrollTop: number): number {\n // Convert virtual scroll position to natural position\n const naturalScrollTop = this.scrollRatio < 1\n ? virtualScrollTop / this.scrollRatio\n : virtualScrollTop;\n\n // Natural Y = viewport offset + natural scroll position\n const naturalY = viewportY + naturalScrollTop;\n return Math.floor(naturalY / this.rowHeight);\n }\n\n getRowData(rowIndex: number): TData | undefined {\n return this.cachedRows.get(rowIndex);\n }\n\n // ===========================================================================\n // Data Updates\n // ===========================================================================\n\n /**\n * Refresh data from the data source.\n */\n async refresh(): Promise<void> {\n await this.fetchData();\n // Clear highlight caches since row data may have changed\n this.highlight?.clearAllCaches();\n // Use refreshAllSlots instead of syncSlots to ensure all slot data is updated\n // This is important when data changes (e.g., rows added) but visible indices stay the same\n this.slotPool.refreshAllSlots();\n this.emitContentSize();\n\n // Emit visible range since totalRows may have changed\n const visibleRange = this.getVisibleRowRange();\n this.emit({\n type: \"UPDATE_VISIBLE_RANGE\",\n start: visibleRange.start,\n end: visibleRange.end,\n });\n }\n\n /**\n * Refresh slot display without refetching data.\n * Useful after in-place data modifications like fill operations.\n */\n refreshSlotData(): void {\n this.slotPool.refreshAllSlots();\n }\n\n // ===========================================================================\n // Row Mutation API (delegates to RowMutationManager)\n // ===========================================================================\n\n /**\n * Add rows to the grid at the specified index.\n * If no index is provided, rows are added at the end.\n */\n addRows(rows: TData[], index?: number): void {\n this.rowMutation.addRows(rows, index);\n }\n\n /**\n * Update existing rows with partial data.\n */\n updateRows(updates: Array<{ index: number; data: Partial<TData> }>): void {\n this.rowMutation.updateRows(updates);\n }\n\n /**\n * Delete rows at the specified indices.\n */\n deleteRows(indices: number[]): void {\n this.rowMutation.deleteRows(indices);\n }\n\n /**\n * Get a row by index.\n */\n getRow(index: number): TData | undefined {\n return this.rowMutation.getRow(index);\n }\n\n /**\n * Set a complete row at the specified index.\n * Use this for complete row replacement. For partial updates, use updateRows.\n */\n setRow(index: number, data: TData): void {\n this.rowMutation.setRow(index, data);\n }\n\n /**\n * Update the data source and refresh.\n */\n async setDataSource(dataSource: DataSource<TData>): Promise<void> {\n this.dataSource = dataSource;\n await this.refresh();\n }\n\n /**\n * Update columns and recompute layout.\n */\n setColumns(columns: ColumnDefinition[]): void {\n this.columns = columns;\n this.computeColumnPositions();\n this.emitContentSize();\n this.emitHeaders();\n this.slotPool.syncSlots();\n }\n\n /**\n * Destroy the grid core and release all references.\n * Call this before discarding the GridCore to ensure proper cleanup.\n * This method is idempotent - safe to call multiple times.\n */\n destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n // Destroy child managers\n this.slotPool.destroy();\n this.highlight?.destroy();\n this.sortFilter.destroy();\n this.rowMutation.destroy();\n\n // Clear cached row data (can be large for big datasets)\n this.cachedRows.clear();\n\n // Clear listeners\n this.emitter.clearListeners();\n\n // Reset state\n this.totalRows = 0;\n }\n}\n\n","// packages/core/src/sorting/worker-pool.ts\n// Manages a pool of Web Workers for parallel operations\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface WorkerPoolOptions {\n /** Maximum number of workers (default: navigator.hardwareConcurrency ?? 4) */\n maxWorkers?: number;\n /** Whether to pre-warm workers on initialization */\n preWarm?: boolean;\n}\n\ninterface PendingTask<TResponse> {\n resolve: (value: TResponse) => void;\n reject: (error: Error) => void;\n}\n\ninterface WorkerState {\n worker: Worker;\n busy: boolean;\n pendingRequests: Map<number, PendingTask<unknown>>;\n}\n\n// =============================================================================\n// WorkerPool\n// =============================================================================\n\n/**\n * Manages a pool of Web Workers for parallel task execution.\n * Workers are created lazily and reused across operations.\n */\nexport class WorkerPool {\n private workerCode: string;\n private maxWorkers: number;\n private workers: WorkerState[] = [];\n private workerUrl: string | null = null;\n private nextRequestId = 0;\n private isTerminated = false;\n\n constructor(workerCode: string, options: WorkerPoolOptions = {}) {\n this.workerCode = workerCode;\n this.maxWorkers =\n options.maxWorkers ??\n (typeof navigator !== \"undefined\" ? navigator.hardwareConcurrency : 4) ??\n 4;\n\n if (options.preWarm) {\n this.preWarmWorkers();\n }\n }\n\n /**\n * Get the current pool size (number of active workers).\n */\n getPoolSize(): number {\n return this.workers.length;\n }\n\n /**\n * Get the maximum pool size.\n */\n getMaxWorkers(): number {\n return this.maxWorkers;\n }\n\n /**\n * Check if the pool is available for use.\n */\n isAvailable(): boolean {\n return !this.isTerminated && typeof Worker !== \"undefined\";\n }\n\n /**\n * Execute a single task on an available worker.\n * Returns the worker's response.\n */\n async execute<TRequest extends { id?: number }, TResponse>(\n request: TRequest,\n transferables?: Transferable[],\n ): Promise<TResponse> {\n if (this.isTerminated) {\n throw new Error(\"WorkerPool has been terminated\");\n }\n\n if (typeof Worker === \"undefined\") {\n throw new Error(\"Web Workers are not available in this environment\");\n }\n\n const worker = this.getAvailableWorker();\n const id = this.nextRequestId++;\n const requestWithId = { ...request, id };\n\n return new Promise((resolve, reject) => {\n worker.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n });\n worker.busy = true;\n\n if (transferables && transferables.length > 0) {\n worker.worker.postMessage(requestWithId, transferables);\n } else {\n worker.worker.postMessage(requestWithId);\n }\n });\n }\n\n /**\n * Execute multiple tasks in parallel across available workers.\n * Each task is assigned to a different worker if possible.\n * Returns results in the same order as the input requests.\n */\n async executeParallel<TRequest extends { id?: number }, TResponse>(\n tasks: Array<{ request: TRequest; transferables?: Transferable[] }>,\n ): Promise<TResponse[]> {\n if (this.isTerminated) {\n throw new Error(\"WorkerPool has been terminated\");\n }\n\n if (tasks.length === 0) {\n return [];\n }\n\n // Ensure we have enough workers for parallel execution\n const numWorkers = Math.min(tasks.length, this.maxWorkers);\n this.ensureWorkers(numWorkers);\n\n // Create promises for all tasks\n const promises = tasks.map((task, index) => {\n const workerIndex = index % this.workers.length;\n const worker = this.workers[workerIndex]!;\n const id = this.nextRequestId++;\n const requestWithId = { ...task.request, id };\n\n return new Promise<TResponse>((resolve, reject) => {\n worker.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n });\n worker.busy = true;\n\n if (task.transferables && task.transferables.length > 0) {\n worker.worker.postMessage(requestWithId, task.transferables);\n } else {\n worker.worker.postMessage(requestWithId);\n }\n });\n });\n\n return Promise.all(promises);\n }\n\n /**\n * Terminate all workers and clean up resources.\n */\n terminate(): void {\n for (const workerState of this.workers) {\n workerState.worker.terminate();\n\n // Reject any pending requests\n for (const [, pending] of workerState.pendingRequests) {\n pending.reject(new Error(\"Worker pool terminated\"));\n }\n workerState.pendingRequests.clear();\n }\n this.workers = [];\n\n if (this.workerUrl) {\n URL.revokeObjectURL(this.workerUrl);\n this.workerUrl = null;\n }\n\n this.isTerminated = true;\n }\n\n /**\n * Pre-warm workers by creating them ahead of time.\n */\n private preWarmWorkers(): void {\n this.ensureWorkers(this.maxWorkers);\n }\n\n /**\n * Ensure at least `count` workers exist in the pool.\n */\n private ensureWorkers(count: number): void {\n const needed = Math.min(count, this.maxWorkers) - this.workers.length;\n for (let i = 0; i < needed; i++) {\n this.createWorker();\n }\n }\n\n /**\n * Get an available worker, creating one if needed.\n */\n private getAvailableWorker(): WorkerState {\n // First, try to find an idle worker\n const idleWorker = this.workers.find((w) => !w.busy);\n if (idleWorker) {\n return idleWorker;\n }\n\n // If we can create more workers, do so\n if (this.workers.length < this.maxWorkers) {\n return this.createWorker();\n }\n\n // Otherwise, use round-robin on existing workers\n // (they can queue multiple requests)\n const leastBusy = this.workers.reduce((min, w) =>\n w.pendingRequests.size < min.pendingRequests.size ? w : min,\n );\n return leastBusy;\n }\n\n /**\n * Create a new worker and add it to the pool.\n */\n private createWorker(): WorkerState {\n // Create blob URL once and reuse\n if (!this.workerUrl) {\n const blob = new Blob([this.workerCode], {\n type: \"application/javascript\",\n });\n this.workerUrl = URL.createObjectURL(blob);\n }\n\n const worker = new Worker(this.workerUrl);\n const workerState: WorkerState = {\n worker,\n busy: false,\n pendingRequests: new Map(),\n };\n\n worker.onmessage = (e: MessageEvent) => {\n const { id } = e.data;\n const pending = workerState.pendingRequests.get(id);\n\n if (pending) {\n workerState.pendingRequests.delete(id);\n\n if (workerState.pendingRequests.size === 0) {\n workerState.busy = false;\n }\n\n if (e.data.type === \"error\") {\n pending.reject(new Error(e.data.error));\n } else {\n pending.resolve(e.data);\n }\n }\n };\n\n worker.onerror = (error) => {\n // Reject all pending requests for this worker\n for (const [, pending] of workerState.pendingRequests) {\n pending.reject(new Error(`Worker error: ${error.message}`));\n }\n workerState.pendingRequests.clear();\n workerState.busy = false;\n\n // Try to respawn the worker\n this.respawnWorker(workerState);\n };\n\n this.workers.push(workerState);\n return workerState;\n }\n\n /**\n * Respawn a failed worker.\n */\n private respawnWorker(failedWorker: WorkerState): void {\n const index = this.workers.indexOf(failedWorker);\n if (index === -1) return;\n\n try {\n failedWorker.worker.terminate();\n } catch {\n // Ignore termination errors\n }\n\n // Remove the failed worker\n this.workers.splice(index, 1);\n\n // Create a new one if we're under capacity\n if (this.workers.length < this.maxWorkers && !this.isTerminated) {\n this.createWorker();\n }\n }\n}\n","// gp-grid-core/src/sorting/sort-worker.ts\n// Web Worker for sorting large datasets off the main thread\n\nimport type { SortModel, CellValue } from \"../types\";\n\n// =============================================================================\n// Worker Message Types\n// =============================================================================\n\nexport interface SortWorkerRequest {\n type: \"sort\";\n id: number;\n data: unknown[];\n sortModel: SortModel[];\n}\n\nexport interface SortIndicesRequest {\n type: \"sortIndices\";\n id: number;\n values: Float64Array;\n direction: \"asc\" | \"desc\";\n}\n\nexport interface SortMultiColumnRequest {\n type: \"sortMultiColumn\";\n id: number;\n /** Array of column values, each as Float64Array */\n columns: Float64Array[];\n /** Direction for each column: 1 for asc, -1 for desc */\n directions: Int8Array;\n}\n\nexport interface SortWorkerResponse {\n type: \"sorted\";\n id: number;\n data: unknown[];\n}\n\nexport interface SortIndicesResponse {\n type: \"sortedIndices\";\n id: number;\n indices: Uint32Array;\n}\n\nexport interface SortMultiColumnResponse {\n type: \"sortedMultiColumn\";\n id: number;\n indices: Uint32Array;\n}\n\nexport interface SortStringHashesRequest {\n type: \"sortStringHashes\";\n id: number;\n /** Array of hash chunks: [chunk0Values, chunk1Values, chunk2Values] */\n hashChunks: Float64Array[];\n direction: \"asc\" | \"desc\";\n}\n\nexport interface SortStringHashesResponse {\n type: \"sortedStringHashes\";\n id: number;\n indices: Uint32Array;\n /** Collision runs: [start1, end1, start2, end2, ...] for runs of identical hashes */\n collisionRuns: Uint32Array;\n}\n\n// =============================================================================\n// Chunk-aware Message Types (for parallel sorting)\n// =============================================================================\n\nexport interface SortChunkRequest {\n type: \"sortChunk\";\n id: number;\n values: Float64Array;\n direction: \"asc\" | \"desc\";\n /** Offset of this chunk in the original array */\n chunkOffset: number;\n}\n\nexport interface SortChunkResponse {\n type: \"sortedChunk\";\n id: number;\n /** Sorted indices (local to this chunk) */\n indices: Uint32Array;\n /** Sorted values (reordered to match indices) */\n sortedValues: Float64Array;\n /** Offset echoed back for merge coordination */\n chunkOffset: number;\n}\n\nexport interface SortStringChunkRequest {\n type: \"sortStringChunk\";\n id: number;\n hashChunks: Float64Array[];\n direction: \"asc\" | \"desc\";\n chunkOffset: number;\n}\n\nexport interface SortStringChunkResponse {\n type: \"sortedStringChunk\";\n id: number;\n indices: Uint32Array;\n /** Sorted hash values for merge comparison (first hash chunk only for efficiency) */\n sortedHashes: Float64Array;\n collisionRuns: Uint32Array;\n chunkOffset: number;\n}\n\nexport interface SortMultiColumnChunkRequest {\n type: \"sortMultiColumnChunk\";\n id: number;\n columns: Float64Array[];\n directions: Int8Array;\n chunkOffset: number;\n}\n\nexport interface SortMultiColumnChunkResponse {\n type: \"sortedMultiColumnChunk\";\n id: number;\n indices: Uint32Array;\n /** Sorted column values for merge (reordered to match indices) */\n sortedColumns: Float64Array[];\n chunkOffset: number;\n}\n\n// =============================================================================\n// Sorting Functions (duplicated from data-source.ts for worker isolation)\n// =============================================================================\n\nfunction getFieldValue(row: unknown, field: string): CellValue {\n const parts = field.split(\".\");\n let value: unknown = row;\n\n for (const part of parts) {\n if (value == null || typeof value !== \"object\") {\n return null;\n }\n value = (value as Record<string, unknown>)[part];\n }\n\n return (value ?? null) as CellValue;\n}\n\nfunction compareValues(a: CellValue, b: CellValue): number {\n // Null handling\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n\n // Numeric comparison\n const aNum = Number(a);\n const bNum = Number(b);\n if (!isNaN(aNum) && !isNaN(bNum)) {\n return aNum - bNum;\n }\n\n // Date comparison\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // String comparison\n return String(a).localeCompare(String(b));\n}\n\nfunction sortData<T>(data: T[], sortModel: SortModel[]): T[] {\n return [...data].sort((a, b) => {\n for (const { colId, direction } of sortModel) {\n const aVal = getFieldValue(a, colId);\n const bVal = getFieldValue(b, colId);\n const comparison = compareValues(aVal, bVal);\n\n if (comparison !== 0) {\n return direction === \"asc\" ? comparison : -comparison;\n }\n }\n return 0;\n });\n}\n\n// =============================================================================\n// Worker Code String (for inline worker creation)\n// =============================================================================\n\n/**\n * Inline worker code as a string for Blob URL creation.\n * This allows the worker to function without bundler-specific configuration.\n */\nexport const SORT_WORKER_CODE = `\n// Inline sort worker code\nfunction getFieldValue(row, field) {\n const parts = field.split(\".\");\n let value = row;\n\n for (const part of parts) {\n if (value == null || typeof value !== \"object\") {\n return null;\n }\n value = value[part];\n }\n\n return value ?? null;\n}\n\nfunction compareValues(a, b) {\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n\n const aNum = Number(a);\n const bNum = Number(b);\n if (!isNaN(aNum) && !isNaN(bNum)) {\n return aNum - bNum;\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n return String(a).localeCompare(String(b));\n}\n\nfunction sortData(data, sortModel) {\n return [...data].sort((a, b) => {\n for (const { colId, direction } of sortModel) {\n const aVal = getFieldValue(a, colId);\n const bVal = getFieldValue(b, colId);\n const comparison = compareValues(aVal, bVal);\n\n if (comparison !== 0) {\n return direction === \"asc\" ? comparison : -comparison;\n }\n }\n return 0;\n });\n}\n\n// Index-based sorting - much faster for large datasets\n// Uses Transferable typed arrays for zero-copy transfer\nfunction sortIndices(values, direction) {\n const len = values.length;\n const indices = new Uint32Array(len);\n for (let i = 0; i < len; i++) indices[i] = i;\n\n // Sort indices by values\n const mult = direction === \"asc\" ? 1 : -1;\n indices.sort((a, b) => {\n const va = values[a];\n const vb = values[b];\n if (va < vb) return -1 * mult;\n if (va > vb) return 1 * mult;\n return 0;\n });\n\n return indices;\n}\n\n// Multi-column sorting - compares columns in priority order\nfunction sortMultiColumn(columns, directions) {\n const len = columns[0].length;\n const numCols = columns.length;\n const indices = new Uint32Array(len);\n for (let i = 0; i < len; i++) indices[i] = i;\n\n indices.sort((a, b) => {\n for (let c = 0; c < numCols; c++) {\n const va = columns[c][a];\n const vb = columns[c][b];\n if (va < vb) return -1 * directions[c];\n if (va > vb) return 1 * directions[c];\n // Equal - continue to next column\n }\n return 0;\n });\n\n return indices;\n}\n\n// String hash sorting with collision detection\nfunction sortStringHashes(hashChunks, direction) {\n const len = hashChunks[0].length;\n const numChunks = hashChunks.length;\n const indices = new Uint32Array(len);\n for (let i = 0; i < len; i++) indices[i] = i;\n\n const mult = direction === \"asc\" ? 1 : -1;\n\n // Sort by hash only (no collision tracking during sort)\n indices.sort((a, b) => {\n for (let c = 0; c < numChunks; c++) {\n const va = hashChunks[c][a];\n const vb = hashChunks[c][b];\n if (va < vb) return -1 * mult;\n if (va > vb) return 1 * mult;\n }\n return 0; // Stable sort preserves original order\n });\n\n // Detect collision runs AFTER sorting (much more efficient)\n // Find runs of consecutive indices with identical hashes\n const collisionRuns = []; // [start1, end1, start2, end2, ...]\n let runStart = 0;\n\n for (let i = 1; i <= len; i++) {\n // Check if current element has different hash than previous\n let isDifferent = i === len; // End of array is always \"different\"\n if (!isDifferent) {\n const prevIdx = indices[i - 1];\n const currIdx = indices[i];\n for (let c = 0; c < numChunks; c++) {\n if (hashChunks[c][prevIdx] !== hashChunks[c][currIdx]) {\n isDifferent = true;\n break;\n }\n }\n }\n\n if (isDifferent) {\n // End of a run - only record if run has more than 1 element\n if (i - runStart > 1) {\n collisionRuns.push(runStart, i);\n }\n runStart = i;\n }\n }\n\n return { indices, collisionRuns: new Uint32Array(collisionRuns) };\n}\n\n// Chunk-aware sorting for parallel execution\nfunction sortChunk(values, direction) {\n const len = values.length;\n const indices = new Uint32Array(len);\n for (let i = 0; i < len; i++) indices[i] = i;\n\n const mult = direction === \"asc\" ? 1 : -1;\n indices.sort((a, b) => {\n const va = values[a];\n const vb = values[b];\n if (va < vb) return -1 * mult;\n if (va > vb) return 1 * mult;\n return 0;\n });\n\n // Create sorted values array for merge comparison\n const sortedValues = new Float64Array(len);\n for (let i = 0; i < len; i++) {\n sortedValues[i] = values[indices[i]];\n }\n\n return { indices, sortedValues };\n}\n\nfunction sortStringChunk(hashChunks, direction) {\n const len = hashChunks[0].length;\n const numChunks = hashChunks.length;\n const indices = new Uint32Array(len);\n for (let i = 0; i < len; i++) indices[i] = i;\n\n const mult = direction === \"asc\" ? 1 : -1;\n\n indices.sort((a, b) => {\n for (let c = 0; c < numChunks; c++) {\n const va = hashChunks[c][a];\n const vb = hashChunks[c][b];\n if (va < vb) return -1 * mult;\n if (va > vb) return 1 * mult;\n }\n return 0;\n });\n\n // Detect collision runs\n const collisionRuns = [];\n let runStart = 0;\n\n for (let i = 1; i <= len; i++) {\n let isDifferent = i === len;\n if (!isDifferent) {\n const prevIdx = indices[i - 1];\n const currIdx = indices[i];\n for (let c = 0; c < numChunks; c++) {\n if (hashChunks[c][prevIdx] !== hashChunks[c][currIdx]) {\n isDifferent = true;\n break;\n }\n }\n }\n\n if (isDifferent) {\n if (i - runStart > 1) {\n collisionRuns.push(runStart, i);\n }\n runStart = i;\n }\n }\n\n // Create sorted hashes for merge (use first hash chunk)\n const sortedHashes = new Float64Array(len);\n for (let i = 0; i < len; i++) {\n sortedHashes[i] = hashChunks[0][indices[i]];\n }\n\n return { indices, sortedHashes, collisionRuns: new Uint32Array(collisionRuns) };\n}\n\nfunction sortMultiColumnChunk(columns, directions) {\n const len = columns[0].length;\n const numCols = columns.length;\n const indices = new Uint32Array(len);\n for (let i = 0; i < len; i++) indices[i] = i;\n\n indices.sort((a, b) => {\n for (let c = 0; c < numCols; c++) {\n const va = columns[c][a];\n const vb = columns[c][b];\n if (va < vb) return -1 * directions[c];\n if (va > vb) return 1 * directions[c];\n }\n return 0;\n });\n\n // Create sorted column values for merge\n const sortedColumns = columns.map(col => {\n const sorted = new Float64Array(len);\n for (let i = 0; i < len; i++) {\n sorted[i] = col[indices[i]];\n }\n return sorted;\n });\n\n return { indices, sortedColumns };\n}\n\nself.onmessage = function(e) {\n const { type, id } = e.data;\n\n if (type === \"sort\") {\n try {\n const { data, sortModel } = e.data;\n const sorted = sortData(data, sortModel);\n self.postMessage({ type: \"sorted\", id, data: sorted });\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n } else if (type === \"sortIndices\") {\n try {\n const { values, direction } = e.data;\n const indices = sortIndices(values, direction);\n // Transfer the indices buffer back (zero-copy)\n self.postMessage({ type: \"sortedIndices\", id, indices }, [indices.buffer]);\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n } else if (type === \"sortMultiColumn\") {\n try {\n const { columns, directions } = e.data;\n const indices = sortMultiColumn(columns, directions);\n self.postMessage({ type: \"sortedMultiColumn\", id, indices }, [indices.buffer]);\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n } else if (type === \"sortStringHashes\") {\n try {\n const { hashChunks, direction } = e.data;\n const result = sortStringHashes(hashChunks, direction);\n self.postMessage(\n { type: \"sortedStringHashes\", id, indices: result.indices, collisionRuns: result.collisionRuns },\n [result.indices.buffer, result.collisionRuns.buffer]\n );\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n } else if (type === \"sortChunk\") {\n try {\n const { values, direction, chunkOffset } = e.data;\n const result = sortChunk(values, direction);\n self.postMessage(\n { type: \"sortedChunk\", id, indices: result.indices, sortedValues: result.sortedValues, chunkOffset },\n [result.indices.buffer, result.sortedValues.buffer]\n );\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n } else if (type === \"sortStringChunk\") {\n try {\n const { hashChunks, direction, chunkOffset } = e.data;\n const result = sortStringChunk(hashChunks, direction);\n self.postMessage(\n { type: \"sortedStringChunk\", id, indices: result.indices, sortedHashes: result.sortedHashes, collisionRuns: result.collisionRuns, chunkOffset },\n [result.indices.buffer, result.sortedHashes.buffer, result.collisionRuns.buffer]\n );\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n } else if (type === \"sortMultiColumnChunk\") {\n try {\n const { columns, directions, chunkOffset } = e.data;\n const result = sortMultiColumnChunk(columns, directions);\n const transferables = [result.indices.buffer, ...result.sortedColumns.map(c => c.buffer)];\n self.postMessage(\n { type: \"sortedMultiColumnChunk\", id, indices: result.indices, sortedColumns: result.sortedColumns, chunkOffset },\n transferables\n );\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n }\n};\n`;\n\n// =============================================================================\n// Export for use as module worker (if bundler supports it)\n// =============================================================================\n\n// This handles incoming messages when running as a module worker\ninterface WorkerGlobalScopeMinimal {\n onmessage: ((e: MessageEvent) => void) | null;\n postMessage(message: unknown): void;\n}\ndeclare const self: WorkerGlobalScopeMinimal;\n\nif (typeof self !== \"undefined\" && typeof self.onmessage !== \"undefined\") {\n self.onmessage = (e: MessageEvent<SortWorkerRequest>) => {\n const { type, id, data, sortModel } = e.data;\n\n if (type === \"sort\") {\n try {\n const sorted = sortData(data, sortModel);\n self.postMessage({ type: \"sorted\", id, data: sorted } as SortWorkerResponse);\n } catch (error) {\n self.postMessage({ type: \"error\", id, error: String(error) });\n }\n }\n };\n}\n","// packages/core/src/sorting/k-way-merge.ts\n// K-way merge algorithm for combining sorted chunks\n\nimport type { SortDirection } from '../types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Represents a sorted chunk with its values and offset in the original array.\n */\nexport interface SortedChunk {\n /** Sorted indices (local to this chunk) */\n indices: Uint32Array;\n /** Values for comparison (in same order as indices) */\n values: Float64Array;\n /** Offset of this chunk in the original array */\n offset: number;\n}\n\n/**\n * Represents a sorted chunk for multi-column sorting.\n */\nexport interface MultiColumnSortedChunk {\n /** Sorted indices (local to this chunk) */\n indices: Uint32Array;\n /** Values for comparison - array of columns, each in same order as indices */\n columns: Float64Array[];\n /** Sort directions for each column (1 = asc, -1 = desc) */\n directions: Int8Array;\n /** Offset of this chunk in the original array */\n offset: number;\n}\n\n/**\n * Entry in the min/max heap for k-way merge.\n */\ninterface HeapEntry {\n /** Which chunk this entry came from */\n chunkIndex: number;\n /** Current position within the chunk */\n positionInChunk: number;\n /** Value for comparison */\n value: number;\n /** Original global index */\n globalIndex: number;\n}\n\n/**\n * Entry for multi-column heap.\n */\ninterface MultiColumnHeapEntry {\n chunkIndex: number;\n positionInChunk: number;\n values: number[];\n globalIndex: number;\n}\n\n// =============================================================================\n// Min Heap Implementation\n// =============================================================================\n\n/**\n * Binary min-heap for k-way merge.\n * Time complexity: O(log k) for push/pop where k is heap size.\n */\nclass MinHeap {\n private heap: HeapEntry[] = [];\n private multiplier: number;\n\n constructor(direction: SortDirection) {\n // For ascending, smaller values have priority (multiplier = 1)\n // For descending, larger values have priority (multiplier = -1)\n this.multiplier = direction === 'asc' ? 1 : -1;\n }\n\n push(entry: HeapEntry): void {\n this.heap.push(entry);\n this.bubbleUp(this.heap.length - 1);\n }\n\n pop(): HeapEntry | undefined {\n if (this.heap.length === 0) return undefined;\n\n const result = this.heap[0];\n const last = this.heap.pop();\n\n if (this.heap.length > 0 && last) {\n this.heap[0] = last;\n this.bubbleDown(0);\n }\n\n return result;\n }\n\n size(): number {\n return this.heap.length;\n }\n\n private bubbleUp(index: number): void {\n while (index > 0) {\n const parentIndex = Math.floor((index - 1) / 2);\n if (this.compare(this.heap[index]!, this.heap[parentIndex]!) >= 0) {\n break;\n }\n this.swap(index, parentIndex);\n index = parentIndex;\n }\n }\n\n private bubbleDown(index: number): void {\n const length = this.heap.length;\n while (true) {\n const leftChild = 2 * index + 1;\n const rightChild = 2 * index + 2;\n let smallest = index;\n\n if (leftChild < length && this.compare(this.heap[leftChild]!, this.heap[smallest]!) < 0) {\n smallest = leftChild;\n }\n if (rightChild < length && this.compare(this.heap[rightChild]!, this.heap[smallest]!) < 0) {\n smallest = rightChild;\n }\n\n if (smallest === index) break;\n\n this.swap(index, smallest);\n index = smallest;\n }\n }\n\n private compare(a: HeapEntry, b: HeapEntry): number {\n return (a.value - b.value) * this.multiplier;\n }\n\n private swap(i: number, j: number): void {\n const temp = this.heap[i]!;\n this.heap[i] = this.heap[j]!;\n this.heap[j] = temp;\n }\n}\n\n/**\n * Binary heap for multi-column k-way merge.\n */\nclass MultiColumnMinHeap {\n private heap: MultiColumnHeapEntry[] = [];\n private directions: Int8Array;\n\n constructor(directions: Int8Array) {\n this.directions = directions;\n }\n\n push(entry: MultiColumnHeapEntry): void {\n this.heap.push(entry);\n this.bubbleUp(this.heap.length - 1);\n }\n\n pop(): MultiColumnHeapEntry | undefined {\n if (this.heap.length === 0) return undefined;\n\n const result = this.heap[0];\n const last = this.heap.pop();\n\n if (this.heap.length > 0 && last) {\n this.heap[0] = last;\n this.bubbleDown(0);\n }\n\n return result;\n }\n\n size(): number {\n return this.heap.length;\n }\n\n private bubbleUp(index: number): void {\n while (index > 0) {\n const parentIndex = Math.floor((index - 1) / 2);\n if (this.compare(this.heap[index]!, this.heap[parentIndex]!) >= 0) {\n break;\n }\n this.swap(index, parentIndex);\n index = parentIndex;\n }\n }\n\n private bubbleDown(index: number): void {\n const length = this.heap.length;\n while (true) {\n const leftChild = 2 * index + 1;\n const rightChild = 2 * index + 2;\n let smallest = index;\n\n if (leftChild < length && this.compare(this.heap[leftChild]!, this.heap[smallest]!) < 0) {\n smallest = leftChild;\n }\n if (rightChild < length && this.compare(this.heap[rightChild]!, this.heap[smallest]!) < 0) {\n smallest = rightChild;\n }\n\n if (smallest === index) break;\n\n this.swap(index, smallest);\n index = smallest;\n }\n }\n\n private compare(a: MultiColumnHeapEntry, b: MultiColumnHeapEntry): number {\n for (let i = 0; i < this.directions.length; i++) {\n const diff = (a.values[i]! - b.values[i]!) * this.directions[i]!;\n if (diff !== 0) return diff;\n }\n return 0;\n }\n\n private swap(i: number, j: number): void {\n const temp = this.heap[i]!;\n this.heap[i] = this.heap[j]!;\n this.heap[j] = temp;\n }\n}\n\n// =============================================================================\n// K-Way Merge Functions\n// =============================================================================\n\n/**\n * Merge multiple sorted chunks into a single sorted result.\n * Uses a min-heap for O(n log k) time complexity.\n *\n * @param chunks - Array of sorted chunks to merge\n * @param direction - Sort direction ('asc' or 'desc')\n * @returns Uint32Array of globally sorted indices\n */\nexport function kWayMerge(\n chunks: SortedChunk[],\n direction: SortDirection\n): Uint32Array {\n if (chunks.length === 0) {\n return new Uint32Array(0);\n }\n\n if (chunks.length === 1) {\n // Single chunk - just adjust indices to global\n const chunk = chunks[0]!;\n const result = new Uint32Array(chunk.indices.length);\n for (let i = 0; i < chunk.indices.length; i++) {\n result[i] = chunk.indices[i]! + chunk.offset;\n }\n return result;\n }\n\n // Calculate total length\n let totalLength = 0;\n for (const chunk of chunks) {\n totalLength += chunk.indices.length;\n }\n\n const result = new Uint32Array(totalLength);\n const heap = new MinHeap(direction);\n\n // Initialize heap with first element from each non-empty chunk\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]!;\n if (chunk.indices.length > 0) {\n const localIndex = chunk.indices[0]!;\n heap.push({\n chunkIndex: i,\n positionInChunk: 0,\n value: chunk.values[0]!,\n globalIndex: localIndex + chunk.offset,\n });\n }\n }\n\n // Merge\n let resultIndex = 0;\n while (heap.size() > 0) {\n const entry = heap.pop()!;\n result[resultIndex++] = entry.globalIndex;\n\n // Push next element from the same chunk\n const chunk = chunks[entry.chunkIndex]!;\n const nextPosition = entry.positionInChunk + 1;\n\n if (nextPosition < chunk.indices.length) {\n const localIndex = chunk.indices[nextPosition]!;\n heap.push({\n chunkIndex: entry.chunkIndex,\n positionInChunk: nextPosition,\n value: chunk.values[nextPosition]!,\n globalIndex: localIndex + chunk.offset,\n });\n }\n }\n\n return result;\n}\n\n/**\n * Merge multiple sorted chunks for multi-column sorting.\n *\n * @param chunks - Array of multi-column sorted chunks\n * @returns Uint32Array of globally sorted indices\n */\nexport function kWayMergeMultiColumn(\n chunks: MultiColumnSortedChunk[]\n): Uint32Array {\n if (chunks.length === 0) {\n return new Uint32Array(0);\n }\n\n if (chunks.length === 1) {\n const chunk = chunks[0]!;\n const result = new Uint32Array(chunk.indices.length);\n for (let i = 0; i < chunk.indices.length; i++) {\n result[i] = chunk.indices[i]! + chunk.offset;\n }\n return result;\n }\n\n // Use directions from first chunk (all chunks have same directions)\n const directions = chunks[0]!.directions;\n const numColumns = directions.length;\n\n // Calculate total length\n let totalLength = 0;\n for (const chunk of chunks) {\n totalLength += chunk.indices.length;\n }\n\n const result = new Uint32Array(totalLength);\n const heap = new MultiColumnMinHeap(directions);\n\n // Initialize heap with first element from each non-empty chunk\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]!;\n if (chunk.indices.length > 0) {\n const localIndex = chunk.indices[0]!;\n const values: number[] = [];\n for (let c = 0; c < numColumns; c++) {\n values.push(chunk.columns[c]![0]!);\n }\n\n heap.push({\n chunkIndex: i,\n positionInChunk: 0,\n values,\n globalIndex: localIndex + chunk.offset,\n });\n }\n }\n\n // Merge\n let resultIndex = 0;\n while (heap.size() > 0) {\n const entry = heap.pop()!;\n result[resultIndex++] = entry.globalIndex;\n\n const chunk = chunks[entry.chunkIndex]!;\n const nextPosition = entry.positionInChunk + 1;\n\n if (nextPosition < chunk.indices.length) {\n const localIndex = chunk.indices[nextPosition]!;\n const values: number[] = [];\n for (let c = 0; c < numColumns; c++) {\n values.push(chunk.columns[c]![nextPosition]!);\n }\n\n heap.push({\n chunkIndex: entry.chunkIndex,\n positionInChunk: nextPosition,\n values,\n globalIndex: localIndex + chunk.offset,\n });\n }\n }\n\n return result;\n}\n\n/**\n * Detect collision runs at chunk boundaries after merge.\n * This is used for string sorting where hashes may collide across chunks.\n *\n * @param chunks - Original sorted chunks with their hash values\n * @param _direction - Sort direction\n * @returns Array of boundary collision positions [start1, end1, start2, end2, ...]\n */\nexport function detectBoundaryCollisions(\n chunks: SortedChunk[],\n _direction: SortDirection\n): Uint32Array {\n if (chunks.length <= 1) {\n return new Uint32Array(0);\n }\n\n const collisions: number[] = [];\n let globalPosition = 0;\n\n for (let i = 0; i < chunks.length - 1; i++) {\n const currentChunk = chunks[i]!;\n const nextChunk = chunks[i + 1]!;\n\n if (currentChunk.indices.length === 0 || nextChunk.indices.length === 0) {\n globalPosition += currentChunk.indices.length;\n continue;\n }\n\n // Get last value of current chunk and first value of next chunk\n const lastPos = currentChunk.indices.length - 1;\n const lastValue = currentChunk.values[lastPos];\n const firstValue = nextChunk.values[0];\n\n // If they're equal (or very close for floating point), it's a boundary collision\n if (lastValue === firstValue) {\n // Find the extent of the collision run\n // Look backwards in current chunk\n let startInCurrent = lastPos;\n while (startInCurrent > 0 && currentChunk.values[startInCurrent - 1] === lastValue) {\n startInCurrent--;\n }\n\n // Look forwards in next chunk\n let endInNext = 0;\n while (endInNext < nextChunk.indices.length - 1 && nextChunk.values[endInNext + 1] === firstValue) {\n endInNext++;\n }\n\n // Record the collision run (global positions)\n const collisionStart = globalPosition + startInCurrent;\n const collisionEnd = globalPosition + currentChunk.indices.length + endInNext + 1;\n collisions.push(collisionStart, collisionEnd);\n }\n\n globalPosition += currentChunk.indices.length;\n }\n\n return new Uint32Array(collisions);\n}\n","// packages/core/src/sorting/parallel-sort-manager.ts\n// Orchestrates parallel sorting using worker pool and k-way merge\n\nimport type { SortDirection } from '../types';\nimport { WorkerPool } from './worker-pool';\nimport { SORT_WORKER_CODE } from './sort-worker';\nimport type {\n SortChunkRequest,\n SortChunkResponse,\n SortStringChunkRequest,\n SortStringChunkResponse,\n SortMultiColumnChunkRequest,\n SortMultiColumnChunkResponse,\n SortIndicesRequest,\n SortIndicesResponse,\n SortStringHashesRequest,\n SortStringHashesResponse,\n SortMultiColumnRequest,\n SortMultiColumnResponse,\n} from './sort-worker';\nimport { kWayMerge, kWayMergeMultiColumn, type SortedChunk, type MultiColumnSortedChunk } from './k-way-merge';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/** Threshold for using parallel sorting (rows). Below this, use single worker. */\nconst PARALLEL_THRESHOLD = 400_000;\n\n/** Minimum chunk size to avoid overhead for small chunks */\nconst MIN_CHUNK_SIZE = 50_000;\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ParallelSortOptions {\n /** Maximum number of workers (default: navigator.hardwareConcurrency || 4) */\n maxWorkers?: number;\n /** Threshold for parallel sorting (default: 400000) */\n parallelThreshold?: number;\n /** Minimum chunk size (default: 50000) */\n minChunkSize?: number;\n}\n\n// =============================================================================\n// ParallelSortManager\n// =============================================================================\n\n/**\n * Manages parallel sorting operations using a worker pool.\n * Automatically decides between single-worker and parallel sorting based on data size.\n */\nexport class ParallelSortManager {\n private pool: WorkerPool;\n private parallelThreshold: number;\n private minChunkSize: number;\n private isTerminated = false;\n\n constructor(options: ParallelSortOptions = {}) {\n const maxWorkers = options.maxWorkers ??\n (typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : 4) ?? 4;\n\n this.pool = new WorkerPool(SORT_WORKER_CODE, { maxWorkers });\n this.parallelThreshold = options.parallelThreshold ?? PARALLEL_THRESHOLD;\n this.minChunkSize = options.minChunkSize ?? MIN_CHUNK_SIZE;\n }\n\n /**\n * Check if the manager is available for use.\n */\n isAvailable(): boolean {\n return !this.isTerminated && this.pool.isAvailable();\n }\n\n /**\n * Terminate all workers and clean up resources.\n */\n terminate(): void {\n this.pool.terminate();\n this.isTerminated = true;\n }\n\n /**\n * Sort indices using values array.\n * Automatically uses parallel sorting for large datasets.\n */\n async sortIndices(\n values: number[],\n direction: SortDirection\n ): Promise<Uint32Array> {\n if (this.isTerminated) {\n throw new Error('ParallelSortManager has been terminated');\n }\n\n const length = values.length;\n\n // Use single worker for smaller datasets\n if (length < this.parallelThreshold) {\n return this.sortIndicesSingle(values, direction);\n }\n\n return this.sortIndicesParallel(values, direction);\n }\n\n /**\n * Sort string hashes with collision detection.\n * Automatically uses parallel sorting for large datasets.\n */\n async sortStringHashes(\n hashChunks: Float64Array[],\n direction: SortDirection,\n originalStrings: string[]\n ): Promise<Uint32Array> {\n if (this.isTerminated) {\n throw new Error('ParallelSortManager has been terminated');\n }\n\n const length = hashChunks[0]?.length ?? 0;\n\n // Use single worker for smaller datasets\n if (length < this.parallelThreshold) {\n return this.sortStringHashesSingle(hashChunks, direction, originalStrings);\n }\n\n return this.sortStringHashesParallel(hashChunks, direction, originalStrings);\n }\n\n /**\n * Multi-column sort.\n * Automatically uses parallel sorting for large datasets.\n */\n async sortMultiColumn(\n columns: number[][],\n directions: SortDirection[]\n ): Promise<Uint32Array> {\n if (this.isTerminated) {\n throw new Error('ParallelSortManager has been terminated');\n }\n\n const length = columns[0]?.length ?? 0;\n\n // Use single worker for smaller datasets\n if (length < this.parallelThreshold) {\n return this.sortMultiColumnSingle(columns, directions);\n }\n\n return this.sortMultiColumnParallel(columns, directions);\n }\n\n // ===========================================================================\n // Single Worker Methods (for smaller datasets)\n // ===========================================================================\n\n private async sortIndicesSingle(\n values: number[],\n direction: SortDirection\n ): Promise<Uint32Array> {\n const valuesArray = new Float64Array(values);\n const request: SortIndicesRequest = {\n type: 'sortIndices',\n id: 0,\n values: valuesArray,\n direction,\n };\n\n const response = await this.pool.execute<SortIndicesRequest, SortIndicesResponse>(\n request,\n [valuesArray.buffer]\n );\n\n return response.indices;\n }\n\n private async sortStringHashesSingle(\n hashChunks: Float64Array[],\n direction: SortDirection,\n originalStrings: string[]\n ): Promise<Uint32Array> {\n const request: SortStringHashesRequest = {\n type: 'sortStringHashes',\n id: 0,\n hashChunks,\n direction,\n };\n\n const transferables = hashChunks.map(chunk => chunk.buffer);\n const response = await this.pool.execute<SortStringHashesRequest, SortStringHashesResponse>(\n request,\n transferables\n );\n\n // Handle collisions on main thread\n if (response.collisionRuns.length > 0) {\n this.resolveCollisions(response.indices, response.collisionRuns, originalStrings, direction);\n }\n\n return response.indices;\n }\n\n private async sortMultiColumnSingle(\n columns: number[][],\n directions: SortDirection[]\n ): Promise<Uint32Array> {\n const columnArrays = columns.map(col => new Float64Array(col));\n const directionArray = new Int8Array(directions.map(d => d === 'asc' ? 1 : -1));\n\n const request: SortMultiColumnRequest = {\n type: 'sortMultiColumn',\n id: 0,\n columns: columnArrays,\n directions: directionArray,\n };\n\n const transferables = [...columnArrays.map(arr => arr.buffer), directionArray.buffer];\n const response = await this.pool.execute<SortMultiColumnRequest, SortMultiColumnResponse>(\n request,\n transferables\n );\n\n return response.indices;\n }\n\n // ===========================================================================\n // Parallel Sorting Methods (for large datasets)\n // ===========================================================================\n\n private async sortIndicesParallel(\n values: number[],\n direction: SortDirection\n ): Promise<Uint32Array> {\n const chunks = this.splitIntoChunks(values);\n\n // Create requests for each chunk\n const tasks = chunks.map((chunk) => {\n const valuesArray = new Float64Array(chunk.data);\n const request: SortChunkRequest = {\n type: 'sortChunk',\n id: 0,\n values: valuesArray,\n direction,\n chunkOffset: chunk.offset,\n };\n return { request, transferables: [valuesArray.buffer] };\n });\n\n // Execute in parallel\n const responses = await this.pool.executeParallel<SortChunkRequest, SortChunkResponse>(tasks);\n\n // Sort responses by chunkOffset to ensure correct order\n responses.sort((a, b) => a.chunkOffset - b.chunkOffset);\n\n // Build sorted chunks for k-way merge\n const sortedChunks: SortedChunk[] = responses.map(response => ({\n indices: response.indices,\n values: response.sortedValues,\n offset: response.chunkOffset,\n }));\n\n // Merge sorted chunks\n return kWayMerge(sortedChunks, direction);\n }\n\n private async sortStringHashesParallel(\n hashChunks: Float64Array[],\n direction: SortDirection,\n originalStrings: string[]\n ): Promise<Uint32Array> {\n const length = hashChunks[0]!.length;\n const chunkBoundaries = this.calculateChunkBoundaries(length);\n\n // Create requests for each chunk\n const tasks = chunkBoundaries.map((boundary) => {\n // Extract hash chunks for this data chunk\n const chunkHashChunks = hashChunks.map(hc =>\n new Float64Array(hc.buffer, boundary.offset * 8, boundary.length)\n );\n\n // We need to copy since we can't slice from the same buffer for transfer\n const copiedChunks = chunkHashChunks.map(hc => {\n const copy = new Float64Array(hc.length);\n copy.set(hc);\n return copy;\n });\n\n const request: SortStringChunkRequest = {\n type: 'sortStringChunk',\n id: 0,\n hashChunks: copiedChunks,\n direction,\n chunkOffset: boundary.offset,\n };\n\n const transferables = copiedChunks.map(c => c.buffer);\n return { request, transferables };\n });\n\n // Execute in parallel\n const responses = await this.pool.executeParallel<SortStringChunkRequest, SortStringChunkResponse>(tasks);\n\n // Sort responses by chunkOffset\n responses.sort((a, b) => a.chunkOffset - b.chunkOffset);\n\n // Build sorted chunks for k-way merge\n const sortedChunks: SortedChunk[] = responses.map(response => ({\n indices: response.indices,\n values: response.sortedHashes,\n offset: response.chunkOffset,\n }));\n\n // Collect all collision runs (adjusting offsets)\n const allCollisionRuns: number[] = [];\n for (const response of responses) {\n for (let i = 0; i < response.collisionRuns.length; i += 2) {\n // Collision runs are local to chunk, need to track for post-merge resolution\n allCollisionRuns.push(\n response.collisionRuns[i]! + response.chunkOffset,\n response.collisionRuns[i + 1]! + response.chunkOffset\n );\n }\n }\n\n // Merge sorted chunks\n const mergedIndices = kWayMerge(sortedChunks, direction);\n\n // Detect boundary collisions (where chunks meet)\n const boundaryCollisions = this.detectBoundaryCollisionsForStrings(sortedChunks, direction);\n\n // Combine all collision runs\n const combinedCollisions = new Uint32Array([...allCollisionRuns, ...boundaryCollisions]);\n\n // Resolve all collisions\n if (combinedCollisions.length > 0) {\n this.resolveCollisions(mergedIndices, combinedCollisions, originalStrings, direction);\n }\n\n return mergedIndices;\n }\n\n private async sortMultiColumnParallel(\n columns: number[][],\n directions: SortDirection[]\n ): Promise<Uint32Array> {\n const length = columns[0]!.length;\n const chunkBoundaries = this.calculateChunkBoundaries(length);\n const directionArray = new Int8Array(directions.map(d => d === 'asc' ? 1 : -1));\n\n // Create requests for each chunk\n const tasks = chunkBoundaries.map((boundary) => {\n // Extract columns for this chunk\n const chunkColumns = columns.map(col => {\n const chunk = new Float64Array(boundary.length);\n for (let i = 0; i < boundary.length; i++) {\n chunk[i] = col[boundary.offset + i]!;\n }\n return chunk;\n });\n\n // Copy direction array for each request\n const dirCopy = new Int8Array(directionArray);\n\n const request: SortMultiColumnChunkRequest = {\n type: 'sortMultiColumnChunk',\n id: 0,\n columns: chunkColumns,\n directions: dirCopy,\n chunkOffset: boundary.offset,\n };\n\n const transferables = [...chunkColumns.map(c => c.buffer), dirCopy.buffer];\n return { request, transferables };\n });\n\n // Execute in parallel\n const responses = await this.pool.executeParallel<SortMultiColumnChunkRequest, SortMultiColumnChunkResponse>(tasks);\n\n // Sort responses by chunkOffset\n responses.sort((a, b) => a.chunkOffset - b.chunkOffset);\n\n // Build sorted chunks for k-way merge\n const sortedChunks: MultiColumnSortedChunk[] = responses.map(response => ({\n indices: response.indices,\n columns: response.sortedColumns,\n directions: directionArray,\n offset: response.chunkOffset,\n }));\n\n // Merge sorted chunks\n return kWayMergeMultiColumn(sortedChunks);\n }\n\n // ===========================================================================\n // Helper Methods\n // ===========================================================================\n\n /**\n * Split data into chunks for parallel processing.\n */\n private splitIntoChunks<T>(data: T[]): Array<{ data: T[]; offset: number }> {\n const length = data.length;\n const maxWorkers = this.pool.getMaxWorkers();\n const chunkSize = Math.max(this.minChunkSize, Math.ceil(length / maxWorkers));\n const chunks: Array<{ data: T[]; offset: number }> = [];\n\n for (let offset = 0; offset < length; offset += chunkSize) {\n const end = Math.min(offset + chunkSize, length);\n chunks.push({\n data: data.slice(offset, end),\n offset,\n });\n }\n\n return chunks;\n }\n\n /**\n * Calculate chunk boundaries without copying data.\n */\n private calculateChunkBoundaries(length: number): Array<{ offset: number; length: number }> {\n const maxWorkers = this.pool.getMaxWorkers();\n const chunkSize = Math.max(this.minChunkSize, Math.ceil(length / maxWorkers));\n const boundaries: Array<{ offset: number; length: number }> = [];\n\n for (let offset = 0; offset < length; offset += chunkSize) {\n boundaries.push({\n offset,\n length: Math.min(chunkSize, length - offset),\n });\n }\n\n return boundaries;\n }\n\n /**\n * Detect collisions at chunk boundaries for string sorting.\n */\n private detectBoundaryCollisionsForStrings(\n chunks: SortedChunk[],\n _direction: SortDirection\n ): number[] {\n if (chunks.length <= 1) return [];\n\n const collisions: number[] = [];\n let globalPosition = 0;\n\n for (let i = 0; i < chunks.length - 1; i++) {\n const current = chunks[i]!;\n const next = chunks[i + 1]!;\n\n if (current.indices.length === 0 || next.indices.length === 0) {\n globalPosition += current.indices.length;\n continue;\n }\n\n // Compare last value of current chunk with first value of next\n const lastValue = current.values[current.indices.length - 1];\n const firstValue = next.values[0];\n\n if (lastValue === firstValue) {\n // Find extent of collision run\n let startInCurrent = current.indices.length - 1;\n while (startInCurrent > 0 && current.values[startInCurrent - 1] === lastValue) {\n startInCurrent--;\n }\n\n let endInNext = 0;\n while (endInNext < next.indices.length - 1 && next.values[endInNext + 1] === firstValue) {\n endInNext++;\n }\n\n collisions.push(\n globalPosition + startInCurrent,\n globalPosition + current.indices.length + endInNext + 1\n );\n }\n\n globalPosition += current.indices.length;\n }\n\n return collisions;\n }\n\n /**\n * Resolve hash collisions using localeCompare.\n */\n private resolveCollisions(\n indices: Uint32Array,\n collisionRuns: Uint32Array,\n originalStrings: string[],\n direction: SortDirection\n ): void {\n const mult = direction === 'asc' ? 1 : -1;\n\n for (let r = 0; r < collisionRuns.length; r += 2) {\n const start = collisionRuns[r]!;\n const end = collisionRuns[r + 1]!;\n\n if (end <= start || end > indices.length) continue;\n\n const slice = Array.from(indices.slice(start, end));\n\n // Check if all strings are identical (optimization)\n const firstString = originalStrings[slice[0]!];\n let allIdentical = true;\n for (let i = 1; i < slice.length; i++) {\n if (originalStrings[slice[i]!] !== firstString) {\n allIdentical = false;\n break;\n }\n }\n\n if (allIdentical) continue;\n\n // Sort by original strings\n slice.sort((a, b) => mult * originalStrings[a]!.localeCompare(originalStrings[b]!));\n\n // Write back\n for (let i = 0; i < slice.length; i++) {\n indices[start + i] = slice[i]!;\n }\n }\n }\n}\n","// packages/core/src/data-source/sorting.ts\n\nimport type { CellValue, SortModel } from \"../types\";\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/** Number of 10-character chunks for string hashing (30 chars total) */\nexport const HASH_CHUNK_COUNT = 3;\n\n// =============================================================================\n// String Hashing\n// =============================================================================\n\n/**\n * Convert a string to a sortable number using first 10 characters.\n * Uses base 36 (alphanumeric) to fit more characters within float64 safe precision.\n * (36^10 ≈ 3.6×10¹⁵, within MAX_SAFE_INTEGER ~9×10¹⁵)\n */\nexport function stringToSortableNumber(str: string): number {\n const s = str.toLowerCase();\n const len = Math.min(s.length, 10);\n let hash = 0;\n\n // Pack characters into a number using base 36 encoding\n for (let i = 0; i < len; i++) {\n const code = s.charCodeAt(i);\n let mapped: number;\n if (code >= 97 && code <= 122) {\n // a-z -> 0-25\n mapped = code - 97;\n } else if (code >= 48 && code <= 57) {\n // 0-9 -> 26-35\n mapped = code - 48 + 26;\n } else {\n // space and other chars -> 0 (sorts first)\n mapped = 0;\n }\n hash = hash * 36 + mapped;\n }\n\n // Pad shorter strings to ensure \"a\" < \"ab\"\n for (let i = len; i < 10; i++) {\n hash = hash * 36;\n }\n\n return hash;\n}\n\n/**\n * Convert a string to multiple sortable hash values (one per 10-char chunk).\n * This allows correct sorting of strings longer than 10 characters.\n * Returns HASH_CHUNK_COUNT hashes, each covering 10 characters.\n */\nexport function stringToSortableHashes(str: string): number[] {\n const s = str.toLowerCase();\n const hashes: number[] = [];\n\n for (let chunk = 0; chunk < HASH_CHUNK_COUNT; chunk++) {\n const start = chunk * 10;\n let hash = 0;\n\n for (let i = 0; i < 10; i++) {\n const charIndex = start + i;\n const code = charIndex < s.length ? s.charCodeAt(charIndex) : 0;\n let mapped: number;\n if (code >= 97 && code <= 122) {\n // a-z -> 0-25\n mapped = code - 97;\n } else if (code >= 48 && code <= 57) {\n // 0-9 -> 26-35\n mapped = code - 48 + 26;\n } else {\n // space and other chars -> 0 (sorts first)\n mapped = 0;\n }\n hash = hash * 36 + mapped;\n }\n hashes.push(hash);\n }\n\n return hashes;\n}\n\n// =============================================================================\n// Value Conversion\n// =============================================================================\n\n/**\n * Convert any cell value to a sortable number.\n * Strings are converted using a lexicographic hash of the first 8 characters.\n */\nexport function toSortableNumber(val: CellValue): number {\n if (val == null) return Number.MAX_VALUE; // nulls sort last\n\n // Arrays: join and hash as string (no internal sorting for performance)\n if (Array.isArray(val)) {\n if (val.length === 0) return Number.MAX_VALUE;\n return stringToSortableNumber(val.join(', '));\n }\n\n // Numbers pass through directly\n if (typeof val === \"number\") return val;\n\n // Dates convert to timestamp\n if (val instanceof Date) return val.getTime();\n\n // Strings: convert to lexicographic hash\n if (typeof val === \"string\") {\n return stringToSortableNumber(val);\n }\n\n // Fallback: try to convert to number\n const num = Number(val);\n return isNaN(num) ? 0 : num;\n}\n\n// =============================================================================\n// Value Comparison\n// =============================================================================\n\n/**\n * Compare two cell values for sorting\n */\nexport function compareValues(a: CellValue, b: CellValue): number {\n // Handle nulls and empty arrays\n const aIsEmpty = a == null || (Array.isArray(a) && a.length === 0);\n const bIsEmpty = b == null || (Array.isArray(b) && b.length === 0);\n\n if (aIsEmpty && bIsEmpty) return 0;\n if (aIsEmpty) return 1;\n if (bIsEmpty) return -1;\n\n // Handle arrays - join as comma-separated string\n if (Array.isArray(a) || Array.isArray(b)) {\n const strA = Array.isArray(a) ? a.join(', ') : String(a ?? '');\n const strB = Array.isArray(b) ? b.join(', ') : String(b ?? '');\n return strA.localeCompare(strB);\n }\n\n // Numeric comparison\n const aNum = Number(a);\n const bNum = Number(b);\n if (!isNaN(aNum) && !isNaN(bNum)) {\n return aNum - bNum;\n }\n\n // Date comparison\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // String comparison\n return String(a).localeCompare(String(b));\n}\n\n// =============================================================================\n// Sort Application\n// =============================================================================\n\n/**\n * Apply sort model to data array\n */\nexport function applySort<TData>(\n data: TData[],\n sortModel: SortModel[],\n getFieldValue: (row: TData, field: string) => CellValue,\n): TData[] {\n return [...data].sort((a, b) => {\n for (const { colId, direction } of sortModel) {\n const aVal = getFieldValue(a, colId);\n const bVal = getFieldValue(b, colId);\n const comparison = compareValues(aVal, bVal);\n\n if (comparison !== 0) {\n return direction === \"asc\" ? comparison : -comparison;\n }\n }\n return 0;\n });\n}\n","// packages/core/src/filtering/index.ts\n// Shared filtering logic used by both data-source and indexed-data-store\n\nimport type {\n CellValue,\n FilterCondition,\n TextFilterCondition,\n NumberFilterCondition,\n DateFilterCondition,\n ColumnFilterModel,\n FilterModel,\n Row,\n} from \"../types\";\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Check if two dates are on the same day.\n */\nexport function isSameDay(date1: Date, date2: Date): boolean {\n return (\n date1.getFullYear() === date2.getFullYear() &&\n date1.getMonth() === date2.getMonth() &&\n date1.getDate() === date2.getDate()\n );\n}\n\n/**\n * Check if a cell value is considered blank.\n */\nfunction isBlankValue(cellValue: CellValue): boolean {\n return (\n cellValue == null ||\n cellValue === \"\" ||\n (Array.isArray(cellValue) && cellValue.length === 0)\n );\n}\n\n// =============================================================================\n// Text Filter Conditions\n// =============================================================================\n\n/**\n * Evaluate a text filter condition against a cell value.\n */\nexport function evaluateTextCondition(\n cellValue: CellValue,\n condition: TextFilterCondition,\n): boolean {\n const isBlank = isBlankValue(cellValue);\n\n // Handle selectedValues (checkbox-style filtering)\n if (condition.selectedValues && condition.selectedValues.size > 0) {\n const includesBlank = condition.includeBlank === true && isBlank;\n\n // Handle array values (e.g., tags column) - convert to sorted string for comparison\n if (Array.isArray(cellValue)) {\n const sortedArray = [...cellValue].sort((a, b) =>\n String(a).localeCompare(String(b), undefined, {\n numeric: true,\n sensitivity: \"base\",\n }),\n );\n const arrayStr = sortedArray.join(\", \");\n return condition.selectedValues.has(arrayStr) || includesBlank;\n }\n\n const cellStr = String(cellValue ?? \"\");\n return condition.selectedValues.has(cellStr) || includesBlank;\n }\n\n // Handle operator-based conditions\n const strValue = String(cellValue ?? \"\").toLowerCase();\n const filterValue = String(condition.value ?? \"\").toLowerCase();\n\n switch (condition.operator) {\n case \"contains\":\n return strValue.includes(filterValue);\n case \"notContains\":\n return !strValue.includes(filterValue);\n case \"equals\":\n return strValue === filterValue;\n case \"notEquals\":\n return strValue !== filterValue;\n case \"startsWith\":\n return strValue.startsWith(filterValue);\n case \"endsWith\":\n return strValue.endsWith(filterValue);\n case \"blank\":\n return isBlank;\n case \"notBlank\":\n return !isBlank;\n default:\n return true;\n }\n}\n\n// =============================================================================\n// Number Filter Conditions\n// =============================================================================\n\n/**\n * Evaluate a number filter condition against a cell value.\n */\nexport function evaluateNumberCondition(\n cellValue: CellValue,\n condition: NumberFilterCondition,\n): boolean {\n const isBlank = cellValue == null || cellValue === \"\";\n\n if (condition.operator === \"blank\") return isBlank;\n if (condition.operator === \"notBlank\") return !isBlank;\n if (isBlank) return false;\n\n const numValue =\n typeof cellValue === \"number\" ? cellValue : Number(cellValue);\n if (isNaN(numValue)) return false;\n\n const filterValue = condition.value ?? 0;\n const filterValueTo = condition.valueTo ?? 0;\n\n switch (condition.operator) {\n case \"=\":\n return numValue === filterValue;\n case \"!=\":\n return numValue !== filterValue;\n case \">\":\n return numValue > filterValue;\n case \"<\":\n return numValue < filterValue;\n case \">=\":\n return numValue >= filterValue;\n case \"<=\":\n return numValue <= filterValue;\n case \"between\":\n return numValue >= filterValue && numValue <= filterValueTo;\n default:\n return true;\n }\n}\n\n// =============================================================================\n// Date Filter Conditions\n// =============================================================================\n\n/**\n * Evaluate a date filter condition against a cell value.\n */\nexport function evaluateDateCondition(\n cellValue: CellValue,\n condition: DateFilterCondition,\n): boolean {\n const isBlank = cellValue == null || cellValue === \"\";\n\n if (condition.operator === \"blank\") return isBlank;\n if (condition.operator === \"notBlank\") return !isBlank;\n if (isBlank) return false;\n\n const dateValue =\n cellValue instanceof Date ? cellValue : new Date(String(cellValue));\n if (isNaN(dateValue.getTime())) return false;\n\n const filterDate =\n condition.value instanceof Date\n ? condition.value\n : new Date(String(condition.value ?? \"\"));\n const filterDateTo =\n condition.valueTo instanceof Date\n ? condition.valueTo\n : new Date(String(condition.valueTo ?? \"\"));\n\n const dateTime = dateValue.getTime();\n const filterTime = filterDate.getTime();\n const filterTimeTo = filterDateTo.getTime();\n\n switch (condition.operator) {\n case \"=\":\n return isSameDay(dateValue, filterDate);\n case \"!=\":\n return !isSameDay(dateValue, filterDate);\n case \">\":\n return dateTime > filterTime;\n case \"<\":\n return dateTime < filterTime;\n case \"between\":\n return dateTime >= filterTime && dateTime <= filterTimeTo;\n default:\n return true;\n }\n}\n\n// =============================================================================\n// Condition Evaluation\n// =============================================================================\n\n/**\n * Evaluate a single filter condition against a cell value.\n */\nexport function evaluateCondition(\n cellValue: CellValue,\n condition: FilterCondition,\n): boolean {\n switch (condition.type) {\n case \"text\":\n return evaluateTextCondition(cellValue, condition);\n case \"number\":\n return evaluateNumberCondition(cellValue, condition);\n case \"date\":\n return evaluateDateCondition(cellValue, condition);\n default:\n return true;\n }\n}\n\n// =============================================================================\n// Column Filter Evaluation\n// =============================================================================\n\n/**\n * Evaluate a column filter model against a cell value.\n * Uses left-to-right evaluation with per-condition operators.\n */\nexport function evaluateColumnFilter(\n cellValue: CellValue,\n filter: ColumnFilterModel,\n): boolean {\n if (!filter.conditions || filter.conditions.length === 0) return true;\n\n const firstCondition = filter.conditions[0];\n if (!firstCondition) return true;\n\n // Evaluate first condition\n let result = evaluateCondition(cellValue, firstCondition);\n\n // Iterate through remaining conditions with per-condition operators\n for (let i = 1; i < filter.conditions.length; i++) {\n const prevCondition = filter.conditions[i - 1]!;\n const currentCondition = filter.conditions[i]!;\n // Use nextOperator from previous condition, fallback to global combination\n const operator = prevCondition.nextOperator ?? filter.combination;\n const conditionResult = evaluateCondition(cellValue, currentCondition);\n\n if (operator === \"and\") {\n result = result && conditionResult;\n } else {\n result = result || conditionResult;\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// Row Filter Evaluation\n// =============================================================================\n\n/**\n * Check if a row passes all filters in a filter model.\n */\nexport function rowPassesFilter<TData extends Row>(\n row: TData,\n filterModel: FilterModel,\n getFieldValue: (row: TData, field: string) => CellValue,\n): boolean {\n const filterEntries = Object.entries(filterModel).filter(\n ([, value]) => value != null,\n );\n\n if (filterEntries.length === 0) {\n return true;\n }\n\n for (const [field, filter] of filterEntries) {\n const cellValue = getFieldValue(row, field);\n\n if (!evaluateColumnFilter(cellValue, filter)) {\n return false;\n }\n }\n\n return true;\n}\n\n// =============================================================================\n// Array Filter (for batch operations)\n// =============================================================================\n\n/**\n * Apply filters to a data array.\n * Supports both new ColumnFilterModel format and legacy string format.\n */\nexport function applyFilters<TData>(\n data: TData[],\n filterModel: FilterModel | Record<string, string>,\n getFieldValue: (row: TData, field: string) => CellValue,\n): TData[] {\n const filterEntries = Object.entries(filterModel).filter(([, filter]) => {\n // Handle both old string format and new ColumnFilterModel format\n if (typeof filter === \"string\") {\n return filter.trim() !== \"\";\n }\n return filter.conditions && filter.conditions.length > 0;\n });\n\n if (filterEntries.length === 0) {\n return data;\n }\n\n return data.filter((row) => {\n // All column filters must pass (AND between columns)\n for (const [field, filter] of filterEntries) {\n const cellValue = getFieldValue(row, field);\n\n // Handle old string format (backwards compatibility)\n if (typeof filter === \"string\") {\n const strValue = String(cellValue ?? \"\").toLowerCase();\n if (!strValue.includes(filter.toLowerCase())) {\n return false;\n }\n continue;\n }\n\n // Handle new ColumnFilterModel format\n if (!evaluateColumnFilter(cellValue, filter)) {\n return false;\n }\n }\n return true;\n });\n}\n","// packages/core/src/data-source/client-data-source.ts\n\nimport type {\n DataSource,\n DataSourceRequest,\n DataSourceResponse,\n Row,\n CellValue,\n} from \"../types\";\nimport { ParallelSortManager, type ParallelSortOptions } from \"../sorting\";\nimport {\n toSortableNumber,\n stringToSortableHashes,\n applySort,\n HASH_CHUNK_COUNT,\n} from \"./sorting\";\nimport { applyFilters } from \"./filtering\";\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/** Threshold for using Web Worker (rows). Below this, sync sort is used. */\nconst WORKER_THRESHOLD = 200000;\n\n// =============================================================================\n// Field Value Accessor\n// =============================================================================\n\n/**\n * Default field value accessor supporting dot-notation for nested properties\n */\nexport function defaultGetFieldValue<TData>(row: TData, field: string): CellValue {\n const parts = field.split(\".\");\n let value: unknown = row;\n\n for (const part of parts) {\n if (value == null || typeof value !== \"object\") {\n return null;\n }\n value = (value as Record<string, unknown>)[part];\n }\n\n return (value ?? null) as CellValue;\n}\n\n// =============================================================================\n// Client Data Source\n// =============================================================================\n\nexport interface ClientDataSourceOptions<TData> {\n /** Custom field accessor for nested properties */\n getFieldValue?: (row: TData, field: string) => CellValue;\n /** Use Web Worker for sorting large datasets (default: true) */\n useWorker?: boolean;\n /** Options for parallel sorting (only used when useWorker is true) */\n parallelSort?: ParallelSortOptions | false;\n}\n\n/**\n * Creates a client-side data source that holds all data in memory.\n * Sorting and filtering are performed client-side.\n * For large datasets, sorting is automatically offloaded to a Web Worker.\n */\nexport function createClientDataSource<TData extends Row = Row>(\n data: TData[],\n options: ClientDataSourceOptions<TData> = {},\n): DataSource<TData> {\n const { getFieldValue = defaultGetFieldValue, useWorker = true, parallelSort } = options;\n\n // Mutable reference so we can clear it on destroy\n let internalData: TData[] | null = data;\n\n // Lifecycle state for idempotent destroy\n let isDestroyed = false;\n\n // Create parallel sort manager only if useWorker is enabled\n // parallelSort: false disables parallel sorting, undefined or object enables it\n const sortManager = useWorker\n ? new ParallelSortManager(parallelSort === false ? { maxWorkers: 1 } : parallelSort)\n : null;\n\n return {\n async fetch(\n request: DataSourceRequest,\n ): Promise<DataSourceResponse<TData>> {\n // Use internalData which can be cleared on destroy\n let processedData = internalData ? [...internalData] : [];\n\n // Apply filters (always sync - filtering is fast)\n if (request.filter && Object.keys(request.filter).length > 0) {\n processedData = applyFilters(\n processedData,\n request.filter,\n getFieldValue,\n );\n }\n\n // Apply sorting (async with worker for large datasets)\n if (request.sort && request.sort.length > 0) {\n const canUseWorkerSort =\n sortManager &&\n sortManager.isAvailable() &&\n processedData.length >= WORKER_THRESHOLD;\n\n if (canUseWorkerSort) {\n let sortedIndices: Uint32Array;\n\n // For single-column string sorting, use multi-hash approach\n if (request.sort.length === 1) {\n const { colId, direction } = request.sort[0]!;\n\n // Detect column type - arrays are treated as string columns\n let isStringColumn = false;\n for (const row of processedData) {\n const val = getFieldValue(row, colId);\n if (val != null) {\n isStringColumn = typeof val === \"string\" || Array.isArray(val);\n break;\n }\n }\n\n if (isStringColumn) {\n // Use multi-hash sorting for strings\n const originalStrings: string[] = [];\n const hashChunks: number[][] = Array.from(\n { length: HASH_CHUNK_COUNT },\n () => [],\n );\n\n for (const row of processedData) {\n const val = getFieldValue(row, colId);\n const str = val == null ? \"\" : Array.isArray(val) ? val.join(', ') : String(val);\n originalStrings.push(str);\n const hashes = stringToSortableHashes(str);\n for (let c = 0; c < HASH_CHUNK_COUNT; c++) {\n hashChunks[c]!.push(hashes[c]!);\n }\n }\n\n // Convert to Float64Arrays for transfer to worker\n const hashChunkArrays = hashChunks.map(\n (chunk) => new Float64Array(chunk),\n );\n\n sortedIndices = await sortManager.sortStringHashes(\n hashChunkArrays,\n direction,\n originalStrings,\n );\n } else {\n // Use single-value sorting for numeric columns\n const values = processedData.map((row) => {\n const val = getFieldValue(row, colId);\n return toSortableNumber(val);\n });\n sortedIndices = await sortManager.sortIndices(\n values,\n direction,\n );\n }\n } else {\n // Multi-column sorting: use single hash per value\n const columnValues: number[][] = [];\n const directions: Array<\"asc\" | \"desc\"> = [];\n\n for (const { colId, direction } of request.sort) {\n const values = processedData.map((row) => {\n const val = getFieldValue(row, colId);\n return toSortableNumber(val);\n });\n columnValues.push(values);\n directions.push(direction);\n }\n\n sortedIndices = await sortManager.sortMultiColumn(\n columnValues,\n directions,\n );\n }\n\n // Reorder data using sorted indices\n const reordered = new Array<TData>(processedData.length);\n for (let i = 0; i < sortedIndices.length; i++) {\n reordered[i] = processedData[sortedIndices[i]!]!;\n }\n processedData = reordered;\n } else {\n // Use sync sorting for small datasets or when worker is unavailable\n processedData = applySort(processedData, request.sort, getFieldValue);\n }\n }\n\n const totalRows = processedData.length;\n\n // Apply pagination\n const { pageIndex, pageSize } = request.pagination;\n const startIndex = pageIndex * pageSize;\n const rows = processedData.slice(startIndex, startIndex + pageSize);\n\n return { rows, totalRows };\n },\n\n destroy(): void {\n // Idempotent - safe to call multiple times\n if (isDestroyed) return;\n isDestroyed = true;\n\n // Clear data reference to allow garbage collection\n internalData = null;\n\n // Terminate sort manager to clean up Web Workers\n if (sortManager) {\n sortManager.terminate();\n }\n },\n };\n}\n\n// =============================================================================\n// Legacy: Create Data Source from Array\n// =============================================================================\n\n/**\n * Convenience function to create a data source from an array.\n * This provides backwards compatibility with the old `rowData` prop.\n */\nexport function createDataSourceFromArray<TData extends Row = Row>(\n data: TData[],\n): DataSource<TData> {\n return createClientDataSource(data);\n}\n","// packages/core/src/data-source/server-data-source.ts\n\nimport type {\n DataSource,\n DataSourceRequest,\n DataSourceResponse,\n Row,\n} from \"../types\";\n\n// =============================================================================\n// Server Data Source\n// =============================================================================\n\nexport type ServerFetchFunction<TData> = (\n request: DataSourceRequest,\n) => Promise<DataSourceResponse<TData>>;\n\n/**\n * Creates a server-side data source that delegates all operations to the server.\n * The fetch function receives sort/filter/pagination params to pass to the API.\n */\nexport function createServerDataSource<TData extends Row = Row>(\n fetchFn: ServerFetchFunction<TData>,\n): DataSource<TData> {\n return {\n async fetch(\n request: DataSourceRequest,\n ): Promise<DataSourceResponse<TData>> {\n return fetchFn(request);\n },\n };\n}\n","// packages/core/src/indexed-data-store/field-helpers.ts\n\nimport type { CellValue, Row } from \"../types\";\n\n/**\n * Default field value accessor supporting dot notation.\n * @example\n * getFieldValue({ user: { name: \"John\" } }, \"user.name\") // \"John\"\n */\nexport function getFieldValue<TData extends Row>(\n row: TData,\n field: string\n): CellValue {\n const parts = field.split(\".\");\n let value: unknown = row;\n\n for (const part of parts) {\n if (value == null || typeof value !== \"object\") {\n return null;\n }\n value = (value as Record<string, unknown>)[part];\n }\n\n return (value ?? null) as CellValue;\n}\n\n/**\n * Set field value supporting dot notation.\n * Creates nested objects if they don't exist.\n * @example\n * const obj = { user: {} };\n * setFieldValue(obj, \"user.name\", \"John\");\n * // obj is now { user: { name: \"John\" } }\n */\nexport function setFieldValue<TData extends Row>(\n row: TData,\n field: string,\n value: CellValue\n): void {\n const parts = field.split(\".\");\n let current: unknown = row;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (current == null || typeof current !== \"object\") {\n return;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n if (current != null && typeof current === \"object\") {\n (current as Record<string, unknown>)[parts[parts.length - 1]!] = value;\n }\n}\n","// packages/core/src/indexed-data-store/sorting.ts\n\nimport type { CellValue, SortModel, Row } from \"../types\";\n\n/**\n * Convert a string to a sortable number using first 10 characters.\n * Uses base-36 encoding (a-z = 0-25, 0-9 = 26-35).\n */\nexport function stringToSortableNumber(str: string): number {\n const s = str.toLowerCase();\n const len = Math.min(s.length, 10);\n let hash = 0;\n\n for (let i = 0; i < len; i++) {\n const code = s.charCodeAt(i);\n let mapped: number;\n if (code >= 97 && code <= 122) {\n // a-z\n mapped = code - 97;\n } else if (code >= 48 && code <= 57) {\n // 0-9\n mapped = code - 48 + 26;\n } else {\n mapped = 0;\n }\n hash = hash * 36 + mapped;\n }\n\n // Pad to 10 characters\n for (let i = len; i < 10; i++) {\n hash = hash * 36;\n }\n\n return hash;\n}\n\n/**\n * Compare two cell values for sorting.\n * Handles null/undefined, arrays, numbers, dates, and strings.\n */\nexport function compareValues(a: CellValue, b: CellValue): number {\n // Handle nulls and empty arrays\n const aIsEmpty = a == null || (Array.isArray(a) && a.length === 0);\n const bIsEmpty = b == null || (Array.isArray(b) && b.length === 0);\n\n if (aIsEmpty && bIsEmpty) return 0;\n if (aIsEmpty) return 1;\n if (bIsEmpty) return -1;\n\n // Handle arrays - join as comma-separated string\n if (Array.isArray(a) || Array.isArray(b)) {\n const strA = Array.isArray(a) ? a.join(\", \") : String(a ?? \"\");\n const strB = Array.isArray(b) ? b.join(\", \") : String(b ?? \"\");\n return strA.localeCompare(strB);\n }\n\n // Try numeric comparison\n const aNum = Number(a);\n const bNum = Number(b);\n if (!isNaN(aNum) && !isNaN(bNum)) {\n return aNum - bNum;\n }\n\n // Try date comparison\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // Fall back to string comparison\n return String(a).localeCompare(String(b));\n}\n\n/**\n * Compute a sortable hash for a cell value.\n * Used for fast comparisons in sorted indices.\n */\nexport function computeValueHash(value: CellValue): number {\n if (value == null) return Number.MAX_VALUE; // nulls sort last\n\n if (typeof value === \"number\") return value;\n\n if (value instanceof Date) return value.getTime();\n\n if (typeof value === \"string\") {\n return stringToSortableNumber(value);\n }\n\n const num = Number(value);\n return isNaN(num) ? 0 : num;\n}\n\n/**\n * Configuration for sort hash computation\n */\nexport interface SortHashConfig<TData extends Row> {\n sortModel: SortModel[];\n sortModelHash: string;\n getFieldValue: (row: TData, field: string) => CellValue;\n}\n\n/**\n * Compute sort hashes for a row based on sort model.\n * Returns array of hashes, one for each sort column.\n */\nexport function computeRowSortHashes<TData extends Row>(\n row: TData,\n config: SortHashConfig<TData>\n): number[] {\n const hashes: number[] = [];\n\n for (const sort of config.sortModel) {\n const value = config.getFieldValue(row, sort.colId);\n const hash = computeValueHash(value);\n hashes.push(hash);\n }\n\n return hashes;\n}\n\n/**\n * Compare two rows using precomputed hash arrays.\n * Falls back to direct comparison if hashes unavailable.\n */\nexport function compareRowsByHashes(\n hashesA: number[] | undefined,\n hashesB: number[] | undefined,\n sortModel: SortModel[]\n): number | null {\n if (!hashesA || !hashesB) {\n return null; // Signal to use direct comparison\n }\n\n for (let i = 0; i < sortModel.length; i++) {\n const diff = hashesA[i]! - hashesB[i]!;\n if (diff !== 0) {\n return sortModel[i]!.direction === \"asc\" ? diff : -diff;\n }\n }\n\n return 0;\n}\n\n/**\n * Compare two rows directly without hash cache.\n */\nexport function compareRowsDirect<TData extends Row>(\n rowA: TData,\n rowB: TData,\n sortModel: SortModel[],\n getFieldValue: (row: TData, field: string) => CellValue\n): number {\n for (const { colId, direction } of sortModel) {\n const valA = getFieldValue(rowA, colId);\n const valB = getFieldValue(rowB, colId);\n const comparison = compareValues(valA, valB);\n\n if (comparison !== 0) {\n return direction === \"asc\" ? comparison : -comparison;\n }\n }\n\n return 0;\n}\n","// packages/core/src/indexed-data-store/indexed-data-store.ts\n\nimport type {\n CellValue,\n Row,\n RowId,\n SortModel,\n FilterModel,\n DataSourceRequest,\n DataSourceResponse,\n} from \"../types\";\nimport { getFieldValue, setFieldValue } from \"./field-helpers\";\nimport {\n computeRowSortHashes,\n compareRowsByHashes,\n compareRowsDirect,\n} from \"./sorting\";\nimport { rowPassesFilter } from \"./filtering\";\n\n// Re-export RowId for convenience\nexport type { RowId };\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface IndexedDataStoreOptions<TData> {\n /** Function to extract unique ID from row. Required for mutations. */\n getRowId: (row: TData) => RowId;\n /** Custom field accessor for nested properties */\n getFieldValue?: (row: TData, field: string) => CellValue;\n}\n\n/** Hash cache for a single row */\nexport interface RowSortCache {\n /** Map: sortModelHash -> computed hashes for that sort configuration */\n hashes: Map<string, number[]>;\n}\n\n// =============================================================================\n// IndexedDataStore\n// =============================================================================\n\n/**\n * Efficient data structure for incremental operations on grid data.\n * Supports:\n * - O(1) lookup by row ID\n * - O(log n) binary insertion to maintain sort order\n * - Filter state caching with distinct values\n * - Hash caching for fast sorted comparisons\n */\nexport class IndexedDataStore<TData extends Row = Row> {\n // Core storage\n private rows: TData[] = [];\n private rowById: Map<RowId, number> = new Map(); // ID -> index in rows[]\n\n // Sort state\n private sortedIndices: number[] = []; // Indices into rows[] in sorted order\n private sortModel: SortModel[] = [];\n private sortModelHash: string = \"\";\n\n // Filter state\n private filterModel: FilterModel = {};\n private filteredIndices: Set<number> = new Set(); // Indices that pass filter\n private distinctValues: Map<string, Set<CellValue>> = new Map(); // field -> unique values\n\n // Hash cache for sorted comparisons\n private rowSortCache: Map<number, RowSortCache> = new Map();\n\n // Options\n private options: Required<IndexedDataStoreOptions<TData>>;\n\n constructor(\n initialData: TData[] = [],\n options: IndexedDataStoreOptions<TData>,\n ) {\n this.options = {\n getRowId: options.getRowId,\n getFieldValue: options.getFieldValue ?? getFieldValue,\n };\n\n // Initialize with data\n this.setData(initialData);\n }\n\n // ===========================================================================\n // Data Initialization\n // ===========================================================================\n\n /**\n * Clear all data and internal caches.\n * Used for proper memory cleanup when the store is no longer needed.\n */\n clear(): void {\n this.rows = [];\n this.rowById.clear();\n this.sortedIndices = [];\n this.filterModel = {};\n this.filteredIndices.clear();\n this.rowSortCache.clear();\n this.distinctValues.clear();\n this.sortModel = [];\n this.sortModelHash = \"\";\n }\n\n /**\n * Replace all data (used for initial load or full refresh).\n */\n setData(data: TData[]): void {\n this.rows = [...data];\n this.rowById.clear();\n this.rowSortCache.clear();\n this.distinctValues.clear();\n\n // Build ID index\n for (let i = 0; i < this.rows.length; i++) {\n const row = this.rows[i]!;\n const id = this.options.getRowId(row);\n this.rowById.set(id, i);\n }\n\n // Build sorted indices\n this.rebuildSortedIndices();\n\n // Apply filters\n this.rebuildFilteredIndices();\n\n // Build distinct values cache\n this.rebuildDistinctValues();\n }\n\n // ===========================================================================\n // Query API\n // ===========================================================================\n\n /**\n * Query data with sorting, filtering, and pagination.\n * Compatible with DataSource.fetch() interface.\n */\n query(request: DataSourceRequest): DataSourceResponse<TData> {\n // Update sort model (clear if undefined)\n this.setSortModel(request.sort ?? []);\n\n // Update filter model (clear if undefined)\n this.setFilterModel(request.filter ?? {});\n\n // Get visible rows (filtered + sorted)\n const visibleIndices = this.getVisibleIndices();\n const totalRows = visibleIndices.length;\n\n // Apply pagination\n const { pageIndex, pageSize } = request.pagination;\n const startIndex = pageIndex * pageSize;\n const endIndex = Math.min(startIndex + pageSize, totalRows);\n\n const rows: TData[] = [];\n for (let i = startIndex; i < endIndex; i++) {\n const rowIndex = visibleIndices[i];\n if (rowIndex !== undefined) {\n rows.push(this.rows[rowIndex]!);\n }\n }\n\n return { rows, totalRows };\n }\n\n /**\n * Get row by ID.\n */\n getRowById(id: RowId): TData | undefined {\n const index = this.rowById.get(id);\n return index !== undefined ? this.rows[index] : undefined;\n }\n\n /**\n * Get row by index.\n */\n getRowByIndex(index: number): TData | undefined {\n return this.rows[index];\n }\n\n /**\n * Get total row count (unfiltered).\n */\n getTotalRowCount(): number {\n return this.rows.length;\n }\n\n /**\n * Get visible row count (after filtering).\n */\n getVisibleRowCount(): number {\n if (Object.keys(this.filterModel).length === 0) {\n return this.rows.length;\n }\n return this.filteredIndices.size;\n }\n\n /**\n * Get distinct values for a field (for filter UI).\n */\n getDistinctValues(field: string): CellValue[] {\n const values = this.distinctValues.get(field);\n return values ? Array.from(values) : [];\n }\n\n // ===========================================================================\n // Mutation API\n // ===========================================================================\n\n /**\n * Add rows to the store.\n * Rows are inserted at their correct sorted position.\n */\n addRows(rows: TData[]): void {\n for (const row of rows) {\n this.addRow(row);\n }\n }\n\n /**\n * Add a single row.\n */\n private addRow(row: TData): void {\n const id = this.options.getRowId(row);\n\n // Check for duplicate\n if (this.rowById.has(id)) {\n console.warn(`Row with ID ${id} already exists. Skipping.`);\n return;\n }\n\n // Add to rows array\n const index = this.rows.length;\n this.rows.push(row);\n this.rowById.set(id, index);\n\n // Update distinct values\n this.updateDistinctValuesForRow(row, \"add\");\n\n // Compute and cache sort hashes\n if (this.sortModel.length > 0) {\n this.computeRowHashes(index, row);\n }\n\n // Insert into sorted indices at correct position\n if (this.sortModel.length > 0) {\n const insertPos = this.binarySearchInsertPosition(index);\n this.sortedIndices.splice(insertPos, 0, index);\n } else {\n this.sortedIndices.push(index);\n }\n\n // Update filter state\n if (this.rowPassesFilter(row)) {\n this.filteredIndices.add(index);\n }\n }\n\n /**\n * Remove rows by ID.\n */\n removeRows(ids: RowId[]): void {\n const indicesToRemove: number[] = [];\n\n for (const id of ids) {\n const index = this.rowById.get(id);\n if (index !== undefined) {\n indicesToRemove.push(index);\n }\n }\n\n if (indicesToRemove.length === 0) return;\n\n // Sort indices in descending order to remove from end first\n indicesToRemove.sort((a, b) => b - a);\n\n for (const index of indicesToRemove) {\n this.removeRowByIndex(index);\n }\n }\n\n /**\n * Remove a single row by index.\n */\n private removeRowByIndex(index: number): void {\n const row = this.rows[index];\n if (!row) return;\n\n const id = this.options.getRowId(row);\n\n // Update distinct values\n this.updateDistinctValuesForRow(row, \"remove\");\n\n // Remove from sorted indices\n const sortedPos = this.sortedIndices.indexOf(index);\n if (sortedPos !== -1) {\n this.sortedIndices.splice(sortedPos, 1);\n }\n\n // Remove from filtered indices\n this.filteredIndices.delete(index);\n\n // Remove from hash cache\n this.rowSortCache.delete(index);\n\n // Remove from ID map\n this.rowById.delete(id);\n\n // Remove from rows array\n this.rows.splice(index, 1);\n\n // Update indices in maps (all indices after removed one shift down)\n this.reindexAfterRemoval(index);\n }\n\n /**\n * Update indices after a row removal.\n */\n private reindexAfterRemoval(removedIndex: number): void {\n // Update rowById map\n for (const [id, idx] of this.rowById.entries()) {\n if (idx > removedIndex) {\n this.rowById.set(id, idx - 1);\n }\n }\n\n // Update sortedIndices\n for (let i = 0; i < this.sortedIndices.length; i++) {\n if (this.sortedIndices[i]! > removedIndex) {\n this.sortedIndices[i]!--;\n }\n }\n\n // Update filteredIndices\n const newFiltered = new Set<number>();\n for (const idx of this.filteredIndices) {\n if (idx > removedIndex) {\n newFiltered.add(idx - 1);\n } else {\n newFiltered.add(idx);\n }\n }\n this.filteredIndices = newFiltered;\n\n // Update hash cache keys\n const newCache = new Map<number, RowSortCache>();\n for (const [idx, cache] of this.rowSortCache) {\n if (idx > removedIndex) {\n newCache.set(idx - 1, cache);\n } else {\n newCache.set(idx, cache);\n }\n }\n this.rowSortCache = newCache;\n }\n\n /**\n * Update a cell value.\n */\n updateCell(id: RowId, field: string, value: CellValue): void {\n const index = this.rowById.get(id);\n if (index === undefined) {\n console.warn(`Row with ID ${id} not found.`);\n return;\n }\n\n const row = this.rows[index]!;\n const oldValue = this.options.getFieldValue(row, field);\n\n // Update the value\n setFieldValue(row, field, value);\n\n // Update distinct values\n this.updateDistinctValueForField(field, oldValue, value);\n\n // Check if this field affects current sort\n const affectsSort = this.sortModel.some((s) => s.colId === field);\n\n if (affectsSort && this.sortModel.length > 0) {\n // Recompute hash for this row\n this.computeRowHashes(index, row);\n\n // Find current position and remove\n const currentPos = this.sortedIndices.indexOf(index);\n if (currentPos !== -1) {\n this.sortedIndices.splice(currentPos, 1);\n }\n\n // Binary insert at new position\n const newPos = this.binarySearchInsertPosition(index);\n this.sortedIndices.splice(newPos, 0, index);\n }\n\n // Check if this field affects current filter\n const affectsFilter = field in this.filterModel;\n\n if (affectsFilter) {\n const passesFilter = this.rowPassesFilter(row);\n if (passesFilter) {\n this.filteredIndices.add(index);\n } else {\n this.filteredIndices.delete(index);\n }\n }\n }\n\n /**\n * Update multiple fields on a row.\n */\n updateRow(id: RowId, data: Partial<TData>): void {\n for (const [field, value] of Object.entries(data)) {\n this.updateCell(id, field, value as CellValue);\n }\n }\n\n // ===========================================================================\n // Sort Management\n // ===========================================================================\n\n /**\n * Set the sort model. Triggers full re-sort if model changed.\n */\n setSortModel(model: SortModel[]): void {\n const newHash = JSON.stringify(model);\n if (newHash === this.sortModelHash) {\n return; // No change\n }\n\n this.sortModelHash = newHash;\n this.sortModel = [...model];\n\n // Full re-sort required\n this.rebuildHashCache();\n this.rebuildSortedIndices();\n }\n\n /**\n * Get current sort model.\n */\n getSortModel(): SortModel[] {\n return [...this.sortModel];\n }\n\n // ===========================================================================\n // Filter Management\n // ===========================================================================\n\n /**\n * Set the filter model.\n */\n setFilterModel(model: FilterModel): void {\n const newHash = JSON.stringify(model);\n const oldHash = JSON.stringify(this.filterModel);\n\n if (newHash === oldHash) {\n return; // No change\n }\n\n this.filterModel = { ...model };\n this.rebuildFilteredIndices();\n }\n\n /**\n * Get current filter model.\n */\n getFilterModel(): FilterModel {\n return { ...this.filterModel };\n }\n\n // ===========================================================================\n // Private: Sorting\n // ===========================================================================\n\n /**\n * Rebuild sorted indices (full re-sort).\n */\n private rebuildSortedIndices(): void {\n // Create array of all indices\n this.sortedIndices = Array.from({ length: this.rows.length }, (_, i) => i);\n\n if (this.sortModel.length === 0) {\n return; // No sort, keep original order\n }\n\n // Sort using cached hashes or direct comparison\n this.sortedIndices.sort((a, b) => this.compareRows(a, b));\n }\n\n /**\n * Rebuild hash cache for all rows.\n */\n private rebuildHashCache(): void {\n this.rowSortCache.clear();\n\n if (this.sortModel.length === 0) {\n return;\n }\n\n for (let i = 0; i < this.rows.length; i++) {\n this.computeRowHashes(i, this.rows[i]!);\n }\n }\n\n /**\n * Compute and cache sort hashes for a row.\n */\n private computeRowHashes(rowIndex: number, row: TData): void {\n if (this.sortModel.length === 0) return;\n\n const hashes = computeRowSortHashes(row, {\n sortModel: this.sortModel,\n sortModelHash: this.sortModelHash,\n getFieldValue: this.options.getFieldValue,\n });\n\n let cache = this.rowSortCache.get(rowIndex);\n if (!cache) {\n cache = { hashes: new Map() };\n this.rowSortCache.set(rowIndex, cache);\n }\n cache.hashes.set(this.sortModelHash, hashes);\n }\n\n /**\n * Compare two rows using cached hashes.\n */\n private compareRows(indexA: number, indexB: number): number {\n const cacheA = this.rowSortCache.get(indexA);\n const cacheB = this.rowSortCache.get(indexB);\n\n const hashesA = cacheA?.hashes.get(this.sortModelHash);\n const hashesB = cacheB?.hashes.get(this.sortModelHash);\n\n const hashResult = compareRowsByHashes(hashesA, hashesB, this.sortModel);\n if (hashResult !== null) {\n return hashResult;\n }\n\n // Fallback to direct comparison\n return compareRowsDirect(\n this.rows[indexA]!,\n this.rows[indexB]!,\n this.sortModel,\n this.options.getFieldValue,\n );\n }\n\n /**\n * Binary search for insertion position in sortedIndices.\n */\n private binarySearchInsertPosition(rowIndex: number): number {\n let low = 0;\n let high = this.sortedIndices.length;\n\n while (low < high) {\n const mid = (low + high) >>> 1;\n const midIndex = this.sortedIndices[mid]!;\n\n if (this.compareRows(rowIndex, midIndex) > 0) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n return low;\n }\n\n // ===========================================================================\n // Private: Filtering\n // ===========================================================================\n\n /**\n * Rebuild filtered indices.\n */\n private rebuildFilteredIndices(): void {\n this.filteredIndices.clear();\n\n const filterEntries = Object.entries(this.filterModel).filter(\n ([, value]) => value != null,\n );\n\n if (filterEntries.length === 0) {\n // No filters, all rows visible\n return;\n }\n\n for (let i = 0; i < this.rows.length; i++) {\n if (this.rowPassesFilter(this.rows[i]!)) {\n this.filteredIndices.add(i);\n }\n }\n }\n\n /**\n * Check if a row passes the current filter.\n */\n private rowPassesFilter(row: TData): boolean {\n return rowPassesFilter(row, this.filterModel, this.options.getFieldValue);\n }\n\n /**\n * Get visible indices (filtered + sorted).\n */\n private getVisibleIndices(): number[] {\n const hasFilters =\n Object.entries(this.filterModel).filter(([, v]) => v != null).length > 0;\n\n if (!hasFilters) {\n return this.sortedIndices;\n }\n\n // Filter the sorted indices\n return this.sortedIndices.filter((idx) => this.filteredIndices.has(idx));\n }\n\n // ===========================================================================\n // Private: Distinct Values\n // ===========================================================================\n\n /**\n * Rebuild distinct values cache for all fields.\n */\n private rebuildDistinctValues(): void {\n this.distinctValues.clear();\n\n for (const row of this.rows) {\n this.updateDistinctValuesForRow(row, \"add\");\n }\n }\n\n /**\n * Update distinct values when a row is added or removed.\n */\n private updateDistinctValuesForRow(\n row: TData,\n operation: \"add\" | \"remove\",\n ): void {\n // For now, we track all fields\n // Could be optimized to only track fields we care about\n if (typeof row !== \"object\" || row === null) return;\n\n for (const [field, value] of Object.entries(\n row as Record<string, unknown>,\n )) {\n if (value == null) continue;\n\n if (operation === \"add\") {\n let values = this.distinctValues.get(field);\n if (!values) {\n values = new Set();\n this.distinctValues.set(field, values);\n }\n // Handle arrays by adding individual elements (e.g., tags column)\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item != null) {\n values.add(item as CellValue);\n }\n }\n } else {\n values.add(value as CellValue);\n }\n }\n // Note: For \"remove\", we'd need reference counting to know if value is still used\n // For simplicity, we don't remove from distinct values on row removal\n }\n }\n\n /**\n * Update distinct value for a specific field when cell value changes.\n */\n private updateDistinctValueForField(\n field: string,\n _oldValue: CellValue,\n newValue: CellValue,\n ): void {\n // Add new value (old value stays since other rows might use it)\n if (newValue != null) {\n let values = this.distinctValues.get(field);\n if (!values) {\n values = new Set();\n this.distinctValues.set(field, values);\n }\n // Handle arrays by adding individual elements (e.g., tags column)\n if (Array.isArray(newValue)) {\n for (const item of newValue) {\n if (item != null) {\n values.add(item as CellValue);\n }\n }\n } else {\n values.add(newValue);\n }\n }\n }\n}\n","// gp-grid-core/src/transaction-manager.ts\n\nimport type { CellValue, Row, RowId } from \"./types\";\nimport type { IndexedDataStore } from \"./indexed-data-store\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AddTransaction<TData> {\n type: \"ADD\";\n rows: TData[];\n}\n\nexport interface RemoveTransaction {\n type: \"REMOVE\";\n rowIds: RowId[];\n}\n\nexport interface UpdateCellTransaction {\n type: \"UPDATE_CELL\";\n rowId: RowId;\n field: string;\n value: CellValue;\n}\n\nexport interface UpdateRowTransaction<TData> {\n type: \"UPDATE_ROW\";\n rowId: RowId;\n data: Partial<TData>;\n}\n\nexport type Transaction<TData> =\n | AddTransaction<TData>\n | RemoveTransaction\n | UpdateCellTransaction\n | UpdateRowTransaction<TData>;\n\nexport interface TransactionResult {\n added: number;\n removed: number;\n updated: number;\n}\n\nexport interface TransactionManagerOptions<TData extends Row> {\n /** Debounce time in milliseconds. Default 50. Set to 0 for sync. */\n debounceMs: number;\n /** The indexed data store to apply transactions to */\n store: IndexedDataStore<TData>;\n /** Callback when transactions are processed */\n onProcessed?: (result: TransactionResult) => void;\n}\n\n// =============================================================================\n// TransactionManager\n// =============================================================================\n\n/**\n * Manages a queue of data mutations with debounced batch processing.\n * Supports ADD, REMOVE, UPDATE_CELL, and UPDATE_ROW operations.\n */\nexport class TransactionManager<TData extends Row = Row> {\n private queue: Transaction<TData>[] = [];\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingPromise: {\n resolve: () => void;\n reject: (error: Error) => void;\n } | null = null;\n\n private options: TransactionManagerOptions<TData>;\n\n constructor(options: TransactionManagerOptions<TData>) {\n this.options = options;\n }\n\n // ===========================================================================\n // Public API\n // ===========================================================================\n\n /**\n * Queue rows to be added.\n */\n add(rows: TData[]): void {\n if (rows.length === 0) return;\n\n this.queue.push({ type: \"ADD\", rows });\n this.scheduleProcessing();\n }\n\n /**\n * Queue rows to be removed by ID.\n */\n remove(rowIds: RowId[]): void {\n if (rowIds.length === 0) return;\n\n this.queue.push({ type: \"REMOVE\", rowIds });\n this.scheduleProcessing();\n }\n\n /**\n * Queue a cell update.\n */\n updateCell(rowId: RowId, field: string, value: CellValue): void {\n this.queue.push({ type: \"UPDATE_CELL\", rowId, field, value });\n this.scheduleProcessing();\n }\n\n /**\n * Queue a row update (multiple fields).\n */\n updateRow(rowId: RowId, data: Partial<TData>): void {\n if (Object.keys(data).length === 0) return;\n\n this.queue.push({ type: \"UPDATE_ROW\", rowId, data });\n this.scheduleProcessing();\n }\n\n /**\n * Force immediate processing of queued transactions.\n * Returns a promise that resolves when processing is complete.\n */\n flush(): Promise<void> {\n if (this.queue.length === 0) {\n return Promise.resolve();\n }\n\n // Cancel any pending debounce\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n // If already have a pending promise, return it\n if (this.pendingPromise) {\n return new Promise((resolve, reject) => {\n const existing = this.pendingPromise!;\n const originalResolve = existing.resolve;\n const originalReject = existing.reject;\n\n existing.resolve = () => {\n originalResolve();\n resolve();\n };\n existing.reject = (error: Error) => {\n originalReject(error);\n reject(error);\n };\n });\n }\n\n // Create new promise and process\n return new Promise((resolve, reject) => {\n this.pendingPromise = { resolve, reject };\n this.processQueue();\n });\n }\n\n /**\n * Check if there are pending transactions.\n */\n hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n /**\n * Get count of pending transactions.\n */\n getPendingCount(): number {\n return this.queue.length;\n }\n\n /**\n * Clear all pending transactions without processing.\n */\n clear(): void {\n this.queue = [];\n\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n if (this.pendingPromise) {\n this.pendingPromise.resolve();\n this.pendingPromise = null;\n }\n }\n\n // ===========================================================================\n // Private\n // ===========================================================================\n\n /**\n * Schedule processing after throttle delay.\n * Uses throttle pattern: if a timer is already pending, new transactions\n * are added to the queue but don't reset the timer. This ensures updates\n * are processed even when they arrive faster than the throttle interval.\n */\n private scheduleProcessing(): void {\n // If sync mode (debounceMs = 0), process immediately\n if (this.options.debounceMs === 0) {\n this.processQueue();\n return;\n }\n\n // If timer already pending, new transactions will be processed when it fires\n if (this.debounceTimer !== null) {\n return;\n }\n\n // Schedule new timer\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n this.processQueue();\n }, this.options.debounceMs);\n }\n\n /**\n * Process all queued transactions.\n */\n private processQueue(): void {\n if (this.queue.length === 0) {\n if (this.pendingPromise) {\n this.pendingPromise.resolve();\n this.pendingPromise = null;\n }\n return;\n }\n\n // Take snapshot of current queue\n const transactions = this.queue;\n this.queue = [];\n\n // Process transactions\n const result: TransactionResult = {\n added: 0,\n removed: 0,\n updated: 0,\n };\n\n try {\n for (const tx of transactions) {\n switch (tx.type) {\n case \"ADD\":\n this.options.store.addRows(tx.rows);\n result.added += tx.rows.length;\n break;\n\n case \"REMOVE\":\n this.options.store.removeRows(tx.rowIds);\n result.removed += tx.rowIds.length;\n break;\n\n case \"UPDATE_CELL\":\n this.options.store.updateCell(tx.rowId, tx.field, tx.value);\n result.updated++;\n break;\n\n case \"UPDATE_ROW\":\n this.options.store.updateRow(tx.rowId, tx.data);\n result.updated++;\n break;\n }\n }\n\n // Notify callback\n if (this.options.onProcessed) {\n this.options.onProcessed(result);\n }\n\n // Resolve pending promise\n if (this.pendingPromise) {\n this.pendingPromise.resolve();\n this.pendingPromise = null;\n }\n } catch (error) {\n // Reject pending promise\n if (this.pendingPromise) {\n this.pendingPromise.reject(\n error instanceof Error ? error : new Error(String(error))\n );\n this.pendingPromise = null;\n }\n }\n }\n}\n","// packages/core/src/data-source/mutable-data-source.ts\n\nimport type {\n DataSource,\n DataSourceRequest,\n DataSourceResponse,\n Row,\n RowId,\n CellValue,\n} from \"../types\";\nimport { IndexedDataStore } from \"../indexed-data-store\";\nimport {\n TransactionManager,\n type TransactionResult,\n} from \"../transaction-manager\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Callback for data change notifications */\nexport type DataChangeListener = (result: TransactionResult) => void;\n\n/**\n * Data source with mutation capabilities.\n * Extends DataSource with add, remove, and update operations.\n */\nexport interface MutableDataSource<TData = Row> extends DataSource<TData> {\n /** Add rows to the data source. Queued and processed after debounce. */\n addRows(rows: TData[]): void;\n /** Remove rows by ID. Queued and processed after debounce. */\n removeRows(ids: RowId[]): void;\n /** Update a cell value. Queued and processed after debounce. */\n updateCell(id: RowId, field: string, value: CellValue): void;\n /** Update multiple fields on a row. Queued and processed after debounce. */\n updateRow(id: RowId, data: Partial<TData>): void;\n /** Force immediate processing of queued transactions. */\n flushTransactions(): Promise<void>;\n /** Check if there are pending transactions. */\n hasPendingTransactions(): boolean;\n /** Get distinct values for a field (for filter UI). */\n getDistinctValues(field: string): CellValue[];\n /** Get a row by ID. */\n getRowById(id: RowId): TData | undefined;\n /** Get total row count. */\n getTotalRowCount(): number;\n /** Subscribe to data change notifications. Returns unsubscribe function. */\n subscribe(listener: DataChangeListener): () => void;\n /** Clear all data from the data source. */\n clear(): void;\n}\n\nexport interface MutableClientDataSourceOptions<TData> {\n /** Function to extract unique ID from row. Required. */\n getRowId: (row: TData) => RowId;\n /** Custom field accessor for nested properties. */\n getFieldValue?: (row: TData, field: string) => CellValue;\n /** Debounce time for transactions in ms. Default 50. Set to 0 for sync. */\n debounceMs?: number;\n /** Callback when transactions are processed. */\n onTransactionProcessed?: (result: TransactionResult) => void;\n}\n\n// =============================================================================\n// Mutable Client Data Source\n// =============================================================================\n\n/**\n * Creates a mutable client-side data source with transaction support.\n * Uses IndexedDataStore for efficient incremental operations.\n */\nexport function createMutableClientDataSource<TData extends Row = Row>(\n data: TData[],\n options: MutableClientDataSourceOptions<TData>,\n): MutableDataSource<TData> {\n const {\n getRowId,\n getFieldValue,\n debounceMs = 50,\n onTransactionProcessed,\n } = options;\n\n // Create the indexed data store\n const store = new IndexedDataStore(data, {\n getRowId,\n getFieldValue,\n });\n\n // Subscribers for data change notifications\n const subscribers = new Set<DataChangeListener>();\n\n // Create the transaction manager\n const transactionManager = new TransactionManager<TData>({\n debounceMs,\n store,\n onProcessed: (result) => {\n // Notify external callback\n onTransactionProcessed?.(result);\n // Notify all subscribers\n for (const listener of subscribers) {\n listener(result);\n }\n },\n });\n\n return {\n async fetch(\n request: DataSourceRequest,\n ): Promise<DataSourceResponse<TData>> {\n // Flush any pending transactions before fetching\n if (transactionManager.hasPending()) {\n await transactionManager.flush();\n }\n\n return store.query(request);\n },\n\n addRows(rows: TData[]): void {\n transactionManager.add(rows);\n },\n\n removeRows(ids: RowId[]): void {\n transactionManager.remove(ids);\n },\n\n updateCell(id: RowId, field: string, value: CellValue): void {\n transactionManager.updateCell(id, field, value);\n },\n\n updateRow(id: RowId, data: Partial<TData>): void {\n transactionManager.updateRow(id, data);\n },\n\n async flushTransactions(): Promise<void> {\n await transactionManager.flush();\n },\n\n hasPendingTransactions(): boolean {\n return transactionManager.hasPending();\n },\n\n getDistinctValues(field: string): CellValue[] {\n return store.getDistinctValues(field);\n },\n\n getRowById(id: RowId): TData | undefined {\n return store.getRowById(id);\n },\n\n getTotalRowCount(): number {\n return store.getTotalRowCount();\n },\n\n subscribe(listener: DataChangeListener): () => void {\n subscribers.add(listener);\n return () => {\n subscribers.delete(listener);\n };\n },\n\n clear(): void {\n store.clear();\n subscribers.clear();\n },\n };\n}\n","// packages/core/src/styles/variables.ts\n\n// CSS Variables for theming (light and dark modes)\nexport const variablesStyles: string = `\n/* =============================================================================\n GP Grid - CSS Variables for Theming\n ============================================================================= */\n\n.gp-grid-container {\n /* Colors - Light Mode (default) */\n --gp-grid-bg: #ffffff;\n --gp-grid-bg-alt: #f8f9fa;\n --gp-grid-text: #212529;\n --gp-grid-text-secondary: #6c757d;\n --gp-grid-text-muted: #adb5bd;\n --gp-grid-border: #dee2e6;\n --gp-grid-border-light: #e9ecef;\n\n /* Header */\n --gp-grid-header-bg: #f1f3f5;\n --gp-grid-header-text: #212529;\n --gp-grid-header-z-index: 10;\n\n /* Selection */\n --gp-grid-primary: #228be6;\n --gp-grid-primary-light: #e7f5ff;\n --gp-grid-primary-border: #74c0fc;\n --gp-grid-hover: #f1f3f5;\n\n /* Filter */\n --gp-grid-filter-bg: #f8f9fa;\n --gp-grid-input-bg: #ffffff;\n --gp-grid-input-border: #ced4da;\n\n /* Error */\n --gp-grid-error-bg: #fff5f5;\n --gp-grid-error-text: #c92a2a;\n\n /* Loading */\n --gp-grid-loading-bg: rgba(255, 255, 255, 0.95);\n --gp-grid-loading-text: #495057;\n\n /* Scrollbar */\n --gp-grid-scrollbar-track: #f1f3f5;\n --gp-grid-scrollbar-thumb: #ced4da;\n --gp-grid-scrollbar-thumb-hover: #adb5bd;\n}\n\n/* Dark Mode */\n.gp-grid-container--dark {\n --gp-grid-bg: #1a1b1e;\n --gp-grid-bg-alt: #25262b;\n --gp-grid-text: #c1c2c5;\n --gp-grid-text-secondary: #909296;\n --gp-grid-text-muted: #5c5f66;\n --gp-grid-border: #373a40;\n --gp-grid-border-light: #2c2e33;\n\n /* Header */\n --gp-grid-header-bg: #25262b;\n --gp-grid-header-text: #c1c2c5;\n --gp-grid-header-z-index: 10;\n\n /* Selection */\n --gp-grid-primary: #339af0;\n --gp-grid-primary-light: #1c3d5a;\n --gp-grid-primary-border: #1c7ed6;\n --gp-grid-hover: #2c2e33;\n\n /* Filter */\n --gp-grid-filter-bg: #25262b;\n --gp-grid-input-bg: #1a1b1e;\n --gp-grid-input-border: #373a40;\n\n /* Error */\n --gp-grid-error-bg: #2c1a1a;\n --gp-grid-error-text: #ff6b6b;\n\n /* Loading */\n --gp-grid-loading-bg: rgba(26, 27, 30, 0.95);\n --gp-grid-loading-text: #c1c2c5;\n\n /* Scrollbar */\n --gp-grid-scrollbar-track: #25262b;\n --gp-grid-scrollbar-thumb: #373a40;\n --gp-grid-scrollbar-thumb-hover: #4a4d52;\n}\n`;\n","// packages/core/src/styles/container.ts\n\n// Grid container base styles\nexport const containerStyles: string = `\n/* =============================================================================\n GP Grid - Clean Flat Design\n ============================================================================= */\n\n/* Grid Container */\n.gp-grid-container {\n outline: none;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n font-size: 13px;\n line-height: 1.5;\n color: var(--gp-grid-text);\n background-color: var(--gp-grid-bg);\n border: 1px solid var(--gp-grid-border);\n border-radius: 6px;\n user-select: none;\n -webkit-user-select: none;\n}\n\n.gp-grid-container:focus {\n outline: none;\n border-color: var(--gp-grid-primary);\n}\n`;\n","// packages/core/src/styles/header.ts\n\n// Header row, cell, sort arrows, and filter icon styles\nexport const headerStyles: string = `\n/* =============================================================================\n Header\n ============================================================================= */\n\n.gp-grid-header {\n position: sticky;\n top: 0;\n left: 0;\n z-index: var(--gp-grid-header-z-index);\n background-color: var(--gp-grid-header-bg);\n border-bottom: 1px solid var(--gp-grid-border);\n}\n\n.gp-grid-container .gp-grid-header-cell {\n position: absolute;\n box-sizing: border-box;\n border-right: 1px solid var(--gp-grid-border);\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--gp-grid-header-text);\n cursor: pointer;\n user-select: none;\n display: flex;\n align-items: center;\n padding: 0 12px;\n background-color: transparent;\n transition: background-color 0.1s ease;\n}\n\n.gp-grid-container .gp-grid-header-cell:hover {\n background-color: var(--gp-grid-hover);\n}\n\n.gp-grid-container .gp-grid-header-cell:active {\n background-color: var(--gp-grid-border-light);\n}\n\n.gp-grid-container .gp-grid-header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--gp-grid-header-text);\n}\n\n/* Header icons container */\n.gp-grid-header-icons {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n}\n\n/* Stacked sort arrows */\n.gp-grid-sort-arrows {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 2px;\n margin-left: 6px;\n}\n\n.gp-grid-sort-arrows-stack {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n}\n\n.gp-grid-sort-arrow-up,\n.gp-grid-sort-arrow-down {\n opacity: 0.35;\n transition: opacity 0.15s ease, color 0.15s ease;\n color: var(--gp-grid-text);\n}\n\n.gp-grid-sort-arrow-up.active,\n.gp-grid-sort-arrow-down.active {\n opacity: 1;\n color: var(--gp-grid-primary);\n}\n\n.gp-grid-sort-index {\n font-size: 9px;\n color: var(--gp-grid-primary);\n font-weight: 600;\n}\n\n/* Filter icon */\n.gp-grid-filter-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 4px;\n cursor: pointer;\n color: var(--gp-grid-text-secondary);\n transition: background-color 0.15s ease, color 0.15s ease;\n margin-left: 2px;\n}\n\n.gp-grid-filter-icon:hover {\n background-color: var(--gp-grid-hover);\n color: var(--gp-grid-primary);\n}\n\n.gp-grid-filter-icon.active {\n color: var(--gp-grid-primary);\n background-color: var(--gp-grid-primary-light);\n}\n`;\n","// packages/core/src/styles/cells.ts\n\n// Data cell styles including selection, active, editing, and fill handle\nexport const cellStyles: string = `\n/* =============================================================================\n Data Cells\n ============================================================================= */\n\n.gp-grid-row {\n position: absolute;\n top: 0;\n left: 0;\n}\n\n/* Row background - :where() for zero specificity, so user highlight classes always win */\n:where(.gp-grid-row) {\n background-color: var(--gp-grid-bg);\n}\n\n/* Structural properties - required for grid layout */\n.gp-grid-cell {\n position: absolute;\n top: 0;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n}\n\n/* Visual properties - :where() for zero specificity, so user highlight classes always win */\n:where(.gp-grid-cell) {\n padding: 0 12px;\n cursor: cell;\n color: var(--gp-grid-text);\n border-right: 1px solid var(--gp-grid-border-light);\n border-bottom: 1px solid var(--gp-grid-border-light);\n background-color: transparent;\n}\n\n/* Active cell (focused) - structural properties stay, visual use :where() */\n.gp-grid-cell--active {\n outline: none;\n z-index: 5;\n}\n:where(.gp-grid-cell--active) {\n background-color: var(--gp-grid-primary-light);\n border: 2px solid var(--gp-grid-primary);\n padding: 0 11px;\n}\n\n/* Selected cells (range selection) */\n:where(.gp-grid-cell--selected) {\n background-color: var(--gp-grid-primary-light);\n}\n\n/* Editing cell - structural properties stay, visual use :where() */\n.gp-grid-cell--editing {\n z-index: 10;\n}\n:where(.gp-grid-cell--editing) {\n background-color: var(--gp-grid-bg);\n border: 2px solid var(--gp-grid-primary);\n padding: 0;\n}\n\n/* =============================================================================\n Fill Handle (drag to fill)\n ============================================================================= */\n\n.gp-grid-fill-handle {\n position: absolute;\n width: 8px;\n height: 8px;\n background-color: var(--gp-grid-primary);\n border: 2px solid var(--gp-grid-bg);\n cursor: crosshair;\n z-index: 100;\n pointer-events: auto;\n box-sizing: border-box;\n border-radius: 1px;\n}\n\n.gp-grid-fill-handle:hover {\n transform: scale(1.2);\n}\n\n/* Fill preview (cells being filled) */\n.gp-grid-cell.gp-grid-cell--fill-preview {\n background-color: var(--gp-grid-primary-light);\n border: 1px dashed var(--gp-grid-primary);\n}\n\n/* =============================================================================\n Edit Input\n ============================================================================= */\n\n.gp-grid-edit-input {\n width: 100%;\n height: 100%;\n padding: 0 11px;\n font-family: inherit;\n font-size: inherit;\n color: var(--gp-grid-text);\n border: none;\n background-color: transparent;\n}\n\n.gp-grid-edit-input:focus {\n outline: none;\n}\n`;\n","// packages/core/src/styles/states.ts\n\n// Loading, error, and empty state styles\nexport const statesStyles: string = `\n/* =============================================================================\n Loading & Error States\n ============================================================================= */\n\n.gp-grid-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 12px 20px;\n background-color: var(--gp-grid-loading-bg);\n color: var(--gp-grid-loading-text);\n border-radius: 6px;\n border: 1px solid var(--gp-grid-border);\n font-weight: 500;\n font-size: 13px;\n z-index: 1000;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.gp-grid-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid var(--gp-grid-border);\n border-top-color: var(--gp-grid-primary);\n border-radius: 50%;\n animation: gp-grid-spin 0.7s linear infinite;\n}\n\n@keyframes gp-grid-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.gp-grid-error {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 12px 20px;\n background-color: var(--gp-grid-error-bg);\n color: var(--gp-grid-error-text);\n border-radius: 6px;\n border: 1px solid var(--gp-grid-error-text);\n font-weight: 500;\n font-size: 13px;\n z-index: 1000;\n max-width: 80%;\n text-align: center;\n}\n\n/* =============================================================================\n Empty State\n ============================================================================= */\n\n.gp-grid-empty {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: var(--gp-grid-text-muted);\n font-size: 14px;\n text-align: center;\n}\n`;\n","// packages/core/src/styles/scrollbar.ts\n\n// Custom scrollbar styles\nexport const scrollbarStyles: string = `\n/* =============================================================================\n Scrollbar Styling\n ============================================================================= */\n\n.gp-grid-container::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n.gp-grid-container::-webkit-scrollbar-track {\n background-color: var(--gp-grid-scrollbar-track);\n}\n\n.gp-grid-container::-webkit-scrollbar-thumb {\n background-color: var(--gp-grid-scrollbar-thumb);\n border-radius: 4px;\n}\n\n.gp-grid-container::-webkit-scrollbar-thumb:hover {\n background-color: var(--gp-grid-scrollbar-thumb-hover);\n}\n\n.gp-grid-container::-webkit-scrollbar-corner {\n background-color: var(--gp-grid-scrollbar-track);\n}\n`;\n","// packages/core/src/styles/filters.ts\n\n// Filter popup and filter-related styles\nexport const filtersStyles: string = `\n/* =============================================================================\n Filter Popup\n ============================================================================= */\n\n.gp-grid-filter-popup {\n background-color: var(--gp-grid-bg);\n border: 1px solid var(--gp-grid-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n max-height: 400px;\n display: flex;\n flex-direction: column;\n font-size: 13px;\n}\n\n.gp-grid-filter-header {\n padding: 10px 12px;\n font-weight: 600;\n border-bottom: 1px solid var(--gp-grid-border-light);\n color: var(--gp-grid-header-text);\n}\n\n.gp-grid-filter-content {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n overflow: hidden;\n}\n\n/* Mode toggle (Values / Condition) */\n.gp-grid-filter-mode-toggle {\n display: flex;\n gap: 4px;\n background-color: var(--gp-grid-bg-alt);\n border-radius: 4px;\n padding: 2px;\n}\n\n.gp-grid-filter-mode-toggle button {\n flex: 1;\n padding: 6px 10px;\n font-size: 11px;\n font-family: inherit;\n border: none;\n border-radius: 3px;\n background-color: transparent;\n color: var(--gp-grid-text-secondary);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease;\n}\n\n.gp-grid-filter-mode-toggle button:hover {\n color: var(--gp-grid-text);\n}\n\n.gp-grid-filter-mode-toggle button.active {\n background-color: var(--gp-grid-bg);\n color: var(--gp-grid-text);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n/* Info message for too many values */\n.gp-grid-filter-info {\n font-size: 11px;\n color: var(--gp-grid-text-secondary);\n padding: 6px 8px;\n background-color: var(--gp-grid-bg-alt);\n border-radius: 4px;\n}\n\n/* Search input in filter */\n.gp-grid-filter-search {\n width: 100%;\n height: 30px;\n padding: 0 10px;\n font-family: inherit;\n font-size: 12px;\n border: 1px solid var(--gp-grid-input-border);\n border-radius: 4px;\n background-color: var(--gp-grid-input-bg);\n color: var(--gp-grid-text);\n box-sizing: border-box;\n}\n\n.gp-grid-filter-search:focus {\n outline: none;\n border-color: var(--gp-grid-primary);\n}\n\n/* Select All / Deselect All actions */\n.gp-grid-filter-actions {\n display: flex;\n gap: 8px;\n}\n\n.gp-grid-filter-actions button {\n flex: 1;\n padding: 4px 8px;\n font-size: 11px;\n font-family: inherit;\n border: 1px solid var(--gp-grid-input-border);\n border-radius: 3px;\n background-color: var(--gp-grid-bg);\n color: var(--gp-grid-text);\n cursor: pointer;\n}\n\n.gp-grid-filter-actions button:hover:not(:disabled) {\n background-color: var(--gp-grid-hover);\n}\n\n.gp-grid-filter-actions button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Checkbox list */\n.gp-grid-filter-list {\n max-height: 200px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 4px;\n border: 1px solid var(--gp-grid-border-light);\n border-radius: 4px;\n padding: 6px;\n}\n\n.gp-grid-filter-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 6px;\n border-radius: 3px;\n cursor: pointer;\n}\n\n.gp-grid-filter-option:hover {\n background-color: var(--gp-grid-hover);\n}\n\n.gp-grid-filter-option input[type=\"checkbox\"] {\n margin: 0;\n cursor: pointer;\n}\n\n.gp-grid-filter-option span {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gp-grid-filter-blank {\n font-style: italic;\n color: var(--gp-grid-text-muted);\n}\n\n/* Filter condition row (for number/date filters) */\n.gp-grid-filter-condition {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.gp-grid-filter-row {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.gp-grid-filter-row select {\n height: 30px;\n padding: 0 6px;\n font-family: inherit;\n font-size: 12px;\n border: 1px solid var(--gp-grid-input-border);\n border-radius: 4px;\n background-color: var(--gp-grid-input-bg);\n color: var(--gp-grid-text);\n cursor: pointer;\n}\n\n.gp-grid-filter-row input[type=\"number\"],\n.gp-grid-filter-row input[type=\"date\"],\n.gp-grid-filter-row input[type=\"text\"],\n.gp-grid-filter-text-input {\n flex: 1;\n height: 30px;\n padding: 0 8px;\n font-family: inherit;\n font-size: 12px;\n border: 1px solid var(--gp-grid-input-border);\n border-radius: 4px;\n background-color: var(--gp-grid-input-bg);\n color: var(--gp-grid-text);\n min-width: 0;\n box-sizing: border-box;\n}\n\n.gp-grid-filter-row input:focus {\n outline: none;\n border-color: var(--gp-grid-primary);\n}\n\n.gp-grid-filter-to {\n font-size: 11px;\n color: var(--gp-grid-text-secondary);\n}\n\n.gp-grid-filter-remove {\n width: 24px;\n height: 24px;\n padding: 0;\n font-size: 16px;\n line-height: 1;\n border: none;\n border-radius: 3px;\n background-color: transparent;\n color: var(--gp-grid-text-muted);\n cursor: pointer;\n}\n\n.gp-grid-filter-remove:hover {\n background-color: var(--gp-grid-error-bg);\n color: var(--gp-grid-error-text);\n}\n\n/* AND/OR combination toggle */\n.gp-grid-filter-combination {\n display: flex;\n gap: 4px;\n margin-bottom: 4px;\n}\n\n.gp-grid-filter-combination button {\n flex: 1;\n padding: 4px 8px;\n font-size: 10px;\n font-family: inherit;\n border: 1px solid var(--gp-grid-input-border);\n border-radius: 3px;\n background-color: var(--gp-grid-bg);\n color: var(--gp-grid-text-secondary);\n cursor: pointer;\n}\n\n.gp-grid-filter-combination button.active {\n background-color: var(--gp-grid-primary);\n border-color: var(--gp-grid-primary);\n color: #fff;\n}\n\n/* Add condition button */\n.gp-grid-filter-add {\n width: 100%;\n padding: 6px;\n font-size: 11px;\n font-family: inherit;\n border: 1px dashed var(--gp-grid-border);\n border-radius: 4px;\n background-color: transparent;\n color: var(--gp-grid-text-secondary);\n cursor: pointer;\n}\n\n.gp-grid-filter-add:hover {\n background-color: var(--gp-grid-hover);\n border-color: var(--gp-grid-primary);\n color: var(--gp-grid-primary);\n}\n\n/* Apply / Clear buttons */\n.gp-grid-filter-buttons {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--gp-grid-border-light);\n}\n\n.gp-grid-filter-btn-clear,\n.gp-grid-filter-btn-apply {\n flex: 1;\n padding: 8px 12px;\n font-size: 12px;\n font-family: inherit;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.gp-grid-filter-btn-clear {\n border: 1px solid var(--gp-grid-input-border);\n background-color: var(--gp-grid-bg);\n color: var(--gp-grid-text);\n}\n\n.gp-grid-filter-btn-clear:hover {\n background-color: var(--gp-grid-hover);\n}\n\n.gp-grid-filter-btn-apply {\n border: 1px solid var(--gp-grid-primary);\n background-color: var(--gp-grid-primary);\n color: #fff;\n}\n\n.gp-grid-filter-btn-apply:hover {\n opacity: 0.9;\n}\n`;\n","// packages/core/src/styles/index.ts\n// Combines all style modules and exports injectStyles\n\nimport { variablesStyles } from \"./variables\";\nimport { containerStyles } from \"./container\";\nimport { headerStyles } from \"./header\";\nimport { cellStyles } from \"./cells\";\nimport { statesStyles } from \"./states\";\nimport { scrollbarStyles } from \"./scrollbar\";\nimport { filtersStyles } from \"./filters\";\n\nconst STYLE_ID = \"gp-grid-styles\";\n\n/**\n * Combined grid styles from all modules\n */\nexport const gridStyles: string = [\n variablesStyles,\n containerStyles,\n headerStyles,\n cellStyles,\n statesStyles,\n scrollbarStyles,\n filtersStyles,\n].join(\"\\n\");\n\nlet stylesInjected = false;\n\n/**\n * Inject grid styles into the document head.\n * This is called automatically when the Grid component mounts.\n * Styles are only injected once, even if multiple Grid instances exist.\n */\nexport function injectStyles(): void {\n if (stylesInjected) return;\n if (typeof document === \"undefined\") return; // SSR safety\n\n // Check if styles already exist (e.g., from a previous mount)\n if (document.getElementById(STYLE_ID)) {\n stylesInjected = true;\n return;\n }\n\n const styleElement = document.createElement(\"style\");\n styleElement.id = STYLE_ID;\n styleElement.textContent = gridStyles;\n document.head.appendChild(styleElement);\n stylesInjected = true;\n}\n\n// Re-export individual style modules for advanced usage\nexport { variablesStyles } from \"./variables\";\nexport { containerStyles } from \"./container\";\nexport { headerStyles } from \"./header\";\nexport { cellStyles } from \"./cells\";\nexport { statesStyles } from \"./states\";\nexport { scrollbarStyles } from \"./scrollbar\";\nexport { filtersStyles } from \"./filters\";\n"],"mappings":"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"}
|