@reogrid/pro 0.1.3 → 0.1.5

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/pro-react.d.ts CHANGED
@@ -23,7 +23,46 @@ type ColumnHeaderHit = {
23
23
  type CornerHit = {
24
24
  type: 'corner';
25
25
  };
26
- type HitTestResult = CellHit | RowHeaderHit | ColumnHeaderHit | CornerHit;
26
+ type ColumnHeaderDropdownHit = {
27
+ type: 'column-header-dropdown';
28
+ column: number;
29
+ };
30
+ type OutlineToggleHit = {
31
+ type: 'outline-toggle';
32
+ direction: 'row' | 'column';
33
+ level: number;
34
+ start: number;
35
+ };
36
+ type OutlineLevelButtonHit = {
37
+ type: 'outline-level-button';
38
+ direction: 'row' | 'column';
39
+ level: number;
40
+ };
41
+ type HitTestResult = CellHit | RowHeaderHit | ColumnHeaderHit | ColumnHeaderDropdownHit | CornerHit | OutlineToggleHit | OutlineLevelButtonHit;
42
+ type ContextMenuEventBase = {
43
+ clientX: number;
44
+ clientY: number;
45
+ originalEvent: MouseEvent;
46
+ };
47
+ type RowHeaderContextMenu = ContextMenuEventBase & {
48
+ area: 'row-header';
49
+ row: number;
50
+ selectedRows: [number, number];
51
+ };
52
+ type ColumnHeaderContextMenu = ContextMenuEventBase & {
53
+ area: 'column-header';
54
+ column: number;
55
+ selectedColumns: [number, number];
56
+ };
57
+ type CellContextMenu = ContextMenuEventBase & {
58
+ area: 'cell';
59
+ row: number;
60
+ column: number;
61
+ };
62
+ type CornerContextMenu = ContextMenuEventBase & {
63
+ area: 'corner';
64
+ };
65
+ type ContextMenuEvent = RowHeaderContextMenu | ColumnHeaderContextMenu | CellContextMenu | CornerContextMenu;
27
66
 
28
67
  type SelectionBounds = RangeRect & {
29
68
  topRow: number;
@@ -52,7 +91,18 @@ declare class SelectionRange {
52
91
  selectColumns(startColumn: number, endColumn?: number): void;
53
92
  selectAll(): void;
54
93
  getNormalizedBounds(): SelectionBounds | null;
94
+ /** Returns the normalized selection row/column bounds, or null if inactive. */
95
+ getSelectionBounds(): {
96
+ topRow: number;
97
+ bottomRow: number;
98
+ leftColumn: number;
99
+ rightColumn: number;
100
+ } | null;
55
101
  draw(ctx: CanvasRenderingContext2D): void;
102
+ /** Draw the selection fill and border at the given screen-space rect. */
103
+ drawRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): void;
104
+ containsRow(row: number): boolean;
105
+ containsColumn(column: number): boolean;
56
106
  getActiveCell(): {
57
107
  row: number;
58
108
  column: number;
@@ -63,6 +113,15 @@ declare class SelectionRange {
63
113
  };
64
114
  }
65
115
 
116
+ interface FormulaEngineOptions {
117
+ /**
118
+ * When true (the default), all built-in functions (SUM, IF, …) are
119
+ * registered. Set to false for Lite builds where only basic arithmetic
120
+ * and cell references are supported.
121
+ */
122
+ registerBuiltins?: boolean;
123
+ }
124
+
66
125
  type TextAlign = 'left' | 'center' | 'right';
67
126
  type VerticalAlign = 'top' | 'middle' | 'bottom';
68
127
  type TextWrapMode = 'none' | 'wrap' | 'break-word';
@@ -137,6 +196,20 @@ declare class BorderManager {
137
196
  getCell(row: number, column: number): CellBorderDefinition | undefined;
138
197
  applyAll(borders: Map<string, CellBorderDefinition>): void;
139
198
  setRange(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number, border: BorderLineOptions, sides?: RangeBorderSide[]): void;
199
+ /**
200
+ * Shift all border entries when rows or columns are inserted or deleted.
201
+ *
202
+ * @param axis — 'row' or 'column'
203
+ * @param index — insertion/deletion point (0-based)
204
+ * @param count — positive = insert, negative = delete
205
+ */
206
+ shiftBorders(axis: 'row' | 'column', index: number, count: number): void;
207
+ /** Low-level access for permuteRows. */
208
+ getData(key: string): CellBorderDefinition | undefined;
209
+ setData(key: string, value: CellBorderDefinition): void;
210
+ deleteData(key: string): void;
211
+ /** Swap border entries between two rows within a column range. */
212
+ swapRowCells(rowA: number, rowB: number, startColumn: number, endColumn: number): void;
140
213
  pruneOutsideGrid(): void;
141
214
  private assignBorder;
142
215
  private removeBorderSide;
@@ -194,6 +267,14 @@ declare class MergeManager {
194
267
  endRowBoundary: number;
195
268
  }>>;
196
269
  };
270
+ /**
271
+ * Shift all merged ranges when rows or columns are inserted or deleted.
272
+ *
273
+ * @param axis — 'row' or 'column'
274
+ * @param index — insertion/deletion point (0-based)
275
+ * @param count — positive = insert, negative = delete
276
+ */
277
+ shiftRanges(axis: 'row' | 'column', index: number, count: number): void;
197
278
  pruneOutsideGrid(): void;
198
279
  allAnchors(): MergedCellRange[];
199
280
  private applyRange;
@@ -204,6 +285,115 @@ declare class MergeManager {
204
285
  private normalizeBounds;
205
286
  }
206
287
 
288
+ /** Base config accepted by any cell type. */
289
+ interface CellTypeConfigBase {
290
+ type: string;
291
+ [key: string]: unknown;
292
+ }
293
+ interface DropdownClickContext {
294
+ row?: number;
295
+ column: number;
296
+ isHeader: boolean;
297
+ }
298
+ interface DropdownConfig {
299
+ type: 'dropdown';
300
+ options?: string[];
301
+ onclick?: (context: DropdownClickContext) => void;
302
+ }
303
+ /** Open union — any `{ type: string }` config is accepted. */
304
+ type CellTypeConfig = CellTypeConfigBase;
305
+ declare class CellTypeRegistry {
306
+ private readonly configs;
307
+ set(row: number, col: number, config: CellTypeConfig): void;
308
+ get(row: number, col: number): CellTypeConfig | undefined;
309
+ clear(row: number, col: number): void;
310
+ clearAll(): void;
311
+ }
312
+
313
+ type RowOrColumn = 'row' | 'column';
314
+ interface OutlineGroup {
315
+ /** Starting row/column index */
316
+ start: number;
317
+ /** Number of rows/columns in this group */
318
+ count: number;
319
+ /** Whether this group is collapsed (children hidden) */
320
+ collapsed: boolean;
321
+ }
322
+
323
+ declare class OutlineManager {
324
+ readonly direction: RowOrColumn;
325
+ /** levels[0] = outermost groups, levels[n] = most nested */
326
+ private levels;
327
+ constructor(direction: RowOrColumn);
328
+ /** Returns the deepest level index, or -1 if no outlines exist */
329
+ getMaxLevel(): number;
330
+ /** Total number of levels (0 if no outlines) */
331
+ getLevelCount(): number;
332
+ hasOutlines(): boolean;
333
+ getGroupsAtLevel(level: number): readonly OutlineGroup[];
334
+ getAllGroups(): readonly (readonly OutlineGroup[])[];
335
+ /**
336
+ * Add a group spanning [start, start+count).
337
+ * Automatically assigns the correct level based on containment:
338
+ * larger (enclosing) groups are placed at lower levels (outermost).
339
+ * Returns the assigned level index.
340
+ */
341
+ addGroup(start: number, count: number): number;
342
+ /**
343
+ * Remove a group matching [start, start+count).
344
+ * Returns true if a group was found and removed.
345
+ */
346
+ removeGroup(start: number, count: number): boolean;
347
+ /**
348
+ * Find the group at a specific level and start index.
349
+ */
350
+ findGroup(level: number, start: number): OutlineGroup | null;
351
+ /**
352
+ * Collapse a group. Returns the set of indices that should be hidden.
353
+ */
354
+ collapseGroup(level: number, start: number): Set<number>;
355
+ /**
356
+ * Expand a group. Returns the set of indices that should be shown.
357
+ */
358
+ expandGroup(level: number, start: number): Set<number>;
359
+ /**
360
+ * Collapse all groups at the given level and deeper.
361
+ * Expand all groups at levels shallower than the given level.
362
+ * Returns the complete set of hidden indices after the operation.
363
+ */
364
+ collapseToLevel(level: number): void;
365
+ /**
366
+ * Get all indices hidden by collapsed outline groups.
367
+ */
368
+ getHiddenIndices(): Set<number>;
369
+ /**
370
+ * Get hidden indices for a specific group (excluding indices hidden by inner collapsed groups).
371
+ */
372
+ private getGroupHiddenIndices;
373
+ /**
374
+ * Shift all groups when rows/columns are inserted or deleted.
375
+ * @param index - The insert/delete position
376
+ * @param delta - Positive for insert, negative for delete
377
+ */
378
+ shiftGroups(index: number, delta: number): void;
379
+ /** Remove trailing empty levels */
380
+ private trimEmptyLevels;
381
+ /** Clear all outline groups */
382
+ clear(): void;
383
+ /**
384
+ * Get a snapshot for serialization/undo.
385
+ */
386
+ getSnapshot(): OutlineGroup[][];
387
+ /**
388
+ * Restore from a snapshot.
389
+ */
390
+ restoreSnapshot(snapshot: OutlineGroup[][]): void;
391
+ }
392
+
393
+ type AlternateRowColors = {
394
+ evenColor: string;
395
+ oddColor: string;
396
+ };
207
397
  type SelectionChangeListener = (cell: {
208
398
  row: number;
209
399
  column: number;
@@ -221,19 +411,33 @@ type ScrollChangeListener = (offset: {
221
411
  x: number;
222
412
  y: number;
223
413
  }) => void;
414
+ type ContextMenuListener = (event: ContextMenuEvent) => void;
415
+ type ProtectedCellEditListener = (payload: {
416
+ row: number;
417
+ column: number;
418
+ }) => void;
224
419
  type WorksheetExportCell = {
225
420
  row: number;
226
421
  column: number;
227
422
  value?: string;
228
423
  style?: CellStyle;
424
+ /** For formula cells: the cached computed value for the xlsx <v> tag */
425
+ cachedValue?: string | number | boolean;
229
426
  };
230
427
  interface WorksheetExportSnapshot {
231
428
  cells: WorksheetExportCell[];
232
429
  merges: MergedCellRange[];
430
+ borders: Map<string, CellBorderDefinition>;
233
431
  columnWidths: number[];
234
432
  rowHeights: number[];
235
433
  rows: number;
236
434
  columns: number;
435
+ frozenRows: number;
436
+ frozenColumns: number;
437
+ }
438
+ interface WorksheetOptions {
439
+ /** Options forwarded to the FormulaEngine (e.g. disable built-in functions for Lite). */
440
+ formula?: FormulaEngineOptions;
237
441
  }
238
442
  declare class Worksheet {
239
443
  protected pixelRatio: number;
@@ -246,6 +450,8 @@ declare class Worksheet {
246
450
  private readonly columnCustomWidths;
247
451
  readonly rowHeights: number[];
248
452
  private readonly rowCustomHeights;
453
+ private readonly rowHidden;
454
+ private readonly columnHidden;
249
455
  private columnBoundaries;
250
456
  private rowBoundaries;
251
457
  headerColumnWidth: number;
@@ -263,19 +469,45 @@ declare class Worksheet {
263
469
  private readonly viewportSizeListeners;
264
470
  private readonly scrollListeners;
265
471
  private readonly structureListeners;
472
+ private readonly contextMenuListeners;
266
473
  private readonly defaultStyle;
267
474
  private _showGridLines;
475
+ private _alternateRowColors;
268
476
  protected resizeGuide: {
269
477
  type: 'column' | 'row';
270
478
  position: number;
271
479
  } | null;
272
480
  private readonly formulaEngine;
481
+ protected readonly cellTypeRegistry: CellTypeRegistry;
482
+ protected readonly rowOutlines: OutlineManager;
483
+ protected readonly columnOutlines: OutlineManager;
484
+ private readonly headerDropdowns;
485
+ private _protected;
486
+ private readonly cellLockStates;
487
+ private readonly protectedCellEditListeners;
273
488
  protected scrollX: number;
274
489
  protected scrollY: number;
490
+ protected frozenRows: number;
491
+ protected frozenColumns: number;
275
492
  private contentWidth;
276
493
  private contentHeight;
277
- constructor();
494
+ private _renderSuspended;
495
+ private _renderPending;
496
+ constructor(options?: WorksheetOptions);
278
497
  render(): void;
498
+ /** Override in rendering subclasses to perform actual drawing. */
499
+ protected renderImpl(): void;
500
+ /**
501
+ * Suspend rendering. All render() calls become no-ops until resumeRender().
502
+ * Use for batch operations that would otherwise trigger thousands of redraws.
503
+ */
504
+ suspendRender(): void;
505
+ /**
506
+ * Resume rendering and flush a single render if any were requested while suspended.
507
+ */
508
+ resumeRender(): void;
509
+ /** Rebuild formula dependency graph and re-evaluate all formulas. */
510
+ rebuildFormulas(): void;
279
511
  getScrollContentSize(): {
280
512
  width: number;
281
513
  height: number;
@@ -288,15 +520,25 @@ declare class Worksheet {
288
520
  onViewportSizeChange(listener: ViewportSizeListener): () => void;
289
521
  onScrollChange(listener: ScrollChangeListener): () => void;
290
522
  onStructureChange(listener: () => void): () => void;
523
+ getFrozenRows(): number;
524
+ getFrozenColumns(): number;
525
+ setFrozenRows(count: number): void;
526
+ setFrozenColumns(count: number): void;
527
+ getFrozenWidth(): number;
528
+ getFrozenHeight(): number;
291
529
  getCellFromPoint(x: number, y: number): {
292
530
  row: number;
293
531
  column: number;
294
532
  } | null;
295
533
  hitTest(x: number, y: number): HitTestResult | null;
534
+ /** Converts a screen X coordinate to grid content X, accounting for frozen columns. */
535
+ protected screenToGridX(screenX: number): number;
536
+ /** Converts a screen Y coordinate to grid content Y, accounting for frozen rows. */
537
+ protected screenToGridY(screenY: number): number;
296
538
  updateBoundaries(): void;
297
539
  ensureBoundaries(): void;
298
- protected getColumnBoundaries(): readonly number[];
299
- protected getRowBoundaries(): readonly number[];
540
+ getColumnBoundaries(): readonly number[];
541
+ getRowBoundaries(): readonly number[];
300
542
  protected getColumnWidths(): readonly number[];
301
543
  protected getRowHeights(): readonly number[];
302
544
  protected getCellBorders(): Iterable<[string, CellBorderDefinition]>;
@@ -323,9 +565,23 @@ declare class Worksheet {
323
565
  getCellInput(row: number, column: number): string | undefined;
324
566
  protected getRichTextRuns(row: number, column: number): RichTextRun[] | undefined;
325
567
  setCellInput(row: number, column: number, value: string): void;
568
+ setCellNumberFormat(row: number, column: number, formatCode: string): void;
569
+ getCellNumberFormat(row: number, column: number): string | undefined;
570
+ clearCellNumberFormat(row: number, column: number): void;
571
+ setCellType(row: number, col: number, config: CellTypeConfig): void;
572
+ getCellType(row: number, col: number): CellTypeConfig | undefined;
573
+ clearCellType(row: number, col: number): void;
574
+ setHeaderDropdown(column: number, config: DropdownConfig | null): void;
575
+ getHeaderDropdown(column: number): DropdownConfig | undefined;
576
+ hasHeaderDropdown(column: number): boolean;
577
+ /** Check if a screen X coordinate hits the dropdown button area of a cell. */
578
+ isDropdownButtonHit(row: number, column: number, screenX: number): boolean;
326
579
  loadCells(cells: Iterable<CellInputValue>): void;
327
580
  private updateCellDisplayValue;
328
581
  protected getCellText(row: number, column: number): string | null;
582
+ getCellRawStyle(row: number, column: number): Partial<CellStyle> | undefined;
583
+ replaceCellStyle(row: number, column: number, style: Partial<CellStyle> | undefined): void;
584
+ clearCellStyle(row: number, column: number): void;
329
585
  getComputedCellStyle(row: number, column: number): CellStyle;
330
586
  getActiveCellStyle(): CellStyle;
331
587
  getDefaultCellStyle(): CellStyle;
@@ -344,6 +600,30 @@ declare class Worksheet {
344
600
  setSelectionRange(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number): void;
345
601
  onSelectionChange(listener: SelectionChangeListener): () => void;
346
602
  onCellValueChange(listener: CellValueChangeListener): () => void;
603
+ onContextMenu(listener: ContextMenuListener): () => void;
604
+ emitContextMenu(event: ContextMenuEvent): void;
605
+ /** Whether the worksheet is in protected mode (all cells locked by default). */
606
+ get protected(): boolean;
607
+ set protected(value: boolean);
608
+ /** Set an explicit lock state on a cell, overriding the worksheet default. */
609
+ setCellLock(row: number, column: number, state: 'locked' | 'unlocked'): void;
610
+ /** Remove the explicit lock state from a cell, reverting to worksheet default. */
611
+ clearCellLock(row: number, column: number): void;
612
+ /** Get the explicit lock state of a cell, or `undefined` if inheriting from worksheet. */
613
+ getCellLock(row: number, column: number): 'locked' | 'unlocked' | undefined;
614
+ /** Set an explicit lock state on all cells in a range. */
615
+ setRangeLock(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number, state: 'locked' | 'unlocked'): void;
616
+ /** Remove explicit lock states from all cells in a range. */
617
+ clearRangeLock(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number): void;
618
+ /**
619
+ * Resolve the effective protection state of a cell.
620
+ * Explicit lock state takes precedence; otherwise inherits from `worksheet.protected`.
621
+ */
622
+ isCellProtected(row: number, column: number): boolean;
623
+ /** Returns `true` if any cell in the range is protected. */
624
+ isRangeProtected(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number): boolean;
625
+ onProtectedCellEdit(listener: ProtectedCellEditListener): () => void;
626
+ emitProtectedCellEdit(row: number, column: number): void;
347
627
  notifySelectionChanged(): void;
348
628
  isActiveCell(row: number, column: number): boolean;
349
629
  private applyToSelection;
@@ -406,10 +686,86 @@ declare class Worksheet {
406
686
  protected measureRowHeight(_row: number): number;
407
687
  protected measureCellContentHeight(_row: number, _column: number, _style: CellStyle): number;
408
688
  protected measureCellContentWidth(_row: number, _column: number, _style: CellStyle): number;
689
+ isRowHidden(row: number): boolean;
690
+ setRowHidden(row: number, hidden: boolean): void;
691
+ setRowsHidden(rows: number[], hidden: boolean): void;
692
+ isColumnHidden(column: number): boolean;
693
+ setColumnHidden(column: number, hidden: boolean): void;
694
+ setColumnsHidden(columns: number[], hidden: boolean): void;
695
+ private static readonly OUTLINE_LEVEL_SIZE;
696
+ private static readonly OUTLINE_PANEL_PADDING;
697
+ getRowOutlines(): OutlineManager;
698
+ getColumnOutlines(): OutlineManager;
699
+ /** Width of the row outline panel (left of row headers). 0 if no row outlines. */
700
+ getOutlinePanelWidth(): number;
701
+ /** Height of the column outline panel (above column headers). 0 if no column outlines. */
702
+ getOutlinePanelHeight(): number;
703
+ /** Recalculate header sizes based on outline levels */
704
+ private updateHeaderSizes;
705
+ /** Apply outline hidden state to row/column hidden arrays */
706
+ private syncOutlineHidden;
707
+ groupRows(startRow: number, endRow: number): void;
708
+ ungroupRows(startRow: number, endRow: number): void;
709
+ groupColumns(startColumn: number, endColumn: number): void;
710
+ ungroupColumns(startColumn: number, endColumn: number): void;
711
+ collapseRowOutline(level: number, start: number): void;
712
+ expandRowOutline(level: number, start: number): void;
713
+ collapseColumnOutline(level: number, start: number): void;
714
+ expandColumnOutline(level: number, start: number): void;
715
+ collapseRowsToLevel(level: number): void;
716
+ collapseColumnsToLevel(level: number): void;
717
+ insertRows(index: number, count: number): void;
718
+ insertColumns(index: number, count: number): void;
719
+ deleteRows(index: number, count: number): void;
720
+ deleteColumns(index: number, count: number): void;
721
+ /**
722
+ * Write to cellInputs without triggering the formula engine.
723
+ * Used by FormulaEngine.onStructureChange() to update formula strings
724
+ * after reference shifting — avoids infinite recursion.
725
+ */
726
+ setCellInputDirect(row: number, column: number, value: string): void;
727
+ /**
728
+ * Swap cell data between two rows within [startColumn, endColumn].
729
+ * Swaps: cellInputs, cellRichText, cellDisplayOverrides, cellNumberFormats,
730
+ * cellFormatting, borders. Does NOT swap rowHeight or rowHidden.
731
+ * Does NOT trigger render — caller is responsible.
732
+ */
733
+ swapRows(rowA: number, rowB: number, startColumn: number, endColumn: number): void;
734
+ /**
735
+ * Apply a row permutation within [startColumn, endColumn].
736
+ * `targetRows[i]` receives the data currently at `sourceRows[i]`.
737
+ * Both arrays must have the same length and targetRows must equal the
738
+ * set of participating row positions (a permutation of sourceRows is NOT
739
+ * required — sourceRows may list the same rows in a different order).
740
+ *
741
+ * Operates directly on internal Maps for performance — no per-cell
742
+ * render(), formulaEngine, or event callbacks are triggered.
743
+ * Caller must call formulaEngine.rebuildAll() and render() afterwards.
744
+ */
745
+ permuteRows(sourceRows: number[], targetRows: number[], startColumn: number, endColumn: number): void;
746
+ private swapMapEntries;
747
+ /** Get all cell data in a range (for backup before deletion). */
748
+ getRangeData(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number): Map<string, {
749
+ input?: string;
750
+ richText?: RichTextRun[];
751
+ displayOverride?: string;
752
+ numberFormat?: string;
753
+ formatting?: Partial<CellStyle>;
754
+ }>;
755
+ /** Restore cell data from a backup (used by undo of delete operations). */
756
+ restoreRangeData(data: Map<string, {
757
+ input?: string;
758
+ richText?: RichTextRun[];
759
+ displayOverride?: string;
760
+ numberFormat?: string;
761
+ formatting?: Partial<CellStyle>;
762
+ }>): void;
409
763
  setGridSize(rows: number, columns: number): void;
410
764
  setShowGridLines(visible: boolean): void;
411
765
  getShowGridLines(): boolean;
412
766
  protected shouldRenderGridLines(): boolean;
767
+ setAlternateRowColors(colors: AlternateRowColors | null): void;
768
+ getAlternateRowColors(): AlternateRowColors | null;
413
769
  setResizeGuide(guide: {
414
770
  type: 'column' | 'row';
415
771
  position: number;
@@ -431,6 +787,11 @@ declare class Worksheet {
431
787
  right: number;
432
788
  } | null;
433
789
  private clampSelectionToBounds;
790
+ /**
791
+ * Shift keys in all cell data Maps when rows/columns are inserted or deleted.
792
+ * count > 0 = insert, count < 0 = delete.
793
+ */
794
+ private shiftCellDataMaps;
434
795
  private pruneCellDataOutsideGrid;
435
796
  private ensureArrayLength;
436
797
  private emitSelectionChange;
@@ -466,6 +827,73 @@ type WorksheetImageInfo = {
466
827
  anchor: WorksheetImagePlacement;
467
828
  };
468
829
 
830
+ interface ColumnFilterState {
831
+ column: number;
832
+ /** The set of cell text values currently selected (shown). null = no filter (all shown). */
833
+ selectedValues: Set<string> | null;
834
+ }
835
+ interface AutoFilterConfig {
836
+ /** The row containing column labels. Data rows start from headerRow + 1. */
837
+ headerRow: number;
838
+ /** First column in the filter range (inclusive). */
839
+ startColumn: number;
840
+ /** Last column in the filter range (inclusive). */
841
+ endColumn: number;
842
+ }
843
+ interface FilterChangeEvent {
844
+ column: number;
845
+ selectedValues: Set<string> | null;
846
+ hiddenRowCount: number;
847
+ }
848
+
849
+ type SortOrder = 'asc' | 'desc';
850
+ interface SortResult {
851
+ /**
852
+ * Snapshot of row positions before sorting.
853
+ * `originalOrder[i]` is the row index that was at position `startRow + i` before sorting.
854
+ */
855
+ originalOrder: number[];
856
+ }
857
+
858
+ declare class AutoColumnFilter {
859
+ private readonly worksheet;
860
+ private readonly config;
861
+ private readonly columnFilters;
862
+ private readonly filterChangeListeners;
863
+ private currentSort;
864
+ constructor(worksheet: Worksheet, config: AutoFilterConfig);
865
+ getConfig(): Readonly<AutoFilterConfig>;
866
+ /** Get unique cell input values in a column (below header row). Sorted alphabetically. */
867
+ getColumnValues(column: number): string[];
868
+ /** Set the filter for a column. Pass null to clear (show all). */
869
+ setColumnFilter(column: number, selectedValues: Set<string> | null): void;
870
+ /** Get current filter state for a column (null if no filter). */
871
+ getColumnFilter(column: number): ColumnFilterState | null;
872
+ /** Returns true if any column has an active filter. */
873
+ hasActiveFilters(): boolean;
874
+ /** Apply all active filters. Iterates rows once and batch-updates hidden state. */
875
+ apply(): void;
876
+ /** Clear all column filters and unhide all data rows. */
877
+ clearAll(): void;
878
+ /** Listen for filter changes. Returns unsubscribe function. */
879
+ onFilterChange(listener: (event: FilterChangeEvent) => void): () => void;
880
+ /** Sort data rows by the given column. Returns the result for undo support. */
881
+ sortColumn(column: number, order: SortOrder): SortResult;
882
+ /** Get the current sort state (null if not sorted). */
883
+ getSortState(): {
884
+ column: number;
885
+ order: SortOrder;
886
+ } | null;
887
+ /** Set the sort state indicator (does not sort — use sortColumn for that). */
888
+ setSortState(column: number, order: SortOrder): void;
889
+ /** Clear the sort state (does not revert data — use undo for that). */
890
+ clearSortState(): void;
891
+ /** Destroy: clear filters, remove listeners. */
892
+ destroy(): void;
893
+ private getHiddenRowCount;
894
+ private emitFilterChange;
895
+ }
896
+
469
897
  type CellStyleInput = Partial<CellStyle>;
470
898
  declare class CellHandle {
471
899
  private readonly ws;
@@ -480,6 +908,20 @@ declare class CellHandle {
480
908
  setValue(v: string): this;
481
909
  getStyle(): CellStyle;
482
910
  setStyle(s: CellStyleInput): this;
911
+ get format(): string | undefined;
912
+ set format(code: string | undefined);
913
+ setFormat(code: string): this;
914
+ setType(config: CellTypeConfig): this;
915
+ clearType(): this;
916
+ /** Returns `true` if this cell is part of a merged range. */
917
+ get isMerged(): boolean;
918
+ /** Returns the merged range this cell belongs to, or `null` if not merged. */
919
+ get mergeRange(): MergedCellRange | null;
920
+ /** Explicit lock state, or `undefined` if inheriting from worksheet. */
921
+ get locked(): 'locked' | 'unlocked' | undefined;
922
+ set locked(state: 'locked' | 'unlocked' | undefined);
923
+ /** Resolved protection state (considers worksheet.protected + explicit lock). */
924
+ get isProtected(): boolean;
483
925
  }
484
926
  /** No-op handle returned when a cell address is outside the allowed constraint bounds. */
485
927
  declare class NullCellHandle {
@@ -491,6 +933,16 @@ declare class NullCellHandle {
491
933
  setValue(_v: string): this;
492
934
  getStyle(): CellStyle;
493
935
  setStyle(_s: CellStyleInput): this;
936
+ get format(): string | undefined;
937
+ set format(_code: string | undefined);
938
+ setFormat(_code: string): this;
939
+ setType(_config: CellTypeConfig): this;
940
+ clearType(): this;
941
+ get isMerged(): boolean;
942
+ get mergeRange(): MergedCellRange | null;
943
+ get locked(): 'locked' | 'unlocked' | undefined;
944
+ set locked(_state: 'locked' | 'unlocked' | undefined);
945
+ get isProtected(): boolean;
494
946
  }
495
947
  declare class RangeHandle {
496
948
  private readonly ws;
@@ -503,7 +955,10 @@ declare class RangeHandle {
503
955
  setBackgroundColor(color: string): this;
504
956
  merge(): this;
505
957
  unmerge(): this;
958
+ setFormat(code: string): this;
506
959
  border(options: BorderLineOptions, sides?: RangeBorderSide[]): this;
960
+ setLock(state: 'locked' | 'unlocked'): this;
961
+ clearLock(): this;
507
962
  }
508
963
  /** No-op handle returned when a range is outside the allowed constraint bounds. */
509
964
  declare class NullRangeHandle {
@@ -511,7 +966,10 @@ declare class NullRangeHandle {
511
966
  setBackgroundColor(_c: string): this;
512
967
  merge(): this;
513
968
  unmerge(): this;
969
+ setFormat(_code: string): this;
514
970
  border(_o: BorderLineOptions, _s?: RangeBorderSide[]): this;
971
+ setLock(_state: 'locked' | 'unlocked'): this;
972
+ clearLock(): this;
515
973
  }
516
974
  declare class ColumnHandle {
517
975
  private readonly ws;
@@ -546,23 +1004,30 @@ declare class CanvasWorksheet extends Worksheet {
546
1004
  private worksheetImages;
547
1005
  private readonly imageUrlCache;
548
1006
  private readonly imageListeners;
549
- constructor(canvas: HTMLCanvasElement, constraints?: WorksheetConstraints);
1007
+ private autoFilter;
1008
+ constructor(canvas: HTMLCanvasElement, constraints?: WorksheetConstraints, worksheetOptions?: WorksheetOptions);
550
1009
  get showGridLines(): boolean;
551
1010
  set showGridLines(visible: boolean);
1011
+ get alternateRowColors(): AlternateRowColors | null;
1012
+ set alternateRowColors(colors: AlternateRowColors | null);
552
1013
  cell(a1: string): CellHandle | NullCellHandle;
1014
+ cell(row: number, column: number): CellHandle | NullCellHandle;
553
1015
  range(a1Range: string): RangeHandle | NullRangeHandle;
1016
+ range(topRow: number, leftColumn: number, bottomRow: number, rightColumn: number): RangeHandle | NullRangeHandle;
554
1017
  column(index: number): ColumnHandle;
555
1018
  row(index: number): RowHandle;
556
1019
  loadFromUrl(url: string | URL, options?: LoadXlsxOptions): Promise<void>;
557
1020
  loadFromFile(file: File, options?: LoadXlsxOptions): Promise<void>;
558
1021
  loadFromXlsx(data: ArrayBuffer | Uint8Array | string, options?: LoadXlsxOptions): Promise<void>;
559
1022
  saveAsXlsx(options?: SaveXlsxOptions): void;
1023
+ /** Returns the display text for a cell (formatted/computed value). */
1024
+ getDisplayText(row: number, column: number): string | null;
560
1025
  getImages(): WorksheetImageInfo[];
561
1026
  createImageUrl(imageId: string): string;
562
1027
  revokeImageUrl(imageId: string): void;
563
1028
  onImagesChange(listener: (images: WorksheetImageInfo[]) => void): () => void;
564
1029
  resize(width?: number, height?: number): void;
565
- render(): void;
1030
+ protected renderImpl(): void;
566
1031
  private isOutOfBounds;
567
1032
  private isRangeOutOfBounds;
568
1033
  private decodeA1Cell;
@@ -576,23 +1041,77 @@ declare class CanvasWorksheet extends Worksheet {
576
1041
  private triggerDownload;
577
1042
  private clear;
578
1043
  private withBodyClip;
1044
+ private drawSelectionPerViewport;
579
1045
  private drawResizeGuide;
580
1046
  protected measureColumnWidth(column: number): number;
581
1047
  protected measureRowHeight(row: number): number;
582
1048
  protected measureCellContentHeight(row: number, column: number, style: CellStyle): number;
583
1049
  protected measureCellContentWidth(row: number, column: number, style: CellStyle): number;
1050
+ createAutoFilter(config: AutoFilterConfig): AutoColumnFilter;
1051
+ removeAutoFilter(): void;
1052
+ getAutoFilter(): AutoColumnFilter | null;
1053
+ private onFilterHeaderDropdownClick;
1054
+ private readonly filterDropdownListeners;
1055
+ onFilterDropdownClick(listener: (column: number) => void): () => void;
1056
+ /**
1057
+ * Sort rows by the given column. If an AutoFilter is active, sorts within
1058
+ * its data range and updates its sort state. Otherwise sorts all rows.
1059
+ * Does NOT go through ActionManager — caller should wrap in SortColumnAction
1060
+ * if undo support is desired.
1061
+ */
1062
+ sortColumn(column: number, order: SortOrder): SortResult;
1063
+ /** Get the current sort state (from the active AutoFilter, if any). */
1064
+ getSortState(): {
1065
+ column: number;
1066
+ order: SortOrder;
1067
+ } | null;
584
1068
  private createSheetRendererContext;
585
1069
  }
586
1070
 
1071
+ interface ActionTargetRange {
1072
+ topRow: number;
1073
+ leftColumn: number;
1074
+ bottomRow: number;
1075
+ rightColumn: number;
1076
+ }
1077
+ interface WorksheetAction {
1078
+ readonly name: string;
1079
+ /** When `true`, ActionManager checks cell protection before executing. */
1080
+ readonly modifiesCellData?: boolean;
1081
+ do(worksheet: Worksheet): void;
1082
+ undo(worksheet: Worksheet): void;
1083
+ /** Range to select after do/undo. If undefined, selection is not changed. */
1084
+ getTargetRange?(): ActionTargetRange;
1085
+ }
1086
+
1087
+ declare class ActionManager {
1088
+ private readonly undoStack;
1089
+ private readonly redoStack;
1090
+ private readonly capacity;
1091
+ private readonly worksheet;
1092
+ private readonly listeners;
1093
+ constructor(worksheet: Worksheet, capacity?: number);
1094
+ execute(action: WorksheetAction): boolean;
1095
+ undo(): boolean;
1096
+ redo(): boolean;
1097
+ canUndo(): boolean;
1098
+ canRedo(): boolean;
1099
+ clear(): void;
1100
+ onUndoRedoChange(listener: () => void): () => void;
1101
+ private selectActionTarget;
1102
+ private notifyListeners;
1103
+ }
1104
+
587
1105
  interface BeginEditOptions {
588
1106
  presetValue?: string;
589
1107
  selectAll?: boolean;
590
1108
  }
591
1109
  declare class CellEditor {
592
1110
  private readonly worksheet;
1111
+ private readonly actionManager;
593
1112
  private readonly input;
594
1113
  private editingCell;
595
- constructor(worksheet: CanvasWorksheet);
1114
+ constructor(worksheet: CanvasWorksheet, actionManager?: ActionManager);
596
1115
  beginEdit(row: number, column: number, options?: BeginEditOptions): void;
597
1116
  commit(): void;
598
1117
  cancel(): void;
@@ -606,21 +1125,38 @@ declare class CellEditor {
606
1125
  private commitAndMove;
607
1126
  }
608
1127
 
1128
+ declare class DropdownOverlay {
1129
+ private readonly worksheet;
1130
+ private readonly listElement;
1131
+ private currentCallback;
1132
+ private outsideClickHandler;
1133
+ constructor(worksheet: CanvasWorksheet);
1134
+ open(row: number, column: number, options: string[], onSelect: (value: string) => void): void;
1135
+ close(): void;
1136
+ isOpen(): boolean;
1137
+ destroy(): void;
1138
+ private selectItem;
1139
+ private createListElement;
1140
+ }
1141
+
609
1142
  declare class PointerController {
610
1143
  private readonly worksheet;
611
1144
  private readonly canvas;
612
1145
  private readonly eventTarget;
613
1146
  private readonly cellEditor;
1147
+ private readonly dropdownOverlay;
614
1148
  private isDragging;
615
1149
  private dragContext;
616
1150
  private isResizing;
617
1151
  private resizeContext;
618
- constructor(worksheet: CanvasWorksheet, cellEditor: CellEditor, eventTarget?: HTMLElement);
1152
+ private readonly actionManager;
1153
+ constructor(worksheet: CanvasWorksheet, cellEditor: CellEditor, dropdownOverlay: DropdownOverlay, eventTarget?: HTMLElement, actionManager?: ActionManager);
619
1154
  destroy(): void;
620
1155
  private handleMouseDown;
621
1156
  private handleMouseMove;
622
1157
  private handleMouseUp;
623
1158
  private handleDoubleClick;
1159
+ private handleContextMenu;
624
1160
  private getCanvasPoint;
625
1161
  private resolveRowFromHit;
626
1162
  private resolveColumnFromHit;
@@ -630,13 +1166,34 @@ declare class PointerController {
630
1166
  private setCursor;
631
1167
  }
632
1168
 
1169
+ declare class ClipboardService {
1170
+ private readonly worksheet;
1171
+ private readonly actionManager;
1172
+ /** Internal buffer for same-instance paste and cut tracking. */
1173
+ private internalBuffer;
1174
+ private cutOrigin;
1175
+ constructor(worksheet: Worksheet, actionManager: ActionManager);
1176
+ copy(): Promise<void>;
1177
+ cut(): Promise<void>;
1178
+ pasteFromEvent(event: ClipboardEvent): void;
1179
+ pasteFromClipboardApi(): Promise<void>;
1180
+ delete(): void;
1181
+ private pasteText;
1182
+ private buildRangeData;
1183
+ private writeToClipboard;
1184
+ private fallbackCopy;
1185
+ }
1186
+
633
1187
  declare class KeyboardController {
634
1188
  private readonly worksheet;
635
1189
  private readonly cellEditor;
636
- constructor(worksheet: CanvasWorksheet, cellEditor: CellEditor);
1190
+ private readonly actionManager;
1191
+ private readonly clipboardService;
1192
+ constructor(worksheet: CanvasWorksheet, cellEditor: CellEditor, actionManager: ActionManager, clipboardService: ClipboardService);
637
1193
  destroy(): void;
638
1194
  private handleKeyDown;
639
1195
  private handleNavigationKeys;
1196
+ private handlePaste;
640
1197
  private ensureActiveCell;
641
1198
  }
642
1199
 
@@ -650,9 +1207,14 @@ interface ReogridOptions {
650
1207
  maxRows?: number;
651
1208
  /** Maximum number of columns. Writes beyond this limit are silently ignored. */
652
1209
  maxCols?: number;
1210
+ /** Maximum number of undo/redo steps (default: 30). */
1211
+ undoCapacity?: number;
1212
+ /** Options forwarded to the FormulaEngine (e.g. disable built-in functions for Lite). */
1213
+ formula?: WorksheetOptions['formula'];
653
1214
  }
654
1215
  interface ReogridInstance {
655
1216
  worksheet: CanvasWorksheet;
1217
+ actionManager: ActionManager;
656
1218
  cellEditor: CellEditor;
657
1219
  pointerController: PointerController;
658
1220
  keyboardController: KeyboardController;