@gp-grid/angular 0.12.2 → 0.13.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.
package/README.md CHANGED
@@ -73,6 +73,61 @@ Import the stylesheet once (e.g. in `styles.css` or `angular.json`):
73
73
 
74
74
  For custom cell, edit, and header renderers, pass `ng-template` references via the column `cellRenderer` / `editRenderer` / `headerRenderer` fields — see the [Angular docs](https://www.gp-grid.io/docs/angular) for the full API.
75
75
 
76
+ ## Dependency injection
77
+
78
+ For components that want lifecycle-managed cleanup or testable seams, use `provideGridData` and `injectGridData`. They wire the same mutable data source through Angular's DI, mirroring `useGridData` in `@gp-grid/react` and `@gp-grid/vue`. The service implements `OnDestroy` and clears the data source automatically when the component is destroyed.
79
+
80
+ ```ts
81
+ import { Component } from "@angular/core";
82
+ import {
83
+ GpGridComponent,
84
+ provideGridData,
85
+ injectGridData,
86
+ } from "@gp-grid/angular";
87
+ import type { AngularColumnDefinition } from "@gp-grid/angular";
88
+
89
+ interface Person {
90
+ id: number;
91
+ name: string;
92
+ age: number;
93
+ }
94
+
95
+ const initialRows: Person[] = [
96
+ { id: 1, name: "Alice", age: 30 },
97
+ { id: 2, name: "Bob", age: 25 },
98
+ ];
99
+
100
+ @Component({
101
+ selector: "app-root",
102
+ standalone: true,
103
+ imports: [GpGridComponent],
104
+ providers: [
105
+ provideGridData<Person>({
106
+ getRowId: (row) => row.id,
107
+ initialData: initialRows,
108
+ }),
109
+ ],
110
+ template: `
111
+ <gp-grid
112
+ [columns]="columns"
113
+ [dataSource]="grid.dataSource"
114
+ [rowHeight]="36" />
115
+ <button (click)="grid.addRows([{ id: 3, name: 'Carol', age: 28 }])">Add</button>
116
+ `,
117
+ })
118
+ export class App {
119
+ protected readonly grid = injectGridData<Person>();
120
+
121
+ protected readonly columns: AngularColumnDefinition[] = [
122
+ { field: "id", cellDataType: "number", headerName: "ID", width: 80 },
123
+ { field: "name", cellDataType: "text", headerName: "Name", width: 200 },
124
+ { field: "age", cellDataType: "number", headerName: "Age", width: 100 },
125
+ ];
126
+ }
127
+ ```
128
+
129
+ `provideGridData` returns a standard Angular `Provider[]`, so it composes with other `provide*` functions in the component's `providers` array. Register it on the consuming component (not on a parent injector) so each component instance gets its own data source.
130
+
76
131
  ## License
77
132
 
78
133
  Apache-2.0 — see [LICENSE](./LICENSE).
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, computed, TemplateRef, ChangeDetectionStrategy, Component, ViewChild, signal, effect, HostListener, inject, PLATFORM_ID } from '@angular/core';
2
+ import { input, output, computed, TemplateRef, ChangeDetectionStrategy, Component, ViewChild, signal, effect, HostListener, inject, PLATFORM_ID, InjectionToken, Injectable } from '@angular/core';
3
3
  import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
4
- import { isCellSelected, isCellActive, getFieldValue, formatCellValue, isCellInFillPreview, buildCellClasses, isCellEditing, calculateFilterPopupPosition, calculateScaledColumnPositions, getTotalWidth, calculateFillHandlePosition, DataSourceOwner, AutoScrollDriver, PendingRowDragController, InputEventAdapter, applyBatchInstructions, scrollCellIntoView, GridCore, createMutableClientDataSource } from '@gp-grid/core';
4
+ import { getFieldValue, isCellSelected, isCellActive, formatCellValue, isCellInFillPreview, buildCellClasses, isCellEditing, calculateFilterPopupPosition, calculateScaledColumnPositions, getTotalWidth, calculateFillHandlePosition, DataSourceOwner, AutoScrollDriver, PendingRowDragController, InputEventAdapter, applyBatchInstructions, scrollCellIntoView, GridCore, createMutableClientDataSource } from '@gp-grid/core';
5
5
  export { GridCore, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
6
6
 
7
7
  const TEMPLATE$1 = `
@@ -302,8 +302,12 @@ class GridBodyComponent {
302
302
  this.scrolled.emit(el.scrollLeft);
303
303
  }
304
304
  cellParams(rowData, column, rowIndex, colIndex) {
305
+ const rawValue = getFieldValue(rowData, column.field);
306
+ const displayValue = column.valueFormatter
307
+ ? column.valueFormatter(rawValue)
308
+ : rawValue;
305
309
  return {
306
- value: getFieldValue(rowData, column.field),
310
+ value: displayValue,
307
311
  rowData,
308
312
  column,
309
313
  rowIndex,
@@ -339,8 +343,12 @@ class GridBodyComponent {
339
343
  }
340
344
  editParams(rowData, column, rowIndex, colIndex) {
341
345
  const ec = this.editingCell();
346
+ const rawValue = getFieldValue(rowData, column.field);
347
+ const displayValue = column.valueFormatter
348
+ ? column.valueFormatter(rawValue)
349
+ : rawValue;
342
350
  return {
343
- value: getFieldValue(rowData, column.field),
351
+ value: displayValue,
344
352
  rowData,
345
353
  column,
346
354
  rowIndex,
@@ -656,10 +664,12 @@ const computeUniqueValues = (distinctValues, formatter) => {
656
664
  for (const val of distinctValues) {
657
665
  if (val === null || val === undefined || val === '')
658
666
  continue;
659
- const key = String(val);
667
+ // Key by formatter output when available so filter-time comparison
668
+ // (which also goes through the formatter) matches what the user selects.
669
+ const key = formatter ? formatter(val) : String(val);
660
670
  if (!seen.has(key)) {
661
671
  seen.add(key);
662
- result.push({ key, label: formatter ? formatter(val) : key });
672
+ result.push({ key, label: key });
663
673
  }
664
674
  }
665
675
  return result.sort((a, b) => a.label.localeCompare(b.label, undefined, { numeric: true, sensitivity: 'base' }));
@@ -1625,9 +1635,102 @@ const createGridData = (initialData, options) => {
1625
1635
  };
1626
1636
  };
1627
1637
 
1638
+ /**
1639
+ * Injection token holding the options passed to {@link provideGridData}.
1640
+ * Read internally by {@link GridDataService}; consumers should not depend on it directly.
1641
+ */
1642
+ const GRID_DATA_OPTIONS = new InjectionToken("GRID_DATA_OPTIONS");
1643
+ /**
1644
+ * Angular service mirroring the React `useGridData` hook and the Vue
1645
+ * `useGridData` composable. Wraps `createMutableClientDataSource` with
1646
+ * automatic cleanup on component destroy.
1647
+ *
1648
+ * Provided per-component via {@link provideGridData}; injected via
1649
+ * {@link injectGridData} (or `inject(GridDataService)` with a manual cast).
1650
+ */
1651
+ class GridDataService {
1652
+ dataSource;
1653
+ constructor() {
1654
+ const options = inject(GRID_DATA_OPTIONS);
1655
+ this.dataSource = createMutableClientDataSource(options.initialData, {
1656
+ getRowId: options.getRowId,
1657
+ debounceMs: options.debounceMs,
1658
+ useWorker: options.useWorker,
1659
+ parallelSort: options.parallelSort,
1660
+ });
1661
+ }
1662
+ updateRow(id, data) {
1663
+ this.dataSource.updateRow(id, data);
1664
+ }
1665
+ addRows(rows) {
1666
+ this.dataSource.addRows(rows);
1667
+ }
1668
+ removeRows(ids) {
1669
+ this.dataSource.removeRows(ids);
1670
+ }
1671
+ updateCell(id, field, value) {
1672
+ this.dataSource.updateCell(id, field, value);
1673
+ }
1674
+ clear() {
1675
+ this.dataSource.clear();
1676
+ }
1677
+ getRowById(id) {
1678
+ return this.dataSource.getRowById(id);
1679
+ }
1680
+ getTotalRowCount() {
1681
+ return this.dataSource.getTotalRowCount();
1682
+ }
1683
+ flushTransactions() {
1684
+ return this.dataSource.flushTransactions();
1685
+ }
1686
+ ngOnDestroy() {
1687
+ this.dataSource.clear();
1688
+ }
1689
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1690
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridDataService });
1691
+ }
1692
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridDataService, decorators: [{
1693
+ type: Injectable
1694
+ }], ctorParameters: () => [] });
1695
+ /**
1696
+ * Typed convenience helper for `inject(GridDataService) as GridDataService<TData>`.
1697
+ *
1698
+ * @example
1699
+ * ```ts
1700
+ * @Component({
1701
+ * providers: [provideGridData<Person>({ getRowId: (r) => r.id, initialData: rows })],
1702
+ * })
1703
+ * export class MyGridComponent {
1704
+ * protected readonly grid = injectGridData<Person>();
1705
+ * }
1706
+ * ```
1707
+ */
1708
+ const injectGridData = () => inject(GridDataService);
1709
+
1710
+ /**
1711
+ * Returns the providers needed to bind a {@link GridDataService} to the
1712
+ * current injector (typically a component's `providers` array). The service
1713
+ * is scoped to that injector — registering it on a parent injector would
1714
+ * silently share the underlying data source between children.
1715
+ *
1716
+ * @example
1717
+ * ```ts
1718
+ * @Component({
1719
+ * providers: [provideGridData<Person>({ getRowId: (r) => r.id, initialData: rows })],
1720
+ * })
1721
+ * export class MyGridComponent {
1722
+ * protected readonly grid = injectGridData<Person>();
1723
+ * }
1724
+ * ```
1725
+ */
1726
+ const provideGridData = (options) => [
1727
+ { provide: GRID_DATA_OPTIONS, useValue: options },
1728
+ GridDataService,
1729
+ ];
1730
+
1628
1731
  /**
1629
1732
  * Generated bundle index. Do not edit.
1630
1733
  */
1631
1734
 
1632
- export { FilterPopupComponent, GpGridComponent, GridBodyComponent, GridHeaderComponent, GridOverlaysComponent, createGridData };
1735
+ export { FilterPopupComponent, GRID_DATA_OPTIONS, GpGridComponent, GridBodyComponent, GridDataService, GridHeaderComponent, GridOverlaysComponent, createGridData, injectGridData, provideGridData };
1633
1736
  //# sourceMappingURL=gp-grid-angular.mjs.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gp-grid/angular",
3
3
  "description": "A high-performance Angular data grid component with virtual scrolling, cell selection, sorting, filtering, and Excel-like editing",
4
- "version": "0.12.2",
4
+ "version": "0.13.1",
5
5
  "license": "Apache-2.0",
6
6
  "module": "fesm2022/gp-grid-angular.mjs",
7
7
  "typings": "types/gp-grid-angular.d.ts",
@@ -51,7 +51,7 @@
51
51
  "@angular/core": ">=18.0.0"
52
52
  },
53
53
  "dependencies": {
54
- "@gp-grid/core": "0.12.2",
54
+ "@gp-grid/core": "0.13.1",
55
55
  "tslib": "^2.8.1"
56
56
  },
57
57
  "sideEffects": false,
@@ -1,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { TemplateRef, ElementRef, AfterViewInit, OnDestroy, Signal, OnInit } from '@angular/core';
2
+ import { TemplateRef, ElementRef, AfterViewInit, OnDestroy, Signal, OnInit, InjectionToken, Provider } from '@angular/core';
3
3
  import * as _gp_grid_core from '@gp-grid/core';
4
4
  import { HeaderRendererParams, SortDirection, VisibleColumnInfo, HeaderData, ColumnDefinition, SlotData, CellPosition, CellRange, CellValue, CellRendererParams, EditRendererParams, FillHandlePosition, DragState, ColumnFilterModel, BatchChangeSetters, DataSource, RowId, HighlightingOptions, CellValueChangedEvent, ParallelSortOptions, MutableDataSource } from '@gp-grid/core';
5
5
  export { CellDataType, CellPosition, CellRange, CellRendererParams, CellValue, CellValueChangedEvent, ColumnDefinition, ColumnFilterModel, DataSource, DataSourceRequest, DataSourceResponse, EditRendererParams, FilterCondition, FilterModel, GridCore, GridInstruction, HeaderRendererParams, HighlightContext, HighlightingOptions, MutableDataSource, RowId, SortDirection, SortModel, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
@@ -445,5 +445,78 @@ interface GridDataApi<TData> {
445
445
  */
446
446
  declare const createGridData: <TData = unknown>(initialData: TData[], options: CreateGridDataOptions<TData>) => GridDataApi<TData>;
447
447
 
448
- export { FilterPopupComponent, GpGridComponent, GridBodyComponent, GridHeaderComponent, GridOverlaysComponent, createGridData };
449
- export type { ActiveFilterPopup, AngularColumnDefinition, CellClassFn, CellDoubleClickEvent, CellPointerDownEvent, CellPointerEnterEvent, CellRendererTemplate, CreateGridDataOptions, EditRendererTemplate, EditingCellState, FillHandlePointerDownEvent, FilterPointerDownEvent, GridDataApi, HeaderPointerDownEvent, HeaderRendererTemplate, HeaderSortEvent, ResizePointerDownEvent, RowClassFn };
448
+ interface GridDataOptions<TData> {
449
+ /** Initial rows to seed the data source. */
450
+ initialData: TData[];
451
+ /** Function to extract a unique ID from each row. Required. */
452
+ getRowId: (row: TData) => RowId;
453
+ /** Debounce time for batching transactions in ms. Default 50. */
454
+ debounceMs?: number;
455
+ /** Use Web Worker for sorting large datasets (default: true) */
456
+ useWorker?: boolean;
457
+ /** Options for parallel sorting (only used when useWorker is true) */
458
+ parallelSort?: ParallelSortOptions | false;
459
+ }
460
+ /**
461
+ * Injection token holding the options passed to {@link provideGridData}.
462
+ * Read internally by {@link GridDataService}; consumers should not depend on it directly.
463
+ */
464
+ declare const GRID_DATA_OPTIONS: InjectionToken<GridDataOptions<unknown>>;
465
+ /**
466
+ * Angular service mirroring the React `useGridData` hook and the Vue
467
+ * `useGridData` composable. Wraps `createMutableClientDataSource` with
468
+ * automatic cleanup on component destroy.
469
+ *
470
+ * Provided per-component via {@link provideGridData}; injected via
471
+ * {@link injectGridData} (or `inject(GridDataService)` with a manual cast).
472
+ */
473
+ declare class GridDataService<TData = unknown> implements OnDestroy {
474
+ readonly dataSource: MutableDataSource<TData>;
475
+ constructor();
476
+ updateRow(id: RowId, data: Partial<TData>): void;
477
+ addRows(rows: TData[]): void;
478
+ removeRows(ids: RowId[]): void;
479
+ updateCell(id: RowId, field: string, value: CellValue): void;
480
+ clear(): void;
481
+ getRowById(id: RowId): TData | undefined;
482
+ getTotalRowCount(): number;
483
+ flushTransactions(): Promise<void>;
484
+ ngOnDestroy(): void;
485
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<GridDataService<any>, never>;
486
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<GridDataService<any>>;
487
+ }
488
+ /**
489
+ * Typed convenience helper for `inject(GridDataService) as GridDataService<TData>`.
490
+ *
491
+ * @example
492
+ * ```ts
493
+ * @Component({
494
+ * providers: [provideGridData<Person>({ getRowId: (r) => r.id, initialData: rows })],
495
+ * })
496
+ * export class MyGridComponent {
497
+ * protected readonly grid = injectGridData<Person>();
498
+ * }
499
+ * ```
500
+ */
501
+ declare const injectGridData: <TData>() => GridDataService<TData>;
502
+
503
+ /**
504
+ * Returns the providers needed to bind a {@link GridDataService} to the
505
+ * current injector (typically a component's `providers` array). The service
506
+ * is scoped to that injector — registering it on a parent injector would
507
+ * silently share the underlying data source between children.
508
+ *
509
+ * @example
510
+ * ```ts
511
+ * @Component({
512
+ * providers: [provideGridData<Person>({ getRowId: (r) => r.id, initialData: rows })],
513
+ * })
514
+ * export class MyGridComponent {
515
+ * protected readonly grid = injectGridData<Person>();
516
+ * }
517
+ * ```
518
+ */
519
+ declare const provideGridData: <TData>(options: GridDataOptions<TData>) => Provider[];
520
+
521
+ export { FilterPopupComponent, GRID_DATA_OPTIONS, GpGridComponent, GridBodyComponent, GridDataService, GridHeaderComponent, GridOverlaysComponent, createGridData, injectGridData, provideGridData };
522
+ export type { ActiveFilterPopup, AngularColumnDefinition, CellClassFn, CellDoubleClickEvent, CellPointerDownEvent, CellPointerEnterEvent, CellRendererTemplate, CreateGridDataOptions, EditRendererTemplate, EditingCellState, FillHandlePointerDownEvent, FilterPointerDownEvent, GridDataApi, GridDataOptions, HeaderPointerDownEvent, HeaderRendererTemplate, HeaderSortEvent, ResizePointerDownEvent, RowClassFn };