@everygrid/grid 0.4.7

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.

Potentially problematic release.


This version of @everygrid/grid might be problematic. Click here for more details.

Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +680 -0
  3. package/dist/Everygrid.css +3 -0
  4. package/dist/everygrid-config.json +7 -0
  5. package/dist/everygrid.standalone.js +401 -0
  6. package/dist/i18n/en.json.d.ts +86 -0
  7. package/dist/i18n/ko.json.d.ts +86 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +24227 -0
  10. package/dist/src/components/ColumnSelectorComponent.d.ts +8 -0
  11. package/dist/src/components/DiffPopupComponent.d.ts +14 -0
  12. package/dist/src/components/EmptyGridPlaceholderComponent.d.ts +15 -0
  13. package/dist/src/components/EverygridComponent.d.ts +5 -0
  14. package/dist/src/components/ExcelViewComponent.d.ts +20 -0
  15. package/dist/src/components/GridTableComponent.d.ts +28 -0
  16. package/dist/src/components/GridToolbarComponent.d.ts +49 -0
  17. package/dist/src/components/HiddenColumnSelectorComponent.d.ts +7 -0
  18. package/dist/src/components/InsertedRowsComponent.d.ts +21 -0
  19. package/dist/src/components/MobileColumnSelectorComponent.d.ts +8 -0
  20. package/dist/src/components/NestedTableComponent.d.ts +10 -0
  21. package/dist/src/components/PaginationComponent.d.ts +21 -0
  22. package/dist/src/components/PinnedTableComponent.d.ts +26 -0
  23. package/dist/src/components/PopupComponent.d.ts +29 -0
  24. package/dist/src/components/RowCountComponent.d.ts +18 -0
  25. package/dist/src/components/RowDetailComponent.d.ts +8 -0
  26. package/dist/src/components/TableCellComponent.d.ts +18 -0
  27. package/dist/src/components/TextEditorPopupComponent.d.ts +6 -0
  28. package/dist/src/core/Everygrid.d.ts +568 -0
  29. package/dist/src/core/ExcelView.d.ts +28 -0
  30. package/dist/src/core/GridHandle.d.ts +214 -0
  31. package/dist/src/core/GridHandle.test.d.ts +1 -0
  32. package/dist/src/core/ResizeUtils.d.ts +3 -0
  33. package/dist/src/core/highlightUtils.d.ts +4 -0
  34. package/dist/src/core/normalizeOptions.d.ts +13 -0
  35. package/dist/src/core/normalizeOptions.test.d.ts +1 -0
  36. package/dist/src/core/types.d.ts +361 -0
  37. package/dist/src/core/useVirtualWindow.d.ts +89 -0
  38. package/dist/src/core/useVirtualWindow.test.d.ts +1 -0
  39. package/dist/src/core/utils.d.ts +19 -0
  40. package/dist/src/i18n/I18n.d.ts +16 -0
  41. package/dist/src/icons/ChevronDownIcon.d.ts +2 -0
  42. package/dist/src/icons/ColumnWidthIcon.d.ts +3 -0
  43. package/dist/src/icons/ColumnsIcon.d.ts +3 -0
  44. package/dist/src/icons/CommaIcon.d.ts +1 -0
  45. package/dist/src/icons/ConfigIcon.d.ts +4 -0
  46. package/dist/src/icons/DiffIcon.d.ts +4 -0
  47. package/dist/src/icons/DownloadIcon.d.ts +3 -0
  48. package/dist/src/icons/EditIcon.d.ts +1 -0
  49. package/dist/src/icons/ExcelIcon.d.ts +3 -0
  50. package/dist/src/icons/GlobeIcon.d.ts +2 -0
  51. package/dist/src/icons/HideIcon.d.ts +1 -0
  52. package/dist/src/icons/InfoIcon.d.ts +2 -0
  53. package/dist/src/icons/InsertRowIcon.d.ts +4 -0
  54. package/dist/src/icons/MobileColumnsIcon.d.ts +3 -0
  55. package/dist/src/icons/PinEmptyIcon.d.ts +1 -0
  56. package/dist/src/icons/PinFilledIcon.d.ts +1 -0
  57. package/dist/src/icons/ReloadIcon.d.ts +3 -0
  58. package/dist/src/icons/RowDetailIcon.d.ts +3 -0
  59. package/dist/src/icons/SearchIcon.d.ts +3 -0
  60. package/dist/src/icons/SortDownIcon.d.ts +1 -0
  61. package/dist/src/icons/SortResetIcon.d.ts +3 -0
  62. package/dist/src/icons/SortUpIcon.d.ts +1 -0
  63. package/dist/src/icons/TrashIcon.d.ts +3 -0
  64. package/dist/src/icons/UndoIcon.d.ts +3 -0
  65. package/dist/src/index.d.ts +14 -0
  66. package/dist/src/react/index.d.ts +20 -0
  67. package/dist/src/standalone-entry.d.ts +2 -0
  68. package/dist/src/wasm/ExcelExportClient.d.ts +14 -0
  69. package/dist/src/wasm/ExportWorker.d.ts +1 -0
  70. package/dist/src/wasm/GridEngineWasm.d.ts +70 -0
  71. package/dist/src/wasm/GridEngineWorker.d.ts +102 -0
  72. package/dist/wasm/everygrid_wasm.js +422 -0
  73. package/dist/wasm/everygrid_wasm_bg.wasm +0 -0
  74. package/package.json +61 -0
