@zengrid/angular 1.2.0 → 1.2.1

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,921 +0,0 @@
1
- import {
2
- Component,
3
- ElementRef,
4
- ChangeDetectionStrategy,
5
- afterNextRender,
6
- viewChild,
7
- contentChildren,
8
- input,
9
- output,
10
- model,
11
- effect,
12
- inject,
13
- PLATFORM_ID,
14
- ChangeDetectorRef,
15
- OutputEmitterRef,
16
- NgZone,
17
- DestroyRef,
18
- } from '@angular/core';
19
- import { isPlatformBrowser } from '@angular/common';
20
- import { Grid } from '@zengrid/core';
21
- import type {
22
- GridOptions,
23
- GridExportOptions,
24
- GridEvents,
25
- SortState,
26
- FilterModel,
27
- CellRange,
28
- CellRef,
29
- ColumnDef,
30
- ColumnStateSnapshot,
31
- GridStateSnapshot,
32
- GridPlugin,
33
- ColumnConstraints,
34
- HeaderRenderer,
35
- VisibleRange,
36
- PaginationConfig,
37
- LoadingConfig,
38
- CellOverflowConfig,
39
- RowHeightConfig,
40
- SortIcons,
41
- RendererCacheConfig,
42
- } from '@zengrid/core';
43
- import { GridApiImpl, PluginHost } from '@zengrid/core';
44
- import type { OperationMode } from '@zengrid/shared';
45
- import { EVENT_MAP } from '../utils/event-map';
46
- import { ZenColumnComponent } from './zen-column.component';
47
- import { TemplateBridgeService } from '../services/template-bridge.service';
48
- import { ZEN_GRID_CONFIG } from '../services/zen-grid-config.token';
49
-
50
- @Component({
51
- selector: 'zen-grid',
52
- standalone: true,
53
- template: `
54
- @if (_isBrowser) {
55
- <div #gridContainer class="zen-grid-container"></div>
56
- } @else {
57
- <div class="zen-grid-placeholder" [style.height.px]="placeholderHeight"></div>
58
- }
59
- <div style="display:none">
60
- <ng-content></ng-content>
61
- </div>
62
- `,
63
- styles: [`
64
- :host {
65
- display: block;
66
- position: relative;
67
- height: 100%;
68
- }
69
- .zen-grid-container {
70
- width: 100%;
71
- height: 100%;
72
- }
73
- .zen-grid-placeholder {
74
- width: 100%;
75
- background: #f5f5f5;
76
- }
77
- `],
78
- changeDetection: ChangeDetectionStrategy.OnPush,
79
- })
80
- export class ZenGridComponent {
81
- private readonly platformId = inject(PLATFORM_ID);
82
- private readonly cdr = inject(ChangeDetectorRef);
83
- private readonly ngZone = inject(NgZone);
84
- private readonly destroyRef = inject(DestroyRef);
85
- private readonly templateBridge = inject(TemplateBridgeService);
86
- private readonly globalConfig = inject(ZEN_GRID_CONFIG, { optional: true });
87
-
88
- readonly _isBrowser = isPlatformBrowser(this.platformId);
89
-
90
- get placeholderHeight(): number {
91
- const rh = this.rowHeight();
92
- const h = typeof rh === 'number' ? rh : 30;
93
- return this.rowCount() * h;
94
- }
95
-
96
- private readonly gridContainerRef = viewChild<ElementRef<HTMLElement>>('gridContainer');
97
- private readonly columnChildren = contentChildren(ZenColumnComponent);
98
-
99
- private grid: Grid | null = null;
100
- private gridReady = false;
101
-
102
- // --- Core Inputs ---
103
- readonly rowCount = input.required<number>();
104
- readonly colCount = input.required<number>();
105
- readonly rowHeight = input<number | number[]>(30);
106
- readonly colWidth = input<number | number[]>(100);
107
- readonly columns = input<ColumnDef[]>([]);
108
-
109
- // --- Feature Inputs ---
110
- readonly enableSelection = input<boolean | undefined>(undefined);
111
- readonly enableMultiSelection = input<boolean | undefined>(undefined);
112
- readonly selectionType = input<'cell' | 'row' | 'column' | 'range' | undefined>(undefined);
113
- readonly enableColumnResize = input<boolean | undefined>(undefined);
114
- readonly enableColumnDrag = input<boolean | undefined>(undefined);
115
- readonly enableKeyboardNavigation = input<boolean | undefined>(undefined);
116
- readonly enableA11y = input<boolean | undefined>(undefined);
117
- readonly enableCellPooling = input<boolean | undefined>(undefined);
118
-
119
- // --- Mode Inputs ---
120
- readonly dataMode = input<OperationMode | undefined>(undefined);
121
- readonly sortMode = input<OperationMode | undefined>(undefined);
122
- readonly filterMode = input<OperationMode | undefined>(undefined);
123
-
124
- // --- Backend Callback Inputs ---
125
- readonly onDataRequest = input<GridOptions['onDataRequest']>(undefined);
126
- readonly onSortRequest = input<GridOptions['onSortRequest']>(undefined);
127
- readonly onFilterRequest = input<GridOptions['onFilterRequest']>(undefined);
128
- readonly onLoadMoreRows = input<GridOptions['onLoadMoreRows']>(undefined);
129
-
130
- // --- Config Object Inputs ---
131
- readonly pagination = input<PaginationConfig | undefined>(undefined);
132
- readonly loading = input<LoadingConfig | undefined>(undefined);
133
- readonly infiniteScrolling = input<GridOptions['infiniteScrolling']>(undefined);
134
- readonly rowHeightMode = input<'fixed' | 'auto' | 'content-aware' | undefined>(undefined);
135
- readonly rowHeightConfig = input<RowHeightConfig | undefined>(undefined);
136
- readonly columnResize = input<GridOptions['columnResize']>(undefined);
137
- readonly columnDrag = input<GridOptions['columnDrag']>(undefined);
138
- readonly cellOverflow = input<CellOverflowConfig | undefined>(undefined);
139
- readonly autoResize = input<boolean | undefined>(undefined);
140
- readonly overscanRows = input<number | undefined>(undefined);
141
- readonly overscanCols = input<number | undefined>(undefined);
142
- readonly sortIcons = input<SortIcons | undefined>(undefined);
143
- readonly rendererCache = input<RendererCacheConfig | undefined>(undefined);
144
-
145
- // --- Callback Inputs (GridOptions event callbacks) ---
146
- readonly onScrollCallback = input<GridOptions['onScroll']>(undefined);
147
- readonly onCellClickCallback = input<GridOptions['onCellClick']>(undefined);
148
- readonly onCellDoubleClickCallback = input<GridOptions['onCellDoubleClick']>(undefined);
149
- readonly onCellContextMenuCallback = input<GridOptions['onCellContextMenu']>(undefined);
150
- readonly onSelectionChangeCallback = input<GridOptions['onSelectionChange']>(undefined);
151
- readonly onPageChangeCallback = input<GridOptions['onPageChange']>(undefined);
152
- readonly onColumnWidthsChangeCallback = input<GridOptions['onColumnWidthsChange']>(undefined);
153
-
154
- // --- Plugin Input ---
155
- readonly plugins = input<GridPlugin[]>([]);
156
-
157
- // --- Two-way Model Signals ---
158
- readonly data = model<any[][]>([]);
159
- readonly selection = model<CellRange[]>([]);
160
- readonly sortState = model<SortState[]>([]);
161
- readonly filterState = model<FilterModel[]>([]);
162
- readonly currentPage = model<number>(1);
163
-
164
- // --- Cell Events ---
165
- readonly cellClick = output<GridEvents['cell:click']>();
166
- readonly cellDoubleClick = output<GridEvents['cell:doubleClick']>();
167
- readonly cellContextMenu = output<GridEvents['cell:contextMenu']>();
168
- readonly cellChange = output<GridEvents['cell:change']>();
169
- readonly cellBeforeChange = output<GridEvents['cell:beforeChange']>();
170
- readonly cellAfterChange = output<GridEvents['cell:afterChange']>();
171
-
172
- // --- Selection Events ---
173
- readonly selectionChangeEvent = output<GridEvents['selection:change']>({ alias: 'selectionChanged' });
174
- readonly selectionStart = output<GridEvents['selection:start']>();
175
- readonly selectionEnd = output<GridEvents['selection:end']>();
176
-
177
- // --- Editing Events ---
178
- readonly editStart = output<GridEvents['edit:start']>();
179
- readonly editEnd = output<GridEvents['edit:end']>();
180
- readonly editCommit = output<GridEvents['edit:commit']>();
181
- readonly editCancel = output<GridEvents['edit:cancel']>();
182
-
183
- // --- Scroll Events ---
184
- readonly scroll = output<GridEvents['scroll']>();
185
- readonly scrollStart = output<GridEvents['scroll:start']>();
186
- readonly scrollEnd = output<GridEvents['scroll:end']>();
187
-
188
- // --- Sort Events ---
189
- readonly sortChange = output<GridEvents['sort:change']>();
190
- readonly sortBeforeSort = output<GridEvents['sort:beforeSort']>();
191
- readonly sortAfterSort = output<GridEvents['sort:afterSort']>();
192
-
193
- // --- Filter Events ---
194
- readonly filterChange = output<GridEvents['filter:change']>();
195
- readonly filterBeforeFilter = output<GridEvents['filter:beforeFilter']>();
196
- readonly filterAfterFilter = output<GridEvents['filter:afterFilter']>();
197
- readonly filterExport = output<GridEvents['filter:export']>();
198
- readonly filterStart = output<GridEvents['filter:start']>();
199
- readonly filterEnd = output<GridEvents['filter:end']>();
200
- readonly filterError = output<GridEvents['filter:error']>();
201
- readonly filterClear = output<GridEvents['filter:clear']>();
202
-
203
- // --- Focus Events ---
204
- readonly focusChange = output<GridEvents['focus:change']>();
205
- readonly focusIn = output<GridEvents['focus:in']>();
206
- readonly focusOut = output<GridEvents['focus:out']>();
207
-
208
- // --- Keyboard Events ---
209
- readonly keyDown = output<GridEvents['key:down']>();
210
- readonly keyUp = output<GridEvents['key:up']>();
211
- readonly keyPress = output<GridEvents['key:press']>();
212
-
213
- // --- Column Events ---
214
- readonly columnResizeEvent = output<GridEvents['column:resize']>({ alias: 'columnResize' });
215
- readonly columnMove = output<GridEvents['column:move']>();
216
- readonly columnHide = output<GridEvents['column:hide']>();
217
- readonly columnShow = output<GridEvents['column:show']>();
218
- readonly columnDragStart = output<GridEvents['column:dragStart']>();
219
- readonly columnDragEvent = output<GridEvents['column:drag']>({ alias: 'columnDrag' });
220
- readonly columnDragEnd = output<GridEvents['column:dragEnd']>();
221
- readonly columnDragCancel = output<GridEvents['column:dragCancel']>();
222
-
223
- // --- Header Events ---
224
- readonly headerClick = output<GridEvents['header:click']>();
225
- readonly headerDoubleClick = output<GridEvents['header:doubleClick']>();
226
- readonly headerContextMenu = output<GridEvents['header:contextMenu']>();
227
- readonly headerHover = output<GridEvents['header:hover']>();
228
- readonly headerSortClick = output<GridEvents['header:sort:click']>();
229
- readonly headerFilterClick = output<GridEvents['header:filter:click']>();
230
- readonly headerCheckboxChange = output<GridEvents['header:checkbox:change']>();
231
-
232
- // --- Lifecycle Events ---
233
- readonly renderStart = output<GridEvents['render:start']>();
234
- readonly renderEnd = output<GridEvents['render:end']>();
235
- readonly dataLoad = output<GridEvents['data:load']>();
236
- readonly dataChange = output<GridEvents['data:change']>();
237
- readonly loadingStart = output<GridEvents['loading:start']>();
238
- readonly loadingEnd = output<GridEvents['loading:end']>();
239
- readonly loadingProgress = output<GridEvents['loading:progress']>();
240
- readonly undoRedoChange = output<GridEvents['undo-redo:change']>();
241
- readonly gridDestroy = output<GridEvents['destroy']>();
242
- readonly gridError = output<GridEvents['error']>();
243
- readonly gridWarning = output<GridEvents['warning']>();
244
-
245
- // --- Row Events ---
246
- readonly rowInsert = output<GridEvents['row:insert']>();
247
- readonly rowDelete = output<GridEvents['row:delete']>();
248
- readonly rowMove = output<GridEvents['row:move']>();
249
-
250
- // --- Clipboard Events ---
251
- readonly copy = output<GridEvents['copy']>();
252
- readonly cut = output<GridEvents['cut']>();
253
- readonly paste = output<GridEvents['paste']>();
254
-
255
- private outputMap: Record<string, OutputEmitterRef<any>> = {};
256
- private eventHandlers: Array<{ event: string; handler: (payload: any) => void }> = [];
257
-
258
- constructor() {
259
- this.buildOutputMap();
260
-
261
- this.destroyRef.onDestroy(() => {
262
- if (this.grid) {
263
- for (const { event, handler } of this.eventHandlers) {
264
- this.grid.off(event as any, handler);
265
- }
266
- this.grid.destroy();
267
- this.grid = null;
268
- }
269
- this.eventHandlers = [];
270
- this.gridReady = false;
271
- this.templateBridge.destroyAll();
272
- });
273
-
274
- afterNextRender(() => {
275
- this.ngZone.runOutsideAngular(() => {
276
- this.initGrid();
277
- });
278
- });
279
-
280
- // Sync data input → grid (skip first run, initGrid handles initial data)
281
- effect(() => {
282
- const d = this.data();
283
- if (!this.gridReady) return;
284
- if (d && d.length > 0) {
285
- this.grid!.setData(d);
286
- }
287
- });
288
-
289
- // Sync columns from content children or input (skip first run)
290
- effect(() => {
291
- const cols = this.columnChildren();
292
- const inputCols = this.columns();
293
- if (!this.gridReady) return;
294
-
295
- let resolvedColumns: ColumnDef[];
296
- if (cols.length > 0) {
297
- resolvedColumns = cols.map(c => c.toColumnDef(this.templateBridge));
298
- } else {
299
- resolvedColumns = inputCols;
300
- }
301
-
302
- if (resolvedColumns.length > 0) {
303
- this.grid!.updateOptions({ columns: resolvedColumns });
304
- }
305
- });
306
-
307
- // Sync option changes with previous-value diffing (only sends changed values)
308
- const prevOptions: Record<string, any> = {};
309
- effect(() => {
310
- const current: Array<[string, any]> = [
311
- ['rowHeight', this.rowHeight()],
312
- ['colWidth', this.colWidth()],
313
- ['selectionType', this.selectionType()],
314
- ['enableSelection', this.enableSelection()],
315
- ['enableMultiSelection', this.enableMultiSelection()],
316
- ['enableKeyboardNavigation', this.enableKeyboardNavigation()],
317
- ['enableA11y', this.enableA11y()],
318
- ['enableColumnResize', this.enableColumnResize()],
319
- ['enableColumnDrag', this.enableColumnDrag()],
320
- ['dataMode', this.dataMode()],
321
- ['sortMode', this.sortMode()],
322
- ['filterMode', this.filterMode()],
323
- ['pagination', this.pagination()],
324
- ['loading', this.loading()],
325
- ['infiniteScrolling', this.infiniteScrolling()],
326
- ['rowHeightMode', this.rowHeightMode()],
327
- ['rowHeightConfig', this.rowHeightConfig()],
328
- ['columnResize', this.columnResize()],
329
- ['columnDrag', this.columnDrag()],
330
- ['cellOverflow', this.cellOverflow()],
331
- ['autoResize', this.autoResize()],
332
- ['overscanRows', this.overscanRows()],
333
- ['overscanCols', this.overscanCols()],
334
- ];
335
-
336
- if (!this.gridReady) {
337
- // Store initial values for diffing on next run
338
- for (const [key, val] of current) {
339
- prevOptions[key] = val;
340
- }
341
- return;
342
- }
343
-
344
- const changed: Partial<GridOptions> = {};
345
- let hasChanges = false;
346
-
347
- for (const [key, val] of current) {
348
- if (val !== undefined && val !== prevOptions[key]) {
349
- (changed as any)[key] = val;
350
- hasChanges = true;
351
- }
352
- prevOptions[key] = val;
353
- }
354
-
355
- if (hasChanges) {
356
- this.grid!.updateOptions(changed);
357
- }
358
- });
359
- }
360
-
361
- private buildOutputMap(): void {
362
- this.outputMap = {
363
- cellClick: this.cellClick,
364
- cellDoubleClick: this.cellDoubleClick,
365
- cellContextMenu: this.cellContextMenu,
366
- cellChange: this.cellChange,
367
- cellBeforeChange: this.cellBeforeChange,
368
- cellAfterChange: this.cellAfterChange,
369
- selectionChange: this.selectionChangeEvent,
370
- selectionStart: this.selectionStart,
371
- selectionEnd: this.selectionEnd,
372
- editStart: this.editStart,
373
- editEnd: this.editEnd,
374
- editCommit: this.editCommit,
375
- editCancel: this.editCancel,
376
- scroll: this.scroll,
377
- scrollStart: this.scrollStart,
378
- scrollEnd: this.scrollEnd,
379
- sortChange: this.sortChange,
380
- sortBeforeSort: this.sortBeforeSort,
381
- sortAfterSort: this.sortAfterSort,
382
- filterChange: this.filterChange,
383
- filterBeforeFilter: this.filterBeforeFilter,
384
- filterAfterFilter: this.filterAfterFilter,
385
- filterExport: this.filterExport,
386
- filterStart: this.filterStart,
387
- filterEnd: this.filterEnd,
388
- filterError: this.filterError,
389
- filterClear: this.filterClear,
390
- focusChange: this.focusChange,
391
- focusIn: this.focusIn,
392
- focusOut: this.focusOut,
393
- keyDown: this.keyDown,
394
- keyUp: this.keyUp,
395
- keyPress: this.keyPress,
396
- columnResize: this.columnResizeEvent,
397
- columnMove: this.columnMove,
398
- columnHide: this.columnHide,
399
- columnShow: this.columnShow,
400
- columnDragStart: this.columnDragStart,
401
- columnDrag: this.columnDragEvent,
402
- columnDragEnd: this.columnDragEnd,
403
- columnDragCancel: this.columnDragCancel,
404
- headerClick: this.headerClick,
405
- headerDoubleClick: this.headerDoubleClick,
406
- headerContextMenu: this.headerContextMenu,
407
- headerHover: this.headerHover,
408
- headerSortClick: this.headerSortClick,
409
- headerFilterClick: this.headerFilterClick,
410
- headerCheckboxChange: this.headerCheckboxChange,
411
- renderStart: this.renderStart,
412
- renderEnd: this.renderEnd,
413
- dataLoad: this.dataLoad,
414
- dataChange: this.dataChange,
415
- loadingStart: this.loadingStart,
416
- loadingEnd: this.loadingEnd,
417
- loadingProgress: this.loadingProgress,
418
- undoRedoChange: this.undoRedoChange,
419
- gridDestroy: this.gridDestroy,
420
- gridError: this.gridError,
421
- gridWarning: this.gridWarning,
422
- rowInsert: this.rowInsert,
423
- rowDelete: this.rowDelete,
424
- rowMove: this.rowMove,
425
- copy: this.copy,
426
- cut: this.cut,
427
- paste: this.paste,
428
- };
429
- }
430
-
431
- private initGrid(): void {
432
- const containerRef = this.gridContainerRef();
433
- if (!containerRef) return;
434
-
435
- const container = containerRef.nativeElement;
436
- const columnChildren = this.columnChildren();
437
- const inputColumns = this.columns();
438
- const dataValue = this.data();
439
-
440
- let resolvedColumns: ColumnDef[];
441
- if (columnChildren.length > 0) {
442
- resolvedColumns = columnChildren.map(c => c.toColumnDef(this.templateBridge));
443
- } else {
444
- resolvedColumns = inputColumns;
445
- }
446
-
447
- const options: GridOptions = {
448
- rowCount: this.rowCount(),
449
- colCount: this.colCount(),
450
- rowHeight: this.rowHeight(),
451
- colWidth: this.colWidth(),
452
- columns: resolvedColumns.length > 0 ? resolvedColumns : undefined,
453
- ...this.getOptionalOptions(),
454
- ...(this.globalConfig ?? {}),
455
- };
456
-
457
- this.grid = new Grid(container, options);
458
-
459
- // Wire up events
460
- this.wireEvents();
461
-
462
- // Set initial data
463
- if (dataValue && dataValue.length > 0) {
464
- this.grid.setData(dataValue);
465
- }
466
-
467
- // Install plugins
468
- const pluginList = this.plugins();
469
- for (const plugin of pluginList) {
470
- this.grid.usePlugin(plugin);
471
- }
472
-
473
- this.grid.render();
474
- this.gridReady = true;
475
- }
476
-
477
- private getOptionalOptions(): Partial<GridOptions> {
478
- const opts: Partial<GridOptions> = {};
479
-
480
- const enSel = this.enableSelection();
481
- const enMulti = this.enableMultiSelection();
482
- const selType = this.selectionType();
483
- const enKb = this.enableKeyboardNavigation();
484
- const enA11y = this.enableA11y();
485
- const enPool = this.enableCellPooling();
486
- const enResize = this.enableColumnResize();
487
- const enDrag = this.enableColumnDrag();
488
- const dMode = this.dataMode();
489
- const sMode = this.sortMode();
490
- const fMode = this.filterMode();
491
- const dReq = this.onDataRequest();
492
- const sReq = this.onSortRequest();
493
- const fReq = this.onFilterRequest();
494
- const loadMore = this.onLoadMoreRows();
495
- const pag = this.pagination();
496
- const load = this.loading();
497
- const infScroll = this.infiniteScrolling();
498
- const rhMode = this.rowHeightMode();
499
- const rhConfig = this.rowHeightConfig();
500
- const colResizeConf = this.columnResize();
501
- const colDragConf = this.columnDrag();
502
- const overflow = this.cellOverflow();
503
- const autoRes = this.autoResize();
504
- const oRows = this.overscanRows();
505
- const oCols = this.overscanCols();
506
- const sIcons = this.sortIcons();
507
- const rCache = this.rendererCache();
508
- const onScrollCb = this.onScrollCallback();
509
- const onCellClickCb = this.onCellClickCallback();
510
- const onCellDblClickCb = this.onCellDoubleClickCallback();
511
- const onCellCtxCb = this.onCellContextMenuCallback();
512
- const onSelChangeCb = this.onSelectionChangeCallback();
513
- const onPageChangeCb = this.onPageChangeCallback();
514
- const onColWidthsCb = this.onColumnWidthsChangeCallback();
515
-
516
- if (enSel !== undefined) opts.enableSelection = enSel;
517
- if (enMulti !== undefined) opts.enableMultiSelection = enMulti;
518
- if (selType !== undefined) opts.selectionType = selType;
519
- if (enKb !== undefined) opts.enableKeyboardNavigation = enKb;
520
- if (enA11y !== undefined) opts.enableA11y = enA11y;
521
- if (enPool !== undefined) opts.enableCellPooling = enPool;
522
- if (enResize !== undefined) opts.enableColumnResize = enResize;
523
- if (enDrag !== undefined) opts.enableColumnDrag = enDrag;
524
- if (dMode !== undefined) opts.dataMode = dMode;
525
- if (sMode !== undefined) opts.sortMode = sMode;
526
- if (fMode !== undefined) opts.filterMode = fMode;
527
- if (dReq !== undefined) opts.onDataRequest = dReq;
528
- if (sReq !== undefined) opts.onSortRequest = sReq;
529
- if (fReq !== undefined) opts.onFilterRequest = fReq;
530
- if (loadMore !== undefined) opts.onLoadMoreRows = loadMore;
531
- if (pag !== undefined) opts.pagination = pag;
532
- if (load !== undefined) opts.loading = load;
533
- if (infScroll !== undefined) opts.infiniteScrolling = infScroll;
534
- if (rhMode !== undefined) opts.rowHeightMode = rhMode;
535
- if (rhConfig !== undefined) opts.rowHeightConfig = rhConfig;
536
- if (colResizeConf !== undefined) opts.columnResize = colResizeConf;
537
- if (colDragConf !== undefined) opts.columnDrag = colDragConf;
538
- if (overflow !== undefined) opts.cellOverflow = overflow;
539
- if (autoRes !== undefined) opts.autoResize = autoRes;
540
- if (oRows !== undefined) opts.overscanRows = oRows;
541
- if (oCols !== undefined) opts.overscanCols = oCols;
542
- if (sIcons !== undefined) opts.sortIcons = sIcons;
543
- if (rCache !== undefined) opts.rendererCache = rCache;
544
- if (onScrollCb !== undefined) opts.onScroll = onScrollCb;
545
- if (onCellClickCb !== undefined) opts.onCellClick = onCellClickCb;
546
- if (onCellDblClickCb !== undefined) opts.onCellDoubleClick = onCellDblClickCb;
547
- if (onCellCtxCb !== undefined) opts.onCellContextMenu = onCellCtxCb;
548
- if (onSelChangeCb !== undefined) opts.onSelectionChange = onSelChangeCb;
549
- if (onPageChangeCb !== undefined) opts.onPageChange = onPageChangeCb;
550
- if (onColWidthsCb !== undefined) opts.onColumnWidthsChange = onColWidthsCb;
551
-
552
- return opts;
553
- }
554
-
555
- private wireEvents(): void {
556
- if (!this.grid) return;
557
-
558
- for (const [gridEvent, outputName] of Object.entries(EVENT_MAP)) {
559
- const outputRef = this.outputMap[outputName];
560
- if (!outputRef) continue;
561
-
562
- const handler = (payload: any) => {
563
- this.ngZone.run(() => {
564
- outputRef.emit(payload);
565
-
566
- // Update two-way model signals for specific events
567
- if (gridEvent === 'selection:change' && payload?.ranges) {
568
- this.selection.set(payload.ranges);
569
- } else if (gridEvent === 'sort:change' && payload?.sortState) {
570
- this.sortState.set(payload.sortState);
571
- } else if (gridEvent === 'filter:change' && payload?.filterState) {
572
- this.filterState.set(payload.filterState);
573
- } else if (gridEvent === 'data:change') {
574
- // data:change doesn't carry full data, skip model update
575
- }
576
-
577
- this.cdr.markForCheck();
578
- });
579
- };
580
-
581
- this.grid.on(gridEvent as any, handler);
582
- this.eventHandlers.push({ event: gridEvent, handler });
583
- }
584
- }
585
-
586
- // --- Public API Methods ---
587
-
588
- get gridInstance(): Grid | null {
589
- return this.grid;
590
- }
591
-
592
- render(): void {
593
- this.grid?.render();
594
- }
595
-
596
- refresh(): void {
597
- this.grid?.refresh();
598
- }
599
-
600
- clearCache(): void {
601
- this.grid?.clearCache();
602
- }
603
-
604
- updateCells(cells: CellRef[]): void {
605
- this.grid?.updateCells(cells);
606
- }
607
-
608
- setData(data: any[][]): void {
609
- this.grid?.setData(data);
610
- this.data.set(data);
611
- }
612
-
613
- getData(row: number, col: number): any {
614
- return this.grid?.getData(row, col);
615
- }
616
-
617
- // --- Sort API ---
618
- sortColumn(column: number, direction: 'asc' | 'desc' | null = 'asc'): void {
619
- if (direction === null) this.grid?.sort.clear();
620
- else this.grid?.sort.apply([{ column, direction }]);
621
- }
622
-
623
- toggleSort(column: number): void {
624
- this.grid?.sort.toggle(column);
625
- }
626
-
627
- getSortState(): SortState[] {
628
- return this.grid?.sort.getState() ?? [];
629
- }
630
-
631
- clearSort(): void {
632
- this.grid?.sort.clear();
633
- }
634
-
635
- setSortState(sortState: SortState[]): void {
636
- this.grid?.sort.setState(sortState);
637
- }
638
-
639
- // --- Filter API ---
640
- setFilter(column: number, operator: string, value: any): void {
641
- this.grid?.filter.set(column, operator, value);
642
- }
643
-
644
- setColumnFilter(
645
- column: number,
646
- conditions: Array<{ operator: string; value: any }>,
647
- logic: 'AND' | 'OR' = 'AND'
648
- ): void {
649
- this.grid?.filter.setColumn(column, conditions, logic);
650
- }
651
-
652
- getFilterState(): FilterModel[] {
653
- return this.grid?.filter.getState() ?? [];
654
- }
655
-
656
- setFilterState(models: FilterModel[]): void {
657
- this.grid?.filter.setState(models);
658
- }
659
-
660
- clearFilters(): void {
661
- this.grid?.filter.clear();
662
- }
663
-
664
- clearColumnFilter(column: number): void {
665
- this.grid?.filter.clearColumn(column);
666
- }
667
-
668
- setQuickFilter(query: string, columns?: number[]): void {
669
- this.grid?.filter.setQuick(query, columns);
670
- }
671
-
672
- clearQuickFilter(): void {
673
- this.grid?.filter.clearQuick();
674
- }
675
-
676
- // --- Export API ---
677
- exportCSV(options: GridExportOptions = {}): string {
678
- return this.grid?.export.csv(options) ?? '';
679
- }
680
-
681
- exportTSV(options: GridExportOptions = {}): string {
682
- return this.grid?.export.tsv(options) ?? '';
683
- }
684
-
685
- // --- Pagination API ---
686
- goToPage(page: number): void {
687
- this.grid?.pagination.goTo(page);
688
- this.currentPage.set(page);
689
- }
690
-
691
- nextPage(): void {
692
- this.grid?.pagination.next();
693
- this.currentPage.set(this.grid?.pagination.getCurrentPage() ?? 1);
694
- }
695
-
696
- previousPage(): void {
697
- this.grid?.pagination.previous();
698
- this.currentPage.set(this.grid?.pagination.getCurrentPage() ?? 1);
699
- }
700
-
701
- setPageSize(pageSize: number): void {
702
- this.grid?.pagination.setPageSize(pageSize);
703
- }
704
-
705
- getCurrentPage(): number {
706
- return this.grid?.pagination.getCurrentPage() ?? 1;
707
- }
708
-
709
- getPageSize(): number {
710
- return this.grid?.pagination.getPageSize() ?? 0;
711
- }
712
-
713
- getTotalPages(): number {
714
- return this.grid?.pagination.getTotalPages() ?? 0;
715
- }
716
-
717
- // --- Column API ---
718
- resizeColumn(col: number, width: number): void {
719
- this.grid?.resizeColumn(col, width);
720
- }
721
-
722
- autoFitColumn(col: number): void {
723
- this.grid?.autoFitColumn(col);
724
- }
725
-
726
- autoFitAllColumns(): void {
727
- this.grid?.autoFitAllColumns();
728
- }
729
-
730
- setColumnConstraints(col: number, constraints: ColumnConstraints): void {
731
- this.grid?.setColumnConstraints(col, constraints);
732
- }
733
-
734
- // --- State Persistence API ---
735
- getColumnState(): ColumnStateSnapshot[] {
736
- return this.grid?.getColumnState() ?? [];
737
- }
738
-
739
- applyColumnState(
740
- state: ColumnStateSnapshot[],
741
- options?: { applyWidth?: boolean; applyVisibility?: boolean; applyOrder?: boolean }
742
- ): void {
743
- this.grid?.applyColumnState(state, options);
744
- }
745
-
746
- getStateSnapshot(): GridStateSnapshot | null {
747
- return this.grid?.getStateSnapshot() ?? null;
748
- }
749
-
750
- applyStateSnapshot(snapshot: GridStateSnapshot): void {
751
- this.grid?.applyStateSnapshot(snapshot);
752
- }
753
-
754
- // --- Scroll API ---
755
- scrollToCell(row: number, col: number): void {
756
- this.grid?.scrollToCell(row, col);
757
- }
758
-
759
- getScrollPosition(): { top: number; left: number } {
760
- return this.grid?.getScrollPosition() ?? { top: 0, left: 0 };
761
- }
762
-
763
- getVisibleRange(): VisibleRange | null {
764
- return this.grid?.getVisibleRange() ?? null;
765
- }
766
-
767
- // --- Header API ---
768
- registerHeaderRenderer(name: string, renderer: HeaderRenderer): void {
769
- this.grid?.registerHeaderRenderer(name, renderer);
770
- }
771
-
772
- updateHeader(columnIndex: number): void {
773
- this.grid?.updateHeader(columnIndex);
774
- }
775
-
776
- updateAllHeaders(): void {
777
- this.grid?.updateAllHeaders();
778
- }
779
-
780
- // --- Plugin API ---
781
- usePlugin(plugin: GridPlugin): void {
782
- this.grid?.usePlugin(plugin);
783
- }
784
-
785
- // --- Stats ---
786
- getStats(): any {
787
- return this.grid?.getStats();
788
- }
789
-
790
- getDimensions(): { width: number; height: number } {
791
- return this.grid?.getDimensions() ?? { width: 0, height: 0 };
792
- }
793
-
794
- getColumnPosition(col: number): { x: number; width: number } {
795
- return this.grid?.getColumnPosition(col) ?? { x: 0, width: 0 };
796
- }
797
-
798
- // --- Data Mode ---
799
- getDataMode(): 'frontend' | 'backend' {
800
- return this.grid?.getDataMode() ?? 'frontend';
801
- }
802
-
803
- // --- Sort (missing methods) ---
804
- getColumnSort(column: number): any {
805
- return this.grid?.getColumnSort(column);
806
- }
807
-
808
- getSortIcons(): { asc: string; desc: string } {
809
- return this.grid?.getSortIcons() ?? { asc: '▲', desc: '▼' };
810
- }
811
-
812
- getSortMode(): 'frontend' | 'backend' {
813
- return this.grid?.getSortMode() ?? 'frontend';
814
- }
815
-
816
- // --- Filter (missing methods) ---
817
- getFieldFilterState(): any {
818
- return this.grid?.getFieldFilterState();
819
- }
820
-
821
- getFilterExports(): any {
822
- return this.grid?.getFilterExports();
823
- }
824
-
825
- getFilterMode(): 'frontend' | 'backend' {
826
- return this.grid?.getFilterMode() ?? 'frontend';
827
- }
828
-
829
- getQuickFilter(): { query: string; columns: number[] | null } {
830
- return this.grid?.getQuickFilter() ?? { query: '', columns: null };
831
- }
832
-
833
- // --- Pagination (missing methods) ---
834
- firstPage(): void {
835
- this.grid?.firstPage();
836
- this.currentPage.set(this.grid?.getCurrentPage() ?? 1);
837
- }
838
-
839
- lastPage(): void {
840
- this.grid?.lastPage();
841
- this.currentPage.set(this.grid?.getCurrentPage() ?? 1);
842
- }
843
-
844
- // --- Scroll (missing methods) ---
845
- scrollThroughCells(
846
- cells: Array<{ row: number; col: number }>,
847
- options?: {
848
- delayMs?: number;
849
- smooth?: boolean;
850
- onCellReached?: (cell: { row: number; col: number }, index: number) => void;
851
- }
852
- ): { promise: Promise<void>; abort: () => void } {
853
- return this.grid?.scrollThroughCells(cells, options) ?? {
854
- promise: Promise.resolve(),
855
- abort: () => {},
856
- };
857
- }
858
-
859
- // --- Column Resize/Drag (missing methods) ---
860
- attachColumnResize(headerElement: HTMLElement): void {
861
- this.grid?.attachColumnResize(headerElement);
862
- }
863
-
864
- detachColumnResize(): void {
865
- this.grid?.detachColumnResize();
866
- }
867
-
868
- updateColumnResizeHandles(): void {
869
- this.grid?.updateColumnResizeHandles();
870
- }
871
-
872
- attachColumnDrag(headerElement: HTMLElement): void {
873
- this.grid?.attachColumnDrag(headerElement);
874
- }
875
-
876
- detachColumnDrag(): void {
877
- this.grid?.detachColumnDrag();
878
- }
879
-
880
- isDragging(): boolean {
881
- return this.grid?.isDragging() ?? false;
882
- }
883
-
884
- // --- Renderer Registration ---
885
- registerRenderer(name: string, renderer: any): void {
886
- this.grid?.registerRenderer(name, renderer);
887
- }
888
-
889
- // --- Options Update ---
890
- updateOptions(options: Partial<GridOptions>): void {
891
- this.grid?.updateOptions(options);
892
- }
893
-
894
- // --- Header (missing methods) ---
895
- refreshHeaders(): void {
896
- this.grid?.refreshHeaders();
897
- }
898
-
899
- // --- Infinite Scroll ---
900
- resetInfiniteScrolling(): void {
901
- this.grid?.resetInfiniteScrolling();
902
- }
903
-
904
- getSlidingWindowStats(): any {
905
- return this.grid?.getSlidingWindowStats();
906
- }
907
-
908
- // --- Plugin System (advanced) ---
909
- getStore(): any {
910
- return this.grid?.getStore();
911
- }
912
-
913
- getPluginHost(): PluginHost | null {
914
- return this.grid?.getPluginHost() ?? null;
915
- }
916
-
917
- getGridApi(): GridApiImpl | null {
918
- return this.grid?.getGridApi() ?? null;
919
- }
920
-
921
- }