cenitu-table 0.2.0 → 0.4.0-beta.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.
@@ -1,14 +1,17 @@
1
- import { type CellRange } from '../formula/address.js';
1
+ import { type CellAddress, type CellRange } from '../formula/address.js';
2
2
  import { FormulaEngine } from '../formula/engine.js';
3
3
  import { type CellValue, type RawCell } from '../formula/values.js';
4
4
  import { type DataRow } from './binding.js';
5
5
  import { type CellMeta, type CellStyle, type ConditionalFormat } from './cellTypes.js';
6
6
  import { type CsvOptions } from '../export/csv.js';
7
+ import { type ImportOptions, type ImportSheet } from '../export/xlsxRead.js';
7
8
  import type { ExportSheet } from '../export/types.js';
8
9
  import { type MenuEntry } from './contextMenu.js';
9
10
  import { type ColumnFilter, type SortOrder } from './filters.js';
10
11
  import { type MergeArea } from './merges.js';
11
12
  import { type Messages } from './messages.js';
13
+ import { type SearchOptions } from './search.js';
14
+ import { type QueryChangeReason, type ServerFilter, type ServerQuery } from './serverMode.js';
12
15
  export interface GridOptions {
13
16
  /** Rows as arrays, or as objects bound to columns through `columns[i].data` (edits are written back). */
14
17
  data?: RawCell[][] | DataRow[];
@@ -60,6 +63,60 @@ export interface GridOptions {
60
63
  conditionalFormats?: ConditionalFormat[];
61
64
  /** Columns hidden initially. */
62
65
  hiddenColumns?: number[];
66
+ /** Ctrl+F / Ctrl+H open a find and replace box (they stay the browser's shortcuts otherwise). */
67
+ search?: boolean;
68
+ /** Shows a bar above the grid with the active cell's address and content; editable. */
69
+ formulaBar?: boolean;
70
+ /**
71
+ * 'server': the application fetches pages; the grid never sorts or filters itself and reports
72
+ * what it needs through the `afterQueryChange` hook. Formulas are off unless `formulas` is set,
73
+ * and rows cannot be added locally. Default 'client'.
74
+ */
75
+ dataMode?: 'client' | 'server';
76
+ /** Server mode: object property (or function) identifying a row across pages. */
77
+ rowKey?: string | ((row: DataRow | RawCell[]) => unknown);
78
+ /** Server mode: page, sort and filters. Set again to change them from outside. */
79
+ query?: Partial<ServerQuery>;
80
+ /** Server mode: records matching the query, for the page bar. */
81
+ totalCount?: number;
82
+ /** Server mode: old rows stay visible, dimmed, until new ones arrive. */
83
+ loading?: boolean;
84
+ /** Server mode: true shows the default message, a string shows that message. */
85
+ loadError?: boolean | string;
86
+ /** Server mode: page sizes offered (default 25, 50, 100). */
87
+ pageSizes?: number[];
88
+ /** Server mode: page bar below the grid. Default true. */
89
+ pager?: boolean;
90
+ /** Server mode: fills the page bar's slot for application buttons. */
91
+ pagerActions?: (slot: HTMLElement) => void;
92
+ /** Server mode: replaces the content of the empty, loading and error messages. */
93
+ renderStatus?: (el: HTMLElement, state: 'empty' | 'loading' | 'error') => void;
94
+ /** Column widths, order and visibility to start with, from `getColumnLayout`. */
95
+ columnLayout?: ColumnLayout;
96
+ }
97
+ /** Saved column arrangement. Only columns with an id (`columns[i].id` or `data`) are included. */
98
+ export interface ColumnLayout {
99
+ columns: {
100
+ id: string;
101
+ width: number;
102
+ hidden: boolean;
103
+ }[];
104
+ }
105
+ export interface RowEvent {
106
+ /** Visual row. */
107
+ row: number;
108
+ physicalRow: number;
109
+ col: number;
110
+ columnId?: string;
111
+ /** Needs the `rowKey` option. */
112
+ rowKey?: unknown;
113
+ /** The source object (object mode) or raw row. */
114
+ rowData: DataRow | RawCell[] | undefined;
115
+ originalEvent: MouseEvent;
116
+ }
117
+ export interface SortKey {
118
+ col: number;
119
+ order: SortOrder;
63
120
  }
64
121
  /** Row indexes: `row` is visual (as displayed, -1 if hidden), `physicalRow` is the data row. */
65
122
  export interface CellChange {
@@ -68,10 +125,14 @@ export interface CellChange {
68
125
  col: number;
69
126
  /** Bound property in object mode. */
70
127
  prop?: string;
128
+ /** Column id, when the column has one. */
129
+ columnId?: string;
130
+ /** Server mode: `rowKey` value of the row; indexes change between pages, this does not. */
131
+ rowKey?: unknown;
71
132
  oldValue: RawCell;
72
133
  newValue: RawCell;
73
134
  }
74
- export type ChangeSource = 'edit' | 'paste' | 'cut' | 'clear' | 'autofill' | 'undo' | 'redo' | 'api';
135
+ export type ChangeSource = 'edit' | 'paste' | 'cut' | 'clear' | 'autofill' | 'replace' | 'undo' | 'redo' | 'api';
75
136
  export type AlterAction = 'insert_row_above' | 'insert_row_below' | 'remove_row' | 'insert_col_start' | 'insert_col_end' | 'remove_col';
76
137
  interface HookMap {
77
138
  /** Return false to cancel; changes may be modified in place. */
@@ -80,6 +141,16 @@ interface HookMap {
80
141
  afterSelection: (row: number, col: number, row2: number, col2: number) => void;
81
142
  afterAlter: (action: AlterAction, index: number, amount: number) => void;
82
143
  afterColumnSort: (col: number, order: SortOrder | null) => void;
144
+ afterSort: (keys: SortKey[]) => void;
145
+ afterRowClick: (e: RowEvent) => void;
146
+ /** Return false to keep the cell editor closed, e.g. when double click opens the record. */
147
+ afterRowDoubleClick: (e: RowEvent) => boolean | void;
148
+ /** Columns were resized, moved, hidden or shown; save the layout here. */
149
+ afterColumnLayout: (layout: ColumnLayout) => void;
150
+ /** Server mode: fetch the rows for `query`. */
151
+ afterQueryChange: (query: ServerQuery, reason: QueryChangeReason) => void;
152
+ /** Server mode: a paste did not fit the loaded rows; `lost` rows were not pasted. */
153
+ afterPasteClipped: (lost: number) => void;
83
154
  afterFilter: (filters: Map<number, ColumnFilter>) => void;
84
155
  afterAutofill: (source: CellRange, target: CellRange) => void;
85
156
  afterValidate: (row: number, col: number, error: string | undefined) => void;
@@ -116,7 +187,7 @@ export declare class CenituGrid {
116
187
  private cellMeta;
117
188
  private labels;
118
189
  private filters;
119
- private sortState;
190
+ private sortKeys;
120
191
  private invalid;
121
192
  /** Custom row heights by physical row. */
122
193
  private rowHeights;
@@ -146,9 +217,33 @@ export declare class CenituGrid {
146
217
  private undoStack;
147
218
  private redoStack;
148
219
  private clipboard;
220
+ private searchPanel?;
221
+ private searchQuery;
222
+ private searchOptions;
223
+ /** Matches shown highlighted while the search box is open (physical cell keys). */
224
+ private searchHits;
225
+ private bar?;
226
+ private server?;
149
227
  private frame;
150
228
  private cleanup;
151
229
  constructor(container: HTMLElement, options?: GridOptions);
230
+ /** Current query (server mode). */
231
+ getQuery(): ServerQuery | undefined;
232
+ /** Changes the query and asks for new rows. Anything but a page change returns to page 1. */
233
+ setQuery(change: Partial<ServerQuery>, reason?: QueryChangeReason): void;
234
+ /**
235
+ * Shows a fetched page. Unlike `loadData`, sort marks, filters, column layout and scroll stay,
236
+ * and the selection follows its row by `rowKey` when the row is still there. Undo history ends
237
+ * here: it refers to rows that are gone.
238
+ */
239
+ setPageRows(rows: RawCell[][] | DataRow[], totalCount?: number): void;
240
+ private rowKeyAt;
241
+ /** Stable id of a column: `columns[i].id`, then its bound property. */
242
+ getColumnId(col: number): string | undefined;
243
+ private colOfId;
244
+ private buildServerMode;
245
+ /** Page bar, busy state and the empty/loading/error message. */
246
+ private renderServerState;
152
247
  /** Number of visible rows. */
153
248
  countRows(): number;
154
249
  countSourceRows(): number;
@@ -183,11 +278,23 @@ export declare class CenituGrid {
183
278
  setDataAtCell(row: number, col: number, value: RawCell, source?: ChangeSource): void;
184
279
  setDataAtCell(changes: [number, number, RawCell][], source?: ChangeSource): void;
185
280
  loadData(data: RawCell[][] | DataRow[]): void;
281
+ /**
282
+ * Replaces the grid's content with an imported sheet, including its formats, sizes, frozen
283
+ * panes and merges. Hidden rows are shown: the grid only hides rows through filters.
284
+ */
285
+ loadSheet(sheet: ImportSheet): void;
286
+ /**
287
+ * Reads one sheet of an .xlsx file into this grid (default the first) and adds the workbook's
288
+ * defined names to the engine. References to other sheets need all of them: see `workbookEngine`.
289
+ */
290
+ importXLSX(file: ArrayBuffer | Uint8Array | Blob, opts?: ImportOptions & {
291
+ sheet?: number | string;
292
+ }): Promise<ImportSheet>;
186
293
  /** Effective settings of a cell at a visual position. */
187
294
  getCellMeta(row: number, col: number): CellMeta;
188
295
  setCellMeta(row: number, col: number, meta: CellMeta): void;
189
296
  /** Updates a subset of options at runtime. */
190
- updateSettings(s: Pick<GridOptions, 'fixedRowsTop' | 'fixedColumnsStart' | 'readOnly' | 'columns' | 'colHeaders' | 'mergeCells' | 'columnSorting' | 'filters' | 'fillHandle' | 'locale' | 'language' | 'contextMenu' | 'conditionalFormats'>): void;
297
+ updateSettings(s: Pick<GridOptions, 'fixedRowsTop' | 'fixedColumnsStart' | 'readOnly' | 'columns' | 'colHeaders' | 'mergeCells' | 'columnSorting' | 'filters' | 'fillHandle' | 'locale' | 'language' | 'contextMenu' | 'conditionalFormats' | 'search' | 'query' | 'totalCount' | 'loading' | 'loadError'>): void;
191
298
  selectCell(row: number, col: number, row2?: number, col2?: number): void;
192
299
  private announce;
193
300
  /** Selection as [[row, col, row2, col2]] (visual). */
@@ -205,6 +312,16 @@ export declare class CenituGrid {
205
312
  toggleCellStyle(flag: 'bold' | 'italic' | 'underline' | 'strike'): void;
206
313
  setRowHeight(row: number, height: number): void;
207
314
  getRowHeight(row: number): number;
315
+ /** Current widths, order and visibility; store it and give it back with `setColumnLayout`. */
316
+ getColumnLayout(): ColumnLayout;
317
+ /**
318
+ * Restores a saved layout. Ids that no longer exist are skipped and new columns stay after the
319
+ * saved ones, so a layout survives application updates. Clears undo history when columns moved:
320
+ * its entries refer to the old order.
321
+ */
322
+ setColumnLayout(layout: ColumnLayout): void;
323
+ private applyColumnLayout;
324
+ private layoutChanged;
208
325
  hideColumns(cols: number[]): void;
209
326
  /** Shows the given hidden columns, or all of them. */
210
327
  showColumns(cols?: number[]): void;
@@ -223,15 +340,38 @@ export declare class CenituGrid {
223
340
  downloadXLSX(fileName?: string, others?: CenituGrid[]): Promise<void>;
224
341
  /** Sorts by one column (visual order only; data and formulas are untouched). null restores data order. */
225
342
  sort(col: number, order: SortOrder | null): void;
226
- getSortState(): {
227
- col: number;
228
- order: SortOrder;
229
- } | null;
343
+ /** Sorts by several columns: the first key decides, later keys break ties. Empty restores data order. */
344
+ sortBy(keys: SortKey[]): void;
345
+ private applySort;
346
+ /** The primary sort key, or null. */
347
+ getSortState(): SortKey | null;
348
+ getSortKeys(): SortKey[];
349
+ /** Server mode: sets or clears the filter of a column; asks for rows from page 1. */
350
+ setServerFilter(columnId: string, filter: ServerFilter | null): void;
230
351
  setFilter(col: number, filter: ColumnFilter | null): void;
231
352
  clearFilters(): void;
232
353
  getFilters(): Map<number, ColumnFilter>;
233
354
  /** Re-evaluates the filters against current values (they are not re-applied automatically on edit). */
234
355
  applyFilters(): void;
356
+ /** Visual positions of the cells matching `query`, in reading order; hidden columns are skipped. */
357
+ findCells(query: string, opts?: SearchOptions): CellAddress[];
358
+ /** Selects the match after (or before) the active cell, wrapping around; null when there is none. */
359
+ findNext(query: string, opts?: SearchOptions, backward?: boolean): CellAddress | null;
360
+ /**
361
+ * Replaces the match in the active cell (when it is one) and moves to the next. Formula cells
362
+ * change only with `lookIn: 'formulas'`: replacing in their result would delete the formula.
363
+ */
364
+ replaceNext(query: string, replacement: string, opts?: SearchOptions): CellAddress | null;
365
+ /** Replaces every match in one undo step; returns the number of cells changed. */
366
+ replaceAll(query: string, replacement: string, opts?: SearchOptions): number;
367
+ /** Opens the find box (`search` option), with the replace row when `replace` is true. */
368
+ openSearch(replace?: boolean): void;
369
+ closeSearch(): void;
370
+ private searchText;
371
+ private nextHit;
372
+ private highlightHits;
373
+ private stepSearch;
374
+ private replaceIn;
235
375
  /** Merges an area (visual coordinates). Refused while sorting/filtering is active or across frozen edges. */
236
376
  mergeCells(row: number, col: number, rowspan: number, colspan: number): boolean;
237
377
  unmergeCells(row: number, col: number): boolean;
@@ -244,6 +384,7 @@ export declare class CenituGrid {
244
384
  * is a no-op, out of range, or would split a merged cell.
245
385
  */
246
386
  moveColumns(start: number, count: number, target: number): boolean;
387
+ private relocateColumns;
247
388
  canMoveColumns(start: number, count: number, target: number): boolean;
248
389
  undo(): void;
249
390
  redo(): void;
@@ -263,9 +404,11 @@ export declare class CenituGrid {
263
404
  copy(cut?: boolean): string | undefined;
264
405
  /**
265
406
  * Pastes text at the selection. Text from this grid's own copy keeps formulas, with relative
266
- * references moved to the new location. A single value fills the whole selection.
407
+ * references moved to the new location and its cell styles. `html` is the clipboard's HTML
408
+ * flavour from another app; only the styles of its table are used. A single value fills the
409
+ * whole selection.
267
410
  */
268
- paste(text: string): void;
411
+ paste(text: string, html?: string): void;
269
412
  /** Fills `target` (adjacent to `source`, visual coordinates) by extending the source series. */
270
413
  autofill(source: CellRange, target: CellRange): void;
271
414
  render(): void;
@@ -281,6 +424,7 @@ export declare class CenituGrid {
281
424
  private applyChanges;
282
425
  private clearRange;
283
426
  private replay;
427
+ private replayEntry;
284
428
  private applyMeta;
285
429
  private applyEngineOp;
286
430
  private undoEngineOp;
@@ -344,6 +488,11 @@ export declare class CenituGrid {
344
488
  openEditor(initial?: string): void;
345
489
  closeEditor(commit: boolean): void;
346
490
  private focusInput;
491
+ private barFocused;
492
+ private buildFormulaBar;
493
+ private onBarKey;
494
+ /** Shows the active cell in the bar; while editing, the bar mirrors the editor. */
495
+ private syncFormulaBar;
347
496
  /** Keeps the textarea over the active cell (in its pane), so IME windows appear in the right place. */
348
497
  private placeInput;
349
498
  private autoSizeInput;
@@ -360,6 +509,8 @@ export declare class CenituGrid {
360
509
  private onMouseDown;
361
510
  private onContextMenu;
362
511
  private onDoubleClick;
512
+ /** The row a click landed on; null for headers, checkboxes and drags that ended on another cell. */
513
+ private rowEventAt;
363
514
  private onDrag;
364
515
  private onMouseUp;
365
516
  private dropLine?;
@@ -370,6 +521,7 @@ export declare class CenituGrid {
370
521
  private fillToDataEnd;
371
522
  private startResize;
372
523
  private startRowResize;
524
+ private openServerMenu;
373
525
  private openMenu;
374
526
  private toggleCheckboxes;
375
527
  private moveActive;
@@ -2,6 +2,8 @@ export interface Messages {
2
2
  sortAsc: string;
3
3
  sortDesc: string;
4
4
  sortClear: string;
5
+ sortThenAsc: string;
6
+ sortThenDesc: string;
5
7
  filterByCondition: string;
6
8
  filterByValue: string;
7
9
  search: string;
@@ -42,6 +44,32 @@ export interface Messages {
42
44
  hideColumns: string;
43
45
  showColumns: string;
44
46
  cellAnnouncement: string;
47
+ find: string;
48
+ replace: string;
49
+ replaceAll: string;
50
+ replaceWith: string;
51
+ matchCase: string;
52
+ wholeCell: string;
53
+ lookInFormulas: string;
54
+ noMatches: string;
55
+ /** {total} is the number of matches. */
56
+ matchCount: string;
57
+ /** {count} is the number of cells changed. */
58
+ replacedCount: string;
59
+ next: string;
60
+ previous: string;
61
+ close: string;
62
+ formulaBar: string;
63
+ /** {from}, {to} and {total} are record numbers. */
64
+ pagerRange: string;
65
+ pageSize: string;
66
+ noRows: string;
67
+ loading: string;
68
+ loadError: string;
69
+ dateFrom: string;
70
+ dateTo: string;
71
+ yes: string;
72
+ no: string;
45
73
  }
46
74
  export declare const enUS: Messages;
47
75
  export declare const trTR: Messages;
@@ -0,0 +1,41 @@
1
+ import type { Messages } from './messages.js';
2
+ export interface SearchOptions {
3
+ matchCase?: boolean;
4
+ /** Match only when the whole cell equals the query. */
5
+ wholeCell?: boolean;
6
+ /** 'values' (default) searches what cells display; 'formulas' searches what was entered. */
7
+ lookIn?: 'values' | 'formulas';
8
+ }
9
+ type Span = [number, number];
10
+ /** Occurrences of `query` in `text` as [start, end) in `text`'s own indexes. */
11
+ export declare function matchSpans(text: string, query: string, o?: SearchOptions, locale?: string): Span[];
12
+ /** `text` with every occurrence of `query` replaced. */
13
+ export declare function replaceSpans(text: string, spans: Span[], replacement: string): string;
14
+ export interface SearchPanelOptions {
15
+ root: HTMLElement;
16
+ messages: Messages;
17
+ query: string;
18
+ options: SearchOptions;
19
+ /** Shows the replace row; false for read-only grids. */
20
+ replace: boolean;
21
+ /** Re-runs the search; returns the number of matches. */
22
+ onSearch(query: string, options: SearchOptions): number;
23
+ /** Moves to the next match; returns its 1-based position and the total. */
24
+ onNext(backward: boolean): {
25
+ index: number;
26
+ total: number;
27
+ };
28
+ onReplace(replacement: string): {
29
+ index: number;
30
+ total: number;
31
+ };
32
+ onReplaceAll(replacement: string): number;
33
+ onClose(): void;
34
+ }
35
+ export interface SearchPanel {
36
+ close(): void;
37
+ focus(replace: boolean): void;
38
+ }
39
+ /** Floating find/replace box over the grid's top right corner. */
40
+ export declare function openSearchPanel(o: SearchPanelOptions): SearchPanel;
41
+ export {};
@@ -0,0 +1,66 @@
1
+ import type { Messages } from './messages.js';
2
+ /** What a column can be filtered by on the server. */
3
+ export type ServerFilterDef = {
4
+ kind: 'text';
5
+ } | {
6
+ kind: 'list';
7
+ options: {
8
+ value: string;
9
+ label?: string;
10
+ }[];
11
+ } | {
12
+ kind: 'date';
13
+ } | {
14
+ kind: 'boolean';
15
+ };
16
+ /** A filter value in the query; date bounds are as the date inputs give them (YYYY-MM-DD). */
17
+ export type ServerFilter = {
18
+ kind: 'text';
19
+ value: string;
20
+ } | {
21
+ kind: 'list';
22
+ values: string[];
23
+ } | {
24
+ kind: 'date';
25
+ from?: string;
26
+ to?: string;
27
+ } | {
28
+ kind: 'boolean';
29
+ value: boolean;
30
+ };
31
+ export interface ServerSort {
32
+ field: string;
33
+ desc: boolean;
34
+ }
35
+ /** Describes the rows the application should fetch. Fields are column ids, never indexes. */
36
+ export interface ServerQuery {
37
+ page: number;
38
+ pageSize: number;
39
+ sort: ServerSort[];
40
+ filters: Record<string, ServerFilter>;
41
+ search?: string;
42
+ }
43
+ export type QueryChangeReason = 'page' | 'pageSize' | 'sort' | 'filter' | 'search';
44
+ export declare const DEFAULT_PAGE_SIZES: number[];
45
+ export declare function initialQuery(q: Partial<ServerQuery> | undefined, pageSizes: number[]): ServerQuery;
46
+ export declare const pageCount: (total: number, pageSize: number) => number;
47
+ /** The controls for one filter; `read` gives the entered filter, or null when it is empty. */
48
+ export declare function serverFilterSection(doc: Document, m: Messages, def: ServerFilterDef, value: ServerFilter | undefined): {
49
+ el: HTMLElement;
50
+ read(): ServerFilter | null;
51
+ };
52
+ /** Page bar: custom actions, record range, page size, previous and next. */
53
+ export declare class Pager {
54
+ private readonly m;
55
+ readonly el: HTMLDivElement;
56
+ /** Slot for application buttons. */
57
+ readonly actions: HTMLDivElement;
58
+ private readonly range;
59
+ private readonly size;
60
+ private readonly position;
61
+ private readonly prev;
62
+ private readonly next;
63
+ constructor(doc: Document, m: Messages, pageSizes: number[], onPage: (delta: number) => void, onSize: (size: number) => void);
64
+ /** `total` undefined keeps the last figures, so the bar does not flicker while a page loads. */
65
+ update(q: ServerQuery, total: number | undefined): void;
66
+ }
@@ -1,7 +1,9 @@
1
- export { CenituGrid, type GridOptions, type CellChange, type ChangeSource, type AlterAction, type HookName, } from './grid/grid.js';
1
+ export { CenituGrid, type GridOptions, type CellChange, type ChangeSource, type AlterAction, type SortKey, type ColumnLayout, type RowEvent, type HookName, } from './grid/grid.js';
2
2
  export type { CellMeta, CellStyle, ConditionalFormat, NumericFormat, CellRenderContext, CellType as GridCellType } from './grid/cellTypes.js';
3
3
  export type { ColumnFilter, ConditionOp, SortOrder } from './grid/filters.js';
4
4
  export type { MergeArea } from './grid/merges.js';
5
+ export type { SearchOptions } from './grid/search.js';
6
+ export type { ServerQuery, ServerFilter, ServerFilterDef, ServerSort, QueryChangeReason } from './grid/serverMode.js';
5
7
  export type { MenuEntry, MenuItem } from './grid/contextMenu.js';
6
8
  export type { DataRow } from './grid/binding.js';
7
9
  export { enUS, trTR, type Messages } from './grid/messages.js';
@@ -1,6 +1,7 @@
1
1
  import { type CSSProperties } from 'react';
2
2
  import type { ColumnFilter, SortOrder } from '../grid/filters.js';
3
- import { CenituGrid, type CellChange, type ChangeSource, type GridOptions } from '../grid/grid.js';
3
+ import { CenituGrid, type CellChange, type ChangeSource, type ColumnLayout, type GridOptions, type RowEvent } from '../grid/grid.js';
4
+ import type { QueryChangeReason, ServerQuery } from '../grid/serverMode.js';
4
5
  export interface CenituTableProps extends GridOptions {
5
6
  className?: string;
6
7
  style?: CSSProperties;
@@ -8,6 +9,12 @@ export interface CenituTableProps extends GridOptions {
8
9
  onSelection?(row: number, col: number, row2: number, col2: number): void;
9
10
  onSort?(col: number, order: SortOrder | null): void;
10
11
  onFilter?(filters: Map<number, ColumnFilter>): void;
12
+ onRowClick?(event: RowEvent): void;
13
+ /** Return false to keep the cell editor closed. */
14
+ onRowDoubleClick?(event: RowEvent): boolean | void;
15
+ onColumnLayoutChange?(layout: ColumnLayout): void;
16
+ /** Server mode: fetch the rows for `query`. */
17
+ onQueryChange?(query: ServerQuery, reason: QueryChangeReason): void;
11
18
  }
12
19
  /**
13
20
  * React component around CenituGrid. The ref exposes the grid instance for imperative calls.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cenitu-table",
3
- "version": "0.2.0",
3
+ "version": "0.4.0-beta.0",
4
4
  "description": "Spreadsheet-like data grid with a built-in multi-sheet formula engine. Zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "license": "MIT",