@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 +55 -0
- package/fesm2022/gp-grid-angular.mjs +110 -7
- package/package.json +2 -2
- package/types/gp-grid-angular.d.ts +76 -3
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,
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
-
|
|
449
|
-
|
|
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 };
|