@@ -0,0 +1,568 @@
1
+ import { GridHandle, GridEvents, RowKey, CellChange, RowChange } from './GridHandle';
2
+ import { I18n } from '../i18n/I18n';
3
+ import { default as React } from 'react';
4
+ import { GridColumn, GridLoadProgress, GridOptions, GridPaginationConfig, GridVirtualScrollConfig, IEverygrid, KeyTree, ReloadOptions } from './types';
5
+ export declare class Everygrid<T extends Record<string, unknown> = Record<string, unknown>> implements IEverygrid<T> {
6
+ static readonly POPUP_OVERLAY_CLASS = "everygrid-popup-overlay";
7
+ static readonly POPUP_CONTENT_CLASS = "everygrid-popup-content";
8
+ static readonly POPUP_CLOSE_CLASS = "everygrid-popup-close";
9
+ static readonly POPUP_CLOSE_HTML = "&times;";
10
+ private static instances;
11
+ private static _initializedTargets;
12
+ private static _configCache;
13
+ private static _targetRegistry;
14
+ private static _mounting;
15
+ static I18n: typeof I18n;
16
+ static options: GridOptions;
17
+ readonly options: GridOptions<T>;
18
+ hiddenFieldsMap: Map<string, Set<string>>;
19
+ displayColsMap: Map<string, Set<string>>;
20
+ exportState: Map<string, {
21
+ done: number;
22
+ total: number;
23
+ }>;
24
+ private _exportControllers;
25
+ pinnedColumns: Set<string>;
26
+ commaSeparatedFields: Set<string>;
27
+ linkFields: Set<string>;
28
+ isExcelViewMode: boolean;
29
+ sortConfig: Map<string, {
30
+ field: string;
31
+ direction: 'asc' | 'desc' | null;
32
+ }>;
33
+ columnWidths: Map<string, Map<string, number>>;
34
+ activeEditFields: Map<string, Set<string>>;
35
+ private _searchKeysCache;
36
+ activePopup: React.ReactElement | null;
37
+ activePopupData: {
38
+ data: unknown;
39
+ } | null;
40
+ activePopupTitle: string | null;
41
+ activePopupSubtitle: string | null;
42
+ /** Hover text for the subtitle strip (e.g. the full URL behind a file name). */
43
+ activePopupSubtitleTitle: string | null;
44
+ activePopupRow: unknown | null;
45
+ activePopupRowKey: string | null;
46
+ currentPage: Map<string, number>;
47
+ private originalDataMap;
48
+ private _editedKeys;
49
+ private _inserted;
50
+ private _deletedRows;
51
+ checkedValues: Map<string, Set<unknown>>;
52
+ private syncTimeoutId;
53
+ private roots;
54
+ private subscribers;
55
+ private domObserver;
56
+ _destroyed: boolean;
57
+ filterText: string;
58
+ private _wasmEngines;
59
+ private _wasmEngineReady;
60
+ private _wasmDataLoaded;
61
+ _wasmPageCache: Map<string, {
62
+ rows: unknown[];
63
+ total: number;
64
+ }>;
65
+ get wasmReady(): boolean;
66
+ /** Loading/indexing progress for one of this instance's targets. Mirrors the flags the toolbar
67
+ * uses for its progress pill, but as plain data so a host (tab bar, shell) can render it too. */
68
+ loadProgress(containerId: string): GridLoadProgress;
69
+ /** Loading/indexing progress for a mounted target, or null if nothing is mounted under that id.
70
+ * Lets a tab bar or shell show a grid's load progress while that grid's own view is hidden —
71
+ * a keep-alive tab keeps streaming in the background, so its progress outlives its visibility. */
72
+ static getLoadProgress(id: string): GridLoadProgress | null;
73
+ private _serverTotal;
74
+ private _serverFetching;
75
+ _streamTotal: Map<string, number>;
76
+ _dataLimited: Map<string, {
77
+ shown: number;
78
+ total: number | null;
79
+ }>;
80
+ _streamRows: Map<string, Record<string, unknown>[]>;
81
+ _streamUrl: Map<string, string>;
82
+ _processing: Map<string, boolean>;
83
+ private _processingTimer;
84
+ private _filterSeq;
85
+ private _blockCache;
86
+ private _blockPending;
87
+ private _blockRenderScheduled;
88
+ private _loadChain;
89
+ private _loadSeq;
90
+ _wasmRawTotal: Map<string, number>;
91
+ _indexingAllRows: Map<string, Record<string, unknown>[]>;
92
+ _indexingStage: Map<string, 'indexing' | 'ready'>;
93
+ _indexingProgress: Map<string, number>;
94
+ _wasStreaming: Set<string>;
95
+ _loading: Set<string>;
96
+ /** Headers an origin may use to advertise the decoded size of a compressed body. */
97
+ private static readonly UNCOMPRESSED_LENGTH_HEADERS;
98
+ private static readonly LARGE_PAYLOAD_BYTES;
99
+ _dataSource: Map<string, string | (() => Promise<Record<string, unknown>[]>)>;
100
+ _reloading: Map<string, 'button' | 'silent'>;
101
+ private _reloadRun;
102
+ private _reloadNext;
103
+ private _discarding;
104
+ constructor(options: GridOptions<T>);
105
+ /** Unmounts every grid and forgets all loaded config — the next loadConfig re-fetches. */
106
+ /**
107
+ * Re-fetches a mounted grid's data from the source it was created with, in place.
108
+ *
109
+ * The instance method needs a handle the host usually does not keep; this is the same call for
110
+ * callers that only know the target id — what the toolbar's reload button does, by another name.
111
+ * Use it when the fetcher's own inputs have changed (a different row count, a new date range) and
112
+ * the grid should pick that up without being torn down and remounted.
113
+ */
114
+ static reload(id: string, opts?: ReloadOptions): Promise<void>;
115
+ /**
116
+ * A handle onto a mounted grid's data — `Everygrid.get('users').row(3).cell('score').set(90)`.
117
+ * Null when nothing is mounted under that id. See GridHandle.
118
+ */
119
+ /** The instance behind a target id (tests and tooling; the handle is the API). */
120
+ static instances_get<D extends Record<string, unknown> = Record<string, unknown>>(id: string): Everygrid<D>;
121
+ static get<D extends Record<string, unknown> = Record<string, unknown>>(id: string): GridHandle<D> | null;
122
+ static resetAutoInit(): void;
123
+ /**
124
+ * Resolves a config/data URL against the browser's base URI.
125
+ * Absolute paths ('/…') get the base path prefix, so the library works under a sub-path deploy.
126
+ */
127
+ private static _resolveUrl;
128
+ /**
129
+ * Loads config files and registers their targets — no DOM work, no engines, no data fetching.
130
+ * Mount the ones this screen actually shows with `mount()`.
131
+ *
132
+ * Cached per entry URL, so calling it from every screen costs one network round trip for the
133
+ * lifetime of the page. Config edits are picked up on reload, or explicitly via
134
+ * `invalidateConfig()` / `{reload: true}`.
135
+ *
136
+ * @param entryConfigUrl Path to the static entry config file (default: /everygrid.config.json)
137
+ * @param opts Options.
138
+ * @param opts.reload Bypass the cache and re-fetch
139
+ * @returns The target ids that are now registered (across every config file listed)
140
+ */
141
+ static loadConfig(entryConfigUrl?: string, opts?: {
142
+ reload?: boolean;
143
+ }): Promise<string[]>;
144
+ /** Drops cached config so the next loadConfig re-fetches. Already-mounted grids keep their config. */
145
+ static invalidateConfig(entryConfigUrl?: string): void;
146
+ private static _fetchConfig;
147
+ /**
148
+ * Mounts a grid into the element with the same id. Self-sufficient: it loads the root config
149
+ * (`/everygrid.config.json`, cached — one fetch app-wide) on demand, so a screen can just call
150
+ * `mount('a-grid', { fetcher })` with no separate `loadConfig()` bootstrap. If the config carries
151
+ * a target for this id, its settings apply; if not, the grid renders with defaults — config is
152
+ * for customization, not a requirement. The element must be in the DOM (nothing is allocated for a
153
+ * target this screen doesn't show).
154
+ *
155
+ * Idempotent: mounting an already-mounted target returns the live instance.
156
+ *
157
+ * @param targetId Element id; any id renders (a matching config target just customizes it)
158
+ * @param opts Mount options.
159
+ * @param opts.fetcher Data source for this target — a URL (streamed) or an async function
160
+ */
161
+ static mount<D extends Record<string, unknown> = Record<string, unknown>>(targetId: string, opts?: {
162
+ fetcher?: string | (() => Promise<Record<string, unknown>[]>);
163
+ }): Promise<Everygrid<D> | null>;
164
+ private static _doMount;
165
+ /**
166
+ * Preload one or more entry config files (each an `everygrid.config.json` listing its `configs`).
167
+ * Usually unnecessary — `createGrid` / `mount` read the root config on demand — but call it to
168
+ * point at a non-root path (a sub-app), or to load several entries up front. Cached per URL.
169
+ * Returns every registered target id. Also exported as a standalone `loadEverygridConfig`.
170
+ */
171
+ static loadEverygridConfig(entryConfigUrls?: string | string[]): Promise<string[]>;
172
+ /**
173
+ * Create a grid in the element with `id` — the ergonomic form of `mount`. Loads the root config on
174
+ * demand, applies a matching config target or renders with defaults, and returns the instance
175
+ * (null if the element isn't in the DOM). `fetcher` is a URL (streamed) or a `() => Promise<rows>`.
176
+ * Also exported as a standalone `createGrid`.
177
+ */
178
+ static createGrid<D extends Record<string, unknown> = Record<string, unknown>>(id: string, fetcher?: string | (() => Promise<Record<string, unknown>[]>)): Promise<Everygrid<D> | null>;
179
+ /**
180
+ * Tears a mounted grid down completely — React root, WASM engine, worker thread, timers — and
181
+ * makes the target mountable again. Call this when the screen owning the grid goes away.
182
+ *
183
+ * @returns true if a grid was mounted and is now gone
184
+ */
185
+ static unmount(targetId: string): boolean;
186
+ /**
187
+ * Loads config and mounts every target already present in the DOM.
188
+ *
189
+ * @deprecated Prefer `loadConfig()` + `mount()`. Targets whose element doesn't exist yet are
190
+ * skipped rather than waited for, so a screen that renders its container later must mount it
191
+ * itself.
192
+ * @param apiFetchers Map of data fetch functions or absolute URL strings keyed by target id
193
+ * @param entryConfigUrl Path to the static entry config file (default: /everygrid.config.json)
194
+ */
195
+ static autoInit(apiFetchers?: Record<string, string | (() => Promise<Record<string, unknown>[]>)>, entryConfigUrl?: string): Promise<void>;
196
+ /**
197
+ * Fetches a target's data from `url` and loads it in — streaming straight into WASM when
198
+ * the payload is large, otherwise parsing it as one JSON document.
199
+ *
200
+ * Shared by autoInit's first load and by reloadData(), so both take the identical path.
201
+ */
202
+ private _loadFromUrl;
203
+ /** Installs freshly loaded rows as the target's data and hands them to its WASM engine. */
204
+ private _setRows;
205
+ /**
206
+ * The single way rows reach an engine. Queues behind any load already running for the same
207
+ * target, and skips itself if a newer load was requested while it waited — by then its rows
208
+ * are stale and writing them would just undo the newer ones.
209
+ */
210
+ private _loadIntoEngine;
211
+ /**
212
+ * Patches edited rows into every ready engine in place, then refreshes the visible page.
213
+ *
214
+ * The alternative, `_loadIntoEngine`, is a full `setData`: it re-uploads and re-indexes the
215
+ * entire dataset to change one cell, and its progress callback puts the indexing bar on
216
+ * screen for every keystroke-sized edit. Rows are addressed by their position in
217
+ * `options.data`, which is the engine's raw row order.
218
+ */
219
+ private _syncRowsToEngines;
220
+ /**
221
+ * Runs one engine operation on every ready engine of this instance, then re-applies the filter
222
+ * so the page on screen reflects it. All row mutations — edit, insert, remove — go through here.
223
+ */
224
+ private _forEachReadyEngine;
225
+ /** The ids this instance renders into. */
226
+ private _targetIds;
227
+ /** Re-renders this instance's targets that are in the DOM. */
228
+ private _rerenderTargets;
229
+ /** Re-renders one target if it is in the DOM. */
230
+ private _rerender;
231
+ /** After a change to the change set: repaint the grid and tell listeners. */
232
+ private _afterChange;
233
+ /** Resolves with the target's engine once it exists, or null if it never shows up. */
234
+ private _awaitEngine;
235
+ /**
236
+ * Re-fetches a grid's data from the source it was created with and rebuilds its WASM
237
+ * index. No-op for grids whose data was passed in directly (nothing to re-fetch).
238
+ *
239
+ * `silent` marks a reload the host asked for rather than one the reader clicked, so the toolbar's
240
+ * reload button stays as it is. The data still loads with the usual loading UI — the button's
241
+ * spinner reports that *that button* is working, and spinning it for something the reader did not
242
+ * press reads as the grid reloading itself.
243
+ *
244
+ * `discard` says the incoming rows replace the old ones rather than refreshing them — a different
245
+ * query, not the same one again. Holding the previous rows on screen through that is showing an
246
+ * answer to a question nobody asked any more, so the body drops to the loading skeleton the way a
247
+ * first load does. Leave it off for a plain refresh, where the rows on screen stay valid until
248
+ * the new ones land.
249
+ */
250
+ reloadData(containerId: string, opts?: ReloadOptions): Promise<void>;
251
+ /** Resolves once the browser has painted — or at once in a hidden tab, where it never will. */
252
+ private static _afterPaint;
253
+ private _runReload;
254
+ /**
255
+ * Size in bytes of the body as the reader will actually deliver it, or 0 when unknown.
256
+ *
257
+ * `Content-Length` counts bytes *on the wire*, but `res.body` yields bytes *after* the
258
+ * browser has undone any `Content-Encoding`. For a compressed response the two differ by
259
+ * the compression ratio, so comparing read bytes against Content-Length would report
260
+ * nonsense (an 11x-compressed 1GB file would "finish" at 9%). When the body is encoded we
261
+ * only have a real total if the origin advertises the decoded size out of band, via either
262
+ * header below — `x-amz-meta-uncompressed-length` is what S3 returns for user metadata,
263
+ * and it must also be listed in the bucket's CORS ExposeHeaders to be readable here.
264
+ */
265
+ private static _decodedLength;
266
+ /**
267
+ * Streams a JSON array from a ReadableStream directly into the WASM engine.
268
+ *
269
+ * The raw fetch bytes are transferred to the worker (zero-copy) without ever being
270
+ * parsed on the main thread — the worker runs the brace-depth scan and feeds complete
271
+ * objects into WASM. This keeps the main thread free during multi-GB loads (no
272
+ * blank-screen freeze) and avoids holding the whole dataset as JS objects on the heap.
273
+ */
274
+ private static _streamJsonToWasm;
275
+ /**
276
+ * Pulls a just-streamed dataset back out of WASM and installs it as `options.data`.
277
+ *
278
+ * Streaming is chosen whenever the response length is unmeasurable, and a compressed CDN
279
+ * response (`Content-Encoding` with no comparable length header) always is — so small payloads
280
+ * routinely take the streaming path in production while taking the buffered one locally. Those
281
+ * targets would otherwise be left with an empty `options.data`, and everything keyed off it —
282
+ * updateData, the modification marker, resetCell — would silently no-op. Only done while the
283
+ * payload really is small; a genuinely large stream keeps WASM as its only copy.
284
+ */
285
+ private static _materializeStreamedRows;
286
+ /**
287
+ * Re-renders all grid instances that are currently in the DOM.
288
+ * Grids visible in the viewport are rendered immediately.
289
+ * Grids outside the viewport are rendered lazily via IntersectionObserver.
290
+ * Grids not attached to the DOM are skipped entirely.
291
+ */
292
+ static refreshAll(): void;
293
+ /**
294
+ * Re-render every mounted grid immediately — no viewport gating and no data re-fetch. Use this
295
+ * for changes that only affect rendering, like a locale switch (headers, labels, toolbar strings):
296
+ * `refreshAll` would miss grids scrolled out of view and needlessly reload data.
297
+ */
298
+ static rerenderAll(): void;
299
+ /**
300
+ * Switch the UI locale and refresh every mounted grid so headers, labels and toolbar strings
301
+ * update — the one call a host app needs on a language change. (Facade over `I18n.setLocale` +
302
+ * `rerenderAll`; use `I18n.setLocale` directly only if you want to set the locale without redraw.)
303
+ */
304
+ static setLocale(locale: 'ko' | 'en'): void;
305
+ private static readonly LOCALE_MESSAGE;
306
+ /**
307
+ * For grids embedded in an iframe / separate window, where the host app's global `setLocale`
308
+ * can't reach them: listen for a locale pushed by the parent (via {@link sendLocale}) and apply it.
309
+ * Call once inside the embedded page. Returns a function that removes the listener.
310
+ *
311
+ * @param opts.origin only accept messages from this origin (recommended for security). Omit to
312
+ * accept any origin.
313
+ * @param opts.source only accept messages sent from this window (e.g. `window.parent`). Use it
314
+ * when several places could post a locale and only one is authoritative — messages from any
315
+ * other window are ignored, so it settles the "which sender wins" ambiguity. Omit to accept
316
+ * from any window.
317
+ */
318
+ static listenForLocale(opts?: {
319
+ origin?: string;
320
+ source?: Window | null;
321
+ }): () => void;
322
+ /**
323
+ * Push the current locale to an embedded grid window (an iframe's `contentWindow`) whose page
324
+ * called {@link listenForLocale}. The parent/host side of the same handshake.
325
+ *
326
+ * @param target the embedded window to deliver to (e.g. an iframe's `contentWindow`).
327
+ * @param locale the locale to apply in the target — `'ko'` or `'en'`.
328
+ * @param targetOrigin restrict delivery to this origin (recommended); defaults to any (`'*'`).
329
+ */
330
+ static sendLocale(target: Window, locale: 'ko' | 'en', targetOrigin?: string): void;
331
+ closePopup(): void;
332
+ private _kindsCache;
333
+ private _dataVersion;
334
+ /** Marks the loaded data as changed in place (an edit, a reset, a commit). */
335
+ private _touchData;
336
+ private _columnKinds;
337
+ isColumnNumeric(field: string): boolean;
338
+ /** True when every non-empty value in the sampled rows is a boolean. */
339
+ isColumnBoolean(field: string): boolean;
340
+ isColumnDate(field: string): boolean;
341
+ isColumnObject(field: string): boolean;
342
+ getGridTitle(containerId: string): string | undefined;
343
+ resetColumnWidths(container: HTMLElement): void;
344
+ /** Whether the target shows its toolbar (search + actions); `toolbar: [{id, active: false}]` hides it. */
345
+ hasToolbar(containerId: string): boolean;
346
+ /** Whether the toolbar offers the "config" button (`toolbar: [{id, showConfig: true}]`). */
347
+ showsConfig(containerId: string): boolean;
348
+ /**
349
+ * The grid's effective configuration in the config-file shape, reduced to this one grid: its
350
+ * target entry with every per-grid option folded in, plus root-only options. Data, callbacks
351
+ * and fetchers are left out.
352
+ */
353
+ getTargetConfig(containerId: string): Record<string, unknown>;
354
+ /** Opens the grid's effective configuration in the popup viewer (the toolbar's "config" button). */
355
+ showConfig(container: HTMLElement): void;
356
+ getRowActions(containerId: string): {
357
+ insertRow: boolean;
358
+ deleteRow: boolean;
359
+ };
360
+ /** The live reference for a row the grid handed out (a WASM copy is matched by content). */
361
+ private _liveRef;
362
+ isRowInserted(row: T): boolean;
363
+ /**
364
+ * The rows inserted since load, in insertion order. A fresh array each call: the table
365
+ * components are memoised on their props, and the live list keeps its identity across an
366
+ * insert, so handing it out directly could leave a memoised table one row behind.
367
+ */
368
+ getInsertedRows(): T[];
369
+ /**
370
+ * Cheap on purpose — every rendered row asks. A deleted row is registered in _editedKeys, so
371
+ * the engine's copy of it maps to the live reference by content in one step; nothing scans the
372
+ * data.
373
+ */
374
+ isRowDeleted(row: T): boolean;
375
+ /**
376
+ * Adds a new row to the insert grid and returns it. Every data field starts empty unless
377
+ * `values` supplies it. The loaded data and its indices are untouched: the row joins the data
378
+ * (at the end) only on commit, and revert simply drops it.
379
+ */
380
+ insertRow(containerId: string, values?: Partial<T>, at?: number): T;
381
+ /**
382
+ * Marks a row deleted. It stays on screen struck through and can be restored; commit removes
383
+ * it. Deleting a row that was only just inserted simply drops it.
384
+ */
385
+ deleteRow(containerId: string, rowData: T): void;
386
+ /** Undoes a delete. */
387
+ restoreRow(containerId: string, rowData: T): void;
388
+ /** Physically removes rows from the data and the engine, and forgets everything about them. */
389
+ private _dropRows;
390
+ private _insertRowsToEngines;
391
+ private _removeRowsFromEngines;
392
+ /** The field whose value a checked row is remembered by (`checkbox.mapping`), if configured. */
393
+ getCheckboxMapping(containerId: string): string | undefined;
394
+ getCheckedValues(containerId: string): unknown[];
395
+ /** The rows currently checked, in data order. */
396
+ getCheckedRows(containerId: string): T[];
397
+ /**
398
+ * Checks or unchecks rows by their mapping values — the one path every checkbox change takes,
399
+ * from the UI or the API, so the `check` event sees all of them.
400
+ */
401
+ setChecked(containerId: string, values: unknown[], checked: boolean): void;
402
+ clearChecked(containerId: string): void;
403
+ private _listeners;
404
+ /** Subscribe to `cellChange` / `change`; returns the unsubscribe function. */
405
+ on<K extends keyof GridEvents<T>>(event: K, handler: GridEvents<T>[K]): () => void;
406
+ private _emit;
407
+ /** Fires `change` with the current change set; every edit, revert and commit ends here. */
408
+ private _emitChange;
409
+ /**
410
+ * The row's key per the `rowKey` config, else its index. Null when the key field is empty —
411
+ * an inserted row whose key has not been filled in yet — rather than the string "null".
412
+ */
413
+ _keyOf(containerId: string, row: T, index: number): RowKey | null;
414
+ _indexOfKey(containerId: string, key: RowKey): number;
415
+ /** Data index of a row handed out by the grid — a WASM copy is matched by content. */
416
+ _indexOfRow(row: T): number;
417
+ _originalOf(row: T): T;
418
+ _cellChanges(row: T): CellChange[];
419
+ /**
420
+ * Every row that differs from the loaded data: inserted, deleted, or edited so that it still
421
+ * differs from its original. Inserted rows carry their current values and no cell list; deleted
422
+ * rows their original and no cell list.
423
+ */
424
+ _changedRows(containerId: string): RowChange<T>[];
425
+ /**
426
+ * Accepts the current state as the new baseline: deleted rows are removed for good, inserted row
427
+ * become ordinary rows, and no row is modified any more.
428
+ */
429
+ commit(containerId: string): void;
430
+ /**
431
+ * Walks only the rows that have been edited, not the whole dataset. This runs on every render,
432
+ * including the synchronous one per scroll event, and over a million rows the full scan cost
433
+ * ~300ms a frame — the main thread fell that far behind the compositor, which is what the blank
434
+ * band during a scroll actually was. _editedKeys is maintained by every path that mutates a row.
435
+ */
436
+ checkHasChanges(): boolean;
437
+ /** Server-side paging: calls serverFetcher, loads the result into WASM, and re-renders */
438
+ fetchServerPage(containerId: string): Promise<void>;
439
+ /** Applies the current filter/sort state to the WASM engine, fetches the current page, caches result, then re-renders */
440
+ applyWasmFilter(containerId: string, suppressProcessing?: boolean): Promise<void>;
441
+ /**
442
+ * Fetches only the requested page from WASM, reusing the already-computed filtered/sorted
443
+ * result (no re-filter, no re-sort). Use this for page navigation when the filter and sort
444
+ * are unchanged — it slices the cached result in O(pageSize) instead of rescanning every row.
445
+ */
446
+ fetchWasmPage(containerId: string): Promise<void>;
447
+ /** @deprecated Replaced by applyWasmFilter */
448
+ applyWasmState(containerId: string): void;
449
+ /** For rendering: reads and returns the current page data directly from the WASM engine or JS stream rows */
450
+ getDisplayItems(containerId: string, items: T[]): T[];
451
+ /** @deprecated Cache invalidation is no longer needed since data is read directly from WASM. Kept for backward compatibility. */
452
+ invalidateSortCache(_containerId?: string): void;
453
+ /** Sets the text filter and re-renders the grid */
454
+ setFilter(text: string, container?: HTMLElement): void;
455
+ /** Total row count after filter is applied (used for pagination calculation) */
456
+ getFilteredTotal(containerId: string): number;
457
+ /** Parse filter text supporting && (AND) and || (OR) operators */
458
+ /**
459
+ * Lower-cased searchable text for a cell value, mirroring the WASM engine: nested objects/arrays
460
+ * are matched by their JSON serialization (lib.rs FieldVal::Json), not "[object Object]". Keeping
461
+ * this in sync is what makes the JS re-filter (used for the 'filtered' Excel export) select the
462
+ * same rows the visible WASM-filtered grid shows.
463
+ */
464
+ private static _searchText;
465
+ private _parseFilterExpr;
466
+ /** Filter/sort JS stream rows without WASM */
467
+ private _applyStreamFilter;
468
+ getPagination(containerId: string): GridPaginationConfig | undefined;
469
+ /**
470
+ * Rows this device can safely hold, for `dataLimit: 'auto'`. Derived from reported RAM
471
+ * (`navigator.deviceMemory`, Chromium only) and whether the device looks mobile. Conservative on
472
+ * purpose — a grid that loads beats one that shows everything and crashes the tab.
473
+ */
474
+ private static _deviceRowBudget;
475
+ getDataLimit(containerId: string): number | undefined;
476
+ /**
477
+ * Applies the row cap to an in-memory array. Returns the (possibly sliced) rows and records the
478
+ * cap so the banner can report it. A no-op when there is no cap or the data is within it.
479
+ */
480
+ private _capRows;
481
+ getVirtualScroll(containerId: string): GridVirtualScrollConfig | undefined;
482
+ /** Resolved block size for a virtualised target — the unit every engine fetch is aligned to. */
483
+ private _blockSize;
484
+ /**
485
+ * Rows for [start, end) of the current filtered result. Synchronous: whatever is cached comes
486
+ * back immediately, missing blocks come back as `undefined` holes and are fetched in the
487
+ * background. Callers render a placeholder for the holes; the fetch re-renders when it lands.
488
+ */
489
+ getRowsInRange(containerId: string, start: number, end: number): (T | undefined)[];
490
+ /** Fetches one block from the engine into the cache, then coalesces a re-render. */
491
+ private _fetchBlock;
492
+ /**
493
+ * Blocks land one by one but a fast scroll requests several at once, so renders are coalesced
494
+ * into a single pass instead of one per arriving block.
495
+ *
496
+ * A timeout rather than requestAnimationFrame: rAF does not run in a background tab or an
497
+ * occluded window, and this render is what makes arrived rows visible at all — not just a
498
+ * smoothing step. Waiting on a frame left the Excel preview showing its cold-start stand-in
499
+ * long after the real rows had been cached.
500
+ */
501
+ private _scheduleVirtualRender;
502
+ getEditableFields(containerId: string): string[];
503
+ getCurrentWidths(containerId: string): Map<string, number>;
504
+ getDataFields(containerId: string): string[];
505
+ getSearchKeys(containerId: string, fallbackSample?: unknown[]): KeyTree;
506
+ /** Aborts an in-flight worker export for this grid; the export's finally clears state + re-renders. */
507
+ cancelExport(containerId: string): void;
508
+ exportExcel(containerId: string, scope?: 'filtered' | 'all'): Promise<void>;
509
+ /**
510
+ * Localized display name for a field: `columnI18n[locale][containerId][field]`, falling back to
511
+ * the `common` bucket and then the raw field key. Locale comes from the global `I18n`, so a
512
+ * `setLocale` + re-render is all it takes to relabel. Queries never use this — they stay in field
513
+ * keys — so nothing downstream (WASM/matcher/highlight) needs to know about labels.
514
+ */
515
+ columnLabel(field: string, containerId: string): string;
516
+ private buildBaseColumns;
517
+ /** Mobile column keys from config (`mobileColumns`), or undefined when none is set for this grid. */
518
+ getMobileColumns(containerId: string): string[] | undefined;
519
+ getColumns(containerId: string, items: T[], isMobile?: boolean): GridColumn[];
520
+ /** The row-actions column (delete / restore per row), when the config asks for it. Insert lives in the toolbar. */
521
+ private _actionsColumn;
522
+ subscribe(callback: () => void): () => void;
523
+ notify(): void;
524
+ renderGrid(container: HTMLElement, _updatePinned?: boolean): void;
525
+ isCellModified(rowData: T, field: string): boolean;
526
+ syncRowHeights(container: HTMLElement): void;
527
+ showPopup(data: unknown, rowData?: unknown, title?: string, subtitle?: string, subtitleTitle?: string): void;
528
+ showTextPopup(text: string, rowData?: unknown, title?: string): void;
529
+ showEditPopup(rowData: Record<string, unknown>, field: string, data: unknown): void;
530
+ updateData(rowData: Record<string, unknown>, field: string, value: unknown): void;
531
+ /** The id this instance renders into — one instance serves one target under createGrid/mount. */
532
+ private _firstTargetId;
533
+ reset(container: HTMLElement): void;
534
+ /** Cancels every change of the target: edits undone, inserted rows dropped, deleted rows back. */
535
+ cancelAll(containerId: string): void;
536
+ resetCell(rowData: Record<string, unknown>, field: string, container: HTMLElement): void;
537
+ /** Puts one cell's loaded value back. */
538
+ cancelCell(containerId: string, rowData: Record<string, unknown>, field: string): void;
539
+ updateColumnWidth(containerId: string, field: string, width: number): void;
540
+ resetSort(container: HTMLElement): void;
541
+ toggleExcelViewMode(container: HTMLElement): void;
542
+ setCurrentPage(containerId: string, page: number, _container: HTMLElement): void;
543
+ getCurrentPage(containerId: string): number;
544
+ getTotalPages(containerId: string): number;
545
+ showColumnSelector(allFields: string[], container: HTMLElement): void;
546
+ showRowDetail(row: T, container: HTMLElement): void;
547
+ /** Opens the read-only list of every change since load (the toolbar's "diff" button). */
548
+ showDiff(container: HTMLElement): void;
549
+ showMobileColumnSelector(allFields: string[], container: HTMLElement): void;
550
+ showHiddenColumnSelector(container: HTMLElement): void;
551
+ /**
552
+ * A fresh load is a new baseline: edits against the previous rows have nothing to compare to.
553
+ *
554
+ * Nothing is copied here. originalDataMap used to hold a deep copy of every row (and a second
555
+ * full copy sat in a parallel array), which for a million rows meant two JSON round trips of
556
+ * the whole dataset at load and three times the memory. A row's original is now captured on its
557
+ * first edit — see the edit path — so the map only ever holds the rows that have changed.
558
+ */
559
+ private initOriginalDataMap;
560
+ /** The dataset as it was before any edits, built for onDataChange from the per-row snapshots. */
561
+ private _originalData;
562
+ private isDate;
563
+ private observeDOM;
564
+ private checkAndInit;
565
+ private init;
566
+ private getRoot;
567
+ destroy(): void;
568
+ }
@@ -0,0 +1,28 @@
1
+ import { default as XLSX } from 'xlsx-js-style';
2
+ export declare const ExcelView: {
3
+ createExcelTable: (data: unknown[], limit?: number, isExcel?: boolean) => HTMLTableElement;
4
+ downloadExcel: (data: unknown[], gridId?: string) => void;
5
+ unionHeader: (flatRows: Record<string, unknown>[]) => string[];
6
+ buildXlsxBuffer: (flatRows: Record<string, unknown>[], header: string[]) => Uint8Array;
7
+ buildMultiSheetXlsx: (sheets: {
8
+ name: string;
9
+ rows: Record<string, unknown>[];
10
+ front?: string[];
11
+ }[]) => Uint8Array;
12
+ triggerDownload: (bytes: Uint8Array, fileName: string, mime: string) => void;
13
+ downloadTableAsExcel: (table: HTMLTableElement, gridId?: string) => void;
14
+ trimSheet: (ws: XLSX.WorkSheet | undefined) => void;
15
+ styleSheet: (ws: XLSX.WorkSheet | undefined) => void;
16
+ sanitizeSheetName: (name: string, used: Set<string>) => string;
17
+ arrayPaths: (rows: Record<string, unknown>[]) => string[][];
18
+ getAtPath: (obj: unknown, path: string[]) => unknown;
19
+ decomposeToRows: (value: unknown, keyPrefix: string, out: Record<string, unknown>[], scalarAsKey?: boolean) => void;
20
+ detectKeyField: (record: unknown) => string | undefined;
21
+ orderHeader: (header: string[], front: string[]) => string[];
22
+ sheetFromRows: (rows: Record<string, unknown>[], dense?: boolean, front?: string[]) => XLSX.WorkSheet | null;
23
+ appendChildSheets: (wb: XLSX.WorkBook, records: Record<string, unknown>[], paths: string[][], keyField: string | undefined, used: Set<string>, dense?: boolean) => void;
24
+ downloadRelationalExcel: (rows: unknown[], gridId?: string, keyField?: string) => void;
25
+ flattenObjectForExcel: (obj: unknown, prefix?: string, deep?: boolean) => Record<string, unknown>;
26
+ formatObject: (obj: unknown, indent?: number) => string;
27
+ getExcelRows: (data: unknown[]) => Record<string, unknown>[];
28
+ };