@gridstorm/angular 0.1.2 → 0.1.3

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GridStorm Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,203 +1,203 @@
1
- # @gridstorm/angular
2
-
3
- Angular adapter for [GridStorm](https://grid-data-analytics-explorer.vercel.app/) -- a high-performance, headless data grid engine.
4
-
5
- Provides a standalone Angular component (`<gridstorm>`) that wraps the GridStorm core engine and DOM renderer. Requires Angular 16+ (standalone component support).
6
-
7
- ## Installation
8
-
9
- ```bash
10
- npm install @gridstorm/angular @gridstorm/core @gridstorm/dom-renderer
11
- # or
12
- pnpm add @gridstorm/angular @gridstorm/core @gridstorm/dom-renderer
13
- ```
14
-
15
- ## Quick Start
16
-
17
- Import the standalone component directly in your Angular component:
18
-
19
- ```typescript
20
- import { Component } from '@angular/core';
21
- import { GridStormComponent } from '@gridstorm/angular';
22
- import { sortingPlugin } from '@gridstorm/plugin-sorting';
23
- import type { GridApi, ColumnDef } from '@gridstorm/angular';
24
-
25
- @Component({
26
- selector: 'app-employees',
27
- standalone: true,
28
- imports: [GridStormComponent],
29
- template: `
30
- <gridstorm
31
- [columns]="columns"
32
- [rowData]="rowData"
33
- [plugins]="plugins"
34
- [rowHeight]="40"
35
- [theme]="'dark'"
36
- [density]="'normal'"
37
- (gridReady)="onGridReady($event)"
38
- (selectionChanged)="onSelectionChanged($event)"
39
- style="height: 600px; width: 100%;"
40
- ></gridstorm>
41
- `,
42
- })
43
- export class EmployeesComponent {
44
- columns: ColumnDef[] = [
45
- { field: 'name', headerName: 'Name', sortable: true },
46
- { field: 'department', headerName: 'Department', sortable: true },
47
- { field: 'salary', headerName: 'Salary', width: 120 },
48
- ];
49
-
50
- rowData = [
51
- { name: 'Alice', department: 'Engineering', salary: 95000 },
52
- { name: 'Bob', department: 'Marketing', salary: 72000 },
53
- { name: 'Charlie', department: 'Engineering', salary: 110000 },
54
- ];
55
-
56
- plugins = [sortingPlugin()];
57
-
58
- private gridApi: GridApi | null = null;
59
-
60
- onGridReady(api: GridApi) {
61
- this.gridApi = api;
62
- }
63
-
64
- onSelectionChanged(event: any) {
65
- console.log('Selection changed:', event);
66
- }
67
- }
68
- ```
69
-
70
- ## Inputs
71
-
72
- | Input | Type | Default | Description |
73
- | ------------------- | --------------------------- | ---------- | ----------------------------------------------- |
74
- | `columns` | `ColumnDef[]` | `[]` | Column definitions |
75
- | `rowData` | `any[]` | `[]` | Row data array |
76
- | `plugins` | `GridPlugin[]` | `[]` | Plugins to install |
77
- | `getRowId` | `(params) => string` | `undefined`| Custom row ID generator |
78
- | `rowHeight` | `number` | `40` | Row height in pixels |
79
- | `theme` | `string` | `'light'` | Theme name (`'light'`, `'dark'`, or custom) |
80
- | `density` | `string` | `'normal'` | Density mode (`'compact'`, `'normal'`, etc.) |
81
- | `defaultColDef` | `Partial<ColumnDef>` | `undefined`| Default column config applied to all columns |
82
- | `paginationPageSize`| `number` | `undefined`| Rows per page (when pagination enabled) |
83
- | `headerHeight` | `number` | `undefined`| Header row height in pixels |
84
- | `domLayout` | `'normal' \| 'autoHeight'` | `undefined`| DOM layout mode |
85
- | `rowSelection` | `'single' \| 'multiple'` | `undefined`| Row selection mode |
86
- | `pagination` | `boolean` | `undefined`| Enable client-side pagination |
87
- | `ariaLabel` | `string` | `undefined`| ARIA label for accessibility |
88
-
89
- ## Outputs
90
-
91
- | Output | Payload | Description |
92
- | ------------------- | --------------------------- | ----------------------------------------------- |
93
- | `gridReady` | `GridApi` | Grid initialized, API is ready |
94
- | `rowDataChanged` | `{ rowData: any[] }` | Row data was replaced |
95
- | `selectionChanged` | `{ selectedNodes, source }` | Row selection changed |
96
- | `sortChanged` | `{ sortModel }` | Sort model changed |
97
- | `filterChanged` | `{ filterModel }` | Filter model changed |
98
- | `cellValueChanged` | `{ node, colId, ... }` | Cell value edited |
99
- | `cellClicked` | `{ node, colId, value }` | Cell was clicked |
100
- | `cellDoubleClicked` | `{ node, colId, value }` | Cell was double-clicked |
101
- | `rowClicked` | `{ node, event }` | Row was clicked |
102
- | `paginationChanged` | `{ currentPage, ... }` | Pagination state changed |
103
- | `columnResized` | `{ column, ... }` | Column was resized |
104
-
105
- ## GridStormService
106
-
107
- An injectable service for managing multiple grid instances across your application:
108
-
109
- ```typescript
110
- import { Component, OnDestroy } from '@angular/core';
111
- import { GridStormComponent, GridStormService } from '@gridstorm/angular';
112
- import type { GridApi } from '@gridstorm/angular';
113
-
114
- @Component({
115
- selector: 'app-dashboard',
116
- standalone: true,
117
- imports: [GridStormComponent],
118
- template: `
119
- <gridstorm
120
- [columns]="columns"
121
- [rowData]="rowData"
122
- (gridReady)="onGridReady($event)"
123
- ></gridstorm>
124
- `,
125
- })
126
- export class DashboardComponent implements OnDestroy {
127
- // ... columns, rowData ...
128
-
129
- constructor(private gridService: GridStormService) {}
130
-
131
- onGridReady(api: GridApi) {
132
- this.gridService.registerApi('dashboard-grid', api);
133
- }
134
-
135
- ngOnDestroy() {
136
- this.gridService.removeApi('dashboard-grid');
137
- }
138
- }
139
- ```
140
-
141
- Access from another component:
142
-
143
- ```typescript
144
- export class ToolbarComponent {
145
- constructor(private gridService: GridStormService) {}
146
-
147
- exportSelected() {
148
- const api = this.gridService.getApi('dashboard-grid');
149
- if (api) {
150
- const rows = api.getSelectedRows();
151
- // ... export logic
152
- }
153
- }
154
- }
155
- ```
156
-
157
- ## Build Notes
158
-
159
- This package ships TypeScript source files alongside the tsup-compiled output. Since Angular decorators require the Angular compiler for AOT (ahead-of-time) compilation in production builds, there are two integration approaches:
160
-
161
- ### Development / JIT Mode
162
-
163
- Import directly from the package -- the compiled ESM/CJS output works with Angular's JIT compiler:
164
-
165
- ```typescript
166
- import { GridStormComponent, GridStormService } from '@gridstorm/angular';
167
- ```
168
-
169
- ### Production / AOT Builds
170
-
171
- For production AOT builds, you may need to use `ngPackagr` or include the source files in your Angular project's compilation. The raw `.ts` source files are included in the published package under `src/` for this purpose.
172
-
173
- ## Theming
174
-
175
- Apply the `@gridstorm/theme-default` stylesheet and use the `theme` and `density` inputs:
176
-
177
- ```typescript
178
- // In your angular.json styles array:
179
- // "node_modules/@gridstorm/theme-default/dist/index.css"
180
-
181
- @Component({
182
- template: `
183
- <gridstorm
184
- [columns]="columns"
185
- [rowData]="rowData"
186
- [theme]="currentTheme"
187
- [density]="currentDensity"
188
- ></gridstorm>
189
- `,
190
- })
191
- export class ThemedGridComponent {
192
- currentTheme = 'dark';
193
- currentDensity = 'compact';
194
-
195
- toggleTheme() {
196
- this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light';
197
- }
198
- }
199
- ```
200
-
201
- ## License
202
-
203
- MIT
1
+ # @gridstorm/angular
2
+
3
+ Angular adapter for [GridStorm](https://grid-data-analytics-explorer.vercel.app/) -- a high-performance, headless data grid engine.
4
+
5
+ Provides a standalone Angular component (`<gridstorm>`) that wraps the GridStorm core engine and DOM renderer. Requires Angular 16+ (standalone component support).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @gridstorm/angular @gridstorm/core @gridstorm/dom-renderer
11
+ # or
12
+ pnpm add @gridstorm/angular @gridstorm/core @gridstorm/dom-renderer
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ Import the standalone component directly in your Angular component:
18
+
19
+ ```typescript
20
+ import { Component } from '@angular/core';
21
+ import { GridStormComponent } from '@gridstorm/angular';
22
+ import { sortingPlugin } from '@gridstorm/plugin-sorting';
23
+ import type { GridApi, ColumnDef } from '@gridstorm/angular';
24
+
25
+ @Component({
26
+ selector: 'app-employees',
27
+ standalone: true,
28
+ imports: [GridStormComponent],
29
+ template: `
30
+ <gridstorm
31
+ [columns]="columns"
32
+ [rowData]="rowData"
33
+ [plugins]="plugins"
34
+ [rowHeight]="40"
35
+ [theme]="'dark'"
36
+ [density]="'normal'"
37
+ (gridReady)="onGridReady($event)"
38
+ (selectionChanged)="onSelectionChanged($event)"
39
+ style="height: 600px; width: 100%;"
40
+ ></gridstorm>
41
+ `,
42
+ })
43
+ export class EmployeesComponent {
44
+ columns: ColumnDef[] = [
45
+ { field: 'name', headerName: 'Name', sortable: true },
46
+ { field: 'department', headerName: 'Department', sortable: true },
47
+ { field: 'salary', headerName: 'Salary', width: 120 },
48
+ ];
49
+
50
+ rowData = [
51
+ { name: 'Alice', department: 'Engineering', salary: 95000 },
52
+ { name: 'Bob', department: 'Marketing', salary: 72000 },
53
+ { name: 'Charlie', department: 'Engineering', salary: 110000 },
54
+ ];
55
+
56
+ plugins = [sortingPlugin()];
57
+
58
+ private gridApi: GridApi | null = null;
59
+
60
+ onGridReady(api: GridApi) {
61
+ this.gridApi = api;
62
+ }
63
+
64
+ onSelectionChanged(event: any) {
65
+ console.log('Selection changed:', event);
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Inputs
71
+
72
+ | Input | Type | Default | Description |
73
+ | ------------------- | --------------------------- | ---------- | ----------------------------------------------- |
74
+ | `columns` | `ColumnDef[]` | `[]` | Column definitions |
75
+ | `rowData` | `any[]` | `[]` | Row data array |
76
+ | `plugins` | `GridPlugin[]` | `[]` | Plugins to install |
77
+ | `getRowId` | `(params) => string` | `undefined`| Custom row ID generator |
78
+ | `rowHeight` | `number` | `40` | Row height in pixels |
79
+ | `theme` | `string` | `'light'` | Theme name (`'light'`, `'dark'`, or custom) |
80
+ | `density` | `string` | `'normal'` | Density mode (`'compact'`, `'normal'`, etc.) |
81
+ | `defaultColDef` | `Partial<ColumnDef>` | `undefined`| Default column config applied to all columns |
82
+ | `paginationPageSize`| `number` | `undefined`| Rows per page (when pagination enabled) |
83
+ | `headerHeight` | `number` | `undefined`| Header row height in pixels |
84
+ | `domLayout` | `'normal' \| 'autoHeight'` | `undefined`| DOM layout mode |
85
+ | `rowSelection` | `'single' \| 'multiple'` | `undefined`| Row selection mode |
86
+ | `pagination` | `boolean` | `undefined`| Enable client-side pagination |
87
+ | `ariaLabel` | `string` | `undefined`| ARIA label for accessibility |
88
+
89
+ ## Outputs
90
+
91
+ | Output | Payload | Description |
92
+ | ------------------- | --------------------------- | ----------------------------------------------- |
93
+ | `gridReady` | `GridApi` | Grid initialized, API is ready |
94
+ | `rowDataChanged` | `{ rowData: any[] }` | Row data was replaced |
95
+ | `selectionChanged` | `{ selectedNodes, source }` | Row selection changed |
96
+ | `sortChanged` | `{ sortModel }` | Sort model changed |
97
+ | `filterChanged` | `{ filterModel }` | Filter model changed |
98
+ | `cellValueChanged` | `{ node, colId, ... }` | Cell value edited |
99
+ | `cellClicked` | `{ node, colId, value }` | Cell was clicked |
100
+ | `cellDoubleClicked` | `{ node, colId, value }` | Cell was double-clicked |
101
+ | `rowClicked` | `{ node, event }` | Row was clicked |
102
+ | `paginationChanged` | `{ currentPage, ... }` | Pagination state changed |
103
+ | `columnResized` | `{ column, ... }` | Column was resized |
104
+
105
+ ## GridStormService
106
+
107
+ An injectable service for managing multiple grid instances across your application:
108
+
109
+ ```typescript
110
+ import { Component, OnDestroy } from '@angular/core';
111
+ import { GridStormComponent, GridStormService } from '@gridstorm/angular';
112
+ import type { GridApi } from '@gridstorm/angular';
113
+
114
+ @Component({
115
+ selector: 'app-dashboard',
116
+ standalone: true,
117
+ imports: [GridStormComponent],
118
+ template: `
119
+ <gridstorm
120
+ [columns]="columns"
121
+ [rowData]="rowData"
122
+ (gridReady)="onGridReady($event)"
123
+ ></gridstorm>
124
+ `,
125
+ })
126
+ export class DashboardComponent implements OnDestroy {
127
+ // ... columns, rowData ...
128
+
129
+ constructor(private gridService: GridStormService) {}
130
+
131
+ onGridReady(api: GridApi) {
132
+ this.gridService.registerApi('dashboard-grid', api);
133
+ }
134
+
135
+ ngOnDestroy() {
136
+ this.gridService.removeApi('dashboard-grid');
137
+ }
138
+ }
139
+ ```
140
+
141
+ Access from another component:
142
+
143
+ ```typescript
144
+ export class ToolbarComponent {
145
+ constructor(private gridService: GridStormService) {}
146
+
147
+ exportSelected() {
148
+ const api = this.gridService.getApi('dashboard-grid');
149
+ if (api) {
150
+ const rows = api.getSelectedRows();
151
+ // ... export logic
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ ## Build Notes
158
+
159
+ This package ships TypeScript source files alongside the tsup-compiled output. Since Angular decorators require the Angular compiler for AOT (ahead-of-time) compilation in production builds, there are two integration approaches:
160
+
161
+ ### Development / JIT Mode
162
+
163
+ Import directly from the package -- the compiled ESM/CJS output works with Angular's JIT compiler:
164
+
165
+ ```typescript
166
+ import { GridStormComponent, GridStormService } from '@gridstorm/angular';
167
+ ```
168
+
169
+ ### Production / AOT Builds
170
+
171
+ For production AOT builds, you may need to use `ngPackagr` or include the source files in your Angular project's compilation. The raw `.ts` source files are included in the published package under `src/` for this purpose.
172
+
173
+ ## Theming
174
+
175
+ Apply the `@gridstorm/theme-default` stylesheet and use the `theme` and `density` inputs:
176
+
177
+ ```typescript
178
+ // In your angular.json styles array:
179
+ // "node_modules/@gridstorm/theme-default/dist/index.css"
180
+
181
+ @Component({
182
+ template: `
183
+ <gridstorm
184
+ [columns]="columns"
185
+ [rowData]="rowData"
186
+ [theme]="currentTheme"
187
+ [density]="currentDensity"
188
+ ></gridstorm>
189
+ `,
190
+ })
191
+ export class ThemedGridComponent {
192
+ currentTheme = 'dark';
193
+ currentDensity = 'compact';
194
+
195
+ toggleTheme() {
196
+ this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light';
197
+ }
198
+ }
199
+ ```
200
+
201
+ ## License
202
+
203
+ MIT
package/dist/index.cjs CHANGED
@@ -33,6 +33,7 @@ exports.GridStormComponent = class GridStormComponent {
33
33
  this.rowClicked = new core.EventEmitter();
34
34
  this.paginationChanged = new core.EventEmitter();
35
35
  this.columnResized = new core.EventEmitter();
36
+ this.gridError = new core.EventEmitter();
36
37
  // ── Internal state ──
37
38
  this.engine = null;
38
39
  this.renderer = null;
@@ -40,7 +41,13 @@ exports.GridStormComponent = class GridStormComponent {
40
41
  }
41
42
  // ── Lifecycle ──
42
43
  ngOnInit() {
43
- this.initGrid();
44
+ try {
45
+ this.initGrid();
46
+ } catch (err) {
47
+ const error = err instanceof Error ? err : new Error(String(err));
48
+ console.error("[GridStorm Angular] Initialization error:", error);
49
+ this.gridError.emit(error);
50
+ }
44
51
  }
45
52
  ngOnChanges(changes) {
46
53
  if (!this.engine) return;
@@ -208,6 +215,9 @@ __decorateClass([
208
215
  __decorateClass([
209
216
  core.Output()
210
217
  ], exports.GridStormComponent.prototype, "columnResized", 2);
218
+ __decorateClass([
219
+ core.Output()
220
+ ], exports.GridStormComponent.prototype, "gridError", 2);
211
221
  __decorateClass([
212
222
  core.ViewChild("gridContainer", { static: true })
213
223
  ], exports.GridStormComponent.prototype, "gridContainerRef", 2);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/gridstorm.component.ts","../src/gridstorm.service.ts"],"names":["GridStormComponent","EventEmitter","createGrid","DomRenderer","Input","Output","ViewChild","Component","GridStormService","Injectable"],"mappings":"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","file":"index.cjs","sourcesContent":["// ─── GridStorm Angular Component ───\n// Standalone Angular component wrapping the headless core engine + DOM renderer.\n// Uses ElementRef + ViewChild to mount the DomRenderer into the template container.\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n OnInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ViewChild,\n} from '@angular/core';\nimport { createGrid } from '@gridstorm/core';\nimport { DomRenderer } from '@gridstorm/dom-renderer';\nimport type { GridConfig, GridApi, GridEngine, ColumnDef, GridPlugin } from '@gridstorm/core';\n\n@Component({\n selector: 'gridstorm',\n standalone: true,\n template: `\n <div\n #gridContainer\n class=\"gridstorm-wrapper\"\n [attr.data-theme]=\"theme\"\n [attr.data-density]=\"density\"\n style=\"width:100%;height:100%\"\n ></div>\n `,\n})\nexport class GridStormComponent implements OnInit, OnDestroy, OnChanges {\n // ── Inputs ──\n\n /** Column definitions describing each column's structure and behavior. */\n @Input() columns: ColumnDef[] = [];\n\n /** Client-side row data array. */\n @Input() rowData: any[] = [];\n\n /** Array of plugins to install during grid initialization. */\n @Input() plugins: GridPlugin[] = [];\n\n /** Callback to generate a unique string ID for each row. */\n @Input() getRowId?: (params: any) => string;\n\n /** Height of each data row in pixels. */\n @Input() rowHeight = 40;\n\n /** Theme identifier: 'light', 'dark', or custom theme name. */\n @Input() theme = 'light';\n\n /** Density mode: 'compact', 'normal', or 'comfortable'. */\n @Input() density = 'normal';\n\n /** Default column definition applied to all columns as fallback. */\n @Input() defaultColDef?: Partial<ColumnDef>;\n\n /** Number of rows per page when pagination is enabled. */\n @Input() paginationPageSize?: number;\n\n /** Height of the header row in pixels. */\n @Input() headerHeight?: number;\n\n /** Controls how the grid's DOM height is determined. */\n @Input() domLayout?: 'normal' | 'autoHeight' | 'print';\n\n /** Row selection mode: 'single', 'multiple', or false (disabled). */\n @Input() rowSelection?: 'single' | 'multiple' | false;\n\n /** When true, enables client-side pagination. */\n @Input() pagination?: boolean;\n\n /** ARIA label for the grid root element (screen reader accessibility). */\n @Input() ariaLabel?: string;\n\n // ── Outputs ──\n\n /** Emitted when the grid engine is fully initialized and the API is ready. */\n @Output() gridReady = new EventEmitter<GridApi>();\n\n /** Emitted when row data changes. */\n @Output() rowDataChanged = new EventEmitter<any>();\n\n /** Emitted when the selection state changes. */\n @Output() selectionChanged = new EventEmitter<any>();\n\n /** Emitted when the sort model changes. */\n @Output() sortChanged = new EventEmitter<any>();\n\n /** Emitted when the filter model changes. */\n @Output() filterChanged = new EventEmitter<any>();\n\n /** Emitted when a cell value is changed through editing. */\n @Output() cellValueChanged = new EventEmitter<any>();\n\n /** Emitted when a cell is clicked. */\n @Output() cellClicked = new EventEmitter<any>();\n\n /** Emitted when a cell is double-clicked. */\n @Output() cellDoubleClicked = new EventEmitter<any>();\n\n /** Emitted when a row is clicked. */\n @Output() rowClicked = new EventEmitter<any>();\n\n /** Emitted when pagination state changes. */\n @Output() paginationChanged = new EventEmitter<any>();\n\n /** Emitted when a column is resized. */\n @Output() columnResized = new EventEmitter<any>();\n\n // ── Template reference ──\n\n @ViewChild('gridContainer', { static: true })\n private gridContainerRef!: ElementRef<HTMLElement>;\n\n // ── Internal state ──\n\n private engine: GridEngine | null = null;\n private renderer: DomRenderer | null = null;\n private eventUnsubscribers: Array<() => void> = [];\n\n // ── Lifecycle ──\n\n ngOnInit(): void {\n this.initGrid();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (!this.engine) return;\n\n // Sync rowData changes to the engine\n if (changes['rowData'] && !changes['rowData'].firstChange) {\n this.engine.api.setRowData(this.rowData);\n }\n\n // Sync column definition changes to the engine\n if (changes['columns'] && !changes['columns'].firstChange) {\n this.engine.api.setColumnDefs(this.columns);\n }\n\n // Sync pagination page size\n if (changes['paginationPageSize'] && !changes['paginationPageSize'].firstChange) {\n if (this.paginationPageSize != null) {\n this.engine.api.setGridOption('paginationPageSize', this.paginationPageSize);\n }\n }\n }\n\n ngOnDestroy(): void {\n this.destroyGrid();\n }\n\n // ── Public API ──\n\n /**\n * Returns the underlying GridApi instance, or null if the grid\n * has not been initialized yet.\n */\n getApi(): GridApi | null {\n return this.engine?.api ?? null;\n }\n\n /**\n * Returns the underlying GridEngine instance, or null if the grid\n * has not been initialized yet.\n */\n getEngine(): GridEngine | null {\n return this.engine;\n }\n\n // ── Initialization ──\n\n private initGrid(): void {\n const container = this.gridContainerRef.nativeElement;\n if (!container) return;\n\n // Build core config from inputs\n const config: GridConfig = {\n columns: this.columns,\n rowData: this.rowData,\n plugins: this.plugins,\n getRowId: this.getRowId,\n rowHeight: this.rowHeight,\n headerHeight: this.headerHeight,\n defaultColDef: this.defaultColDef,\n domLayout: this.domLayout,\n rowSelection: this.rowSelection,\n pagination: this.pagination,\n paginationPageSize: this.paginationPageSize,\n ariaLabel: this.ariaLabel,\n theme: this.theme,\n };\n\n // Create the headless grid engine\n this.engine = createGrid(config);\n\n // Create and mount the DOM renderer\n this.renderer = new DomRenderer({\n container,\n engine: this.engine,\n });\n this.renderer.mount();\n\n // Wire up event bridge: core events -> Angular EventEmitters\n this.setupEventBridge();\n\n // Emit gridReady\n this.gridReady.emit(this.engine.api);\n }\n\n private setupEventBridge(): void {\n if (!this.engine) return;\n\n const eb = this.engine.eventBus;\n\n this.eventUnsubscribers = [\n eb.on('rowData:changed', (e) => this.rowDataChanged.emit(e)),\n eb.on('selection:changed', (e) => this.selectionChanged.emit(e)),\n eb.on('column:sort:changed', (e) => this.sortChanged.emit(e)),\n eb.on('filter:changed', (e) => this.filterChanged.emit(e)),\n eb.on('cell:valueChanged', (e) => this.cellValueChanged.emit(e)),\n eb.on('cell:clicked', (e) => this.cellClicked.emit(e)),\n eb.on('cell:doubleClicked', (e) => this.cellDoubleClicked.emit(e)),\n eb.on('row:clicked', (e) => this.rowClicked.emit(e)),\n eb.on('pagination:changed', (e) => this.paginationChanged.emit(e)),\n eb.on('column:resized', (e) => this.columnResized.emit(e)),\n ];\n }\n\n private destroyGrid(): void {\n // Unsubscribe from all core events\n for (const unsub of this.eventUnsubscribers) {\n unsub();\n }\n this.eventUnsubscribers = [];\n\n // Destroy the DOM renderer\n if (this.renderer) {\n this.renderer.destroy();\n this.renderer = null;\n }\n\n // Destroy the grid engine\n if (this.engine) {\n this.engine.destroy();\n this.engine = null;\n }\n }\n}\n","// ─── GridStorm Service ───\n// Injectable service for managing multiple GridStorm instances.\n// Allows Angular components to access grid APIs by a unique identifier,\n// useful in applications with multiple grids or cross-component communication.\n\nimport { Injectable } from '@angular/core';\nimport type { GridApi } from '@gridstorm/core';\n\n/**\n * Service for registering and retrieving GridStorm API instances.\n *\n * Use this service when you have multiple grids in your application\n * and need to access their APIs from different components or services.\n *\n * @example\n * ```typescript\n * // In a component that hosts the grid:\n * constructor(private gridService: GridStormService) {}\n *\n * onGridReady(api: GridApi) {\n * this.gridService.registerApi('employees', api);\n * }\n *\n * // In another component that needs to interact with the grid:\n * constructor(private gridService: GridStormService) {}\n *\n * exportData() {\n * const api = this.gridService.getApi('employees');\n * if (api) {\n * const rows = api.getSelectedRows();\n * // ... do something with rows\n * }\n * }\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class GridStormService {\n private apiMap = new Map<string, GridApi>();\n\n /**\n * Registers a GridApi instance under a unique identifier.\n *\n * Call this from the `(gridReady)` output handler of the `<gridstorm>` component.\n * If an API with the same ID already exists, it will be replaced.\n *\n * @param id - A unique string identifier for this grid instance.\n * @param api - The GridApi instance to register.\n */\n registerApi(id: string, api: GridApi): void {\n this.apiMap.set(id, api);\n }\n\n /**\n * Retrieves a previously registered GridApi by its identifier.\n *\n * @param id - The unique identifier used during registration.\n * @returns The GridApi instance, or `undefined` if not found.\n */\n getApi(id: string): GridApi | undefined {\n return this.apiMap.get(id);\n }\n\n /**\n * Removes a registered GridApi by its identifier.\n *\n * Call this when a grid component is destroyed to prevent memory leaks.\n *\n * @param id - The unique identifier of the API to remove.\n */\n removeApi(id: string): void {\n this.apiMap.delete(id);\n }\n\n /**\n * Returns all registered grid API identifiers.\n *\n * @returns An array of registered grid IDs.\n */\n getRegisteredIds(): string[] {\n return Array.from(this.apiMap.keys());\n }\n\n /**\n * Checks whether a grid API is registered under the given identifier.\n *\n * @param id - The unique identifier to check.\n * @returns `true` if an API is registered with that ID.\n */\n hasApi(id: string): boolean {\n return this.apiMap.has(id);\n }\n\n /**\n * Removes all registered grid APIs.\n *\n * Useful during application teardown or testing.\n */\n clear(): void {\n this.apiMap.clear();\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/gridstorm.component.ts","../src/gridstorm.service.ts"],"names":["GridStormComponent","EventEmitter","createGrid","DomRenderer","Input","Output","ViewChild","Component","GridStormService","Injectable"],"mappings":"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","file":"index.cjs","sourcesContent":["// ─── GridStorm Angular Component ───\r\n// Standalone Angular component wrapping the headless core engine + DOM renderer.\r\n// Uses ElementRef + ViewChild to mount the DomRenderer into the template container.\r\n\r\nimport {\r\n Component,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ElementRef,\r\n OnInit,\r\n OnDestroy,\r\n OnChanges,\r\n SimpleChanges,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { createGrid } from '@gridstorm/core';\r\nimport { DomRenderer } from '@gridstorm/dom-renderer';\r\nimport type { GridConfig, GridApi, GridEngine, ColumnDef, GridPlugin } from '@gridstorm/core';\r\n\r\n@Component({\r\n selector: 'gridstorm',\r\n standalone: true,\r\n template: `\r\n <div\r\n #gridContainer\r\n class=\"gridstorm-wrapper\"\r\n [attr.data-theme]=\"theme\"\r\n [attr.data-density]=\"density\"\r\n style=\"width:100%;height:100%\"\r\n ></div>\r\n `,\r\n})\r\nexport class GridStormComponent implements OnInit, OnDestroy, OnChanges {\r\n // ── Inputs ──\r\n\r\n /** Column definitions describing each column's structure and behavior. */\r\n @Input() columns: ColumnDef[] = [];\r\n\r\n /** Client-side row data array. */\r\n @Input() rowData: any[] = [];\r\n\r\n /** Array of plugins to install during grid initialization. */\r\n @Input() plugins: GridPlugin[] = [];\r\n\r\n /** Callback to generate a unique string ID for each row. */\r\n @Input() getRowId?: (params: any) => string;\r\n\r\n /** Height of each data row in pixels. */\r\n @Input() rowHeight = 40;\r\n\r\n /** Theme identifier: 'light', 'dark', or custom theme name. */\r\n @Input() theme = 'light';\r\n\r\n /** Density mode: 'compact', 'normal', or 'comfortable'. */\r\n @Input() density = 'normal';\r\n\r\n /** Default column definition applied to all columns as fallback. */\r\n @Input() defaultColDef?: Partial<ColumnDef>;\r\n\r\n /** Number of rows per page when pagination is enabled. */\r\n @Input() paginationPageSize?: number;\r\n\r\n /** Height of the header row in pixels. */\r\n @Input() headerHeight?: number;\r\n\r\n /** Controls how the grid's DOM height is determined. */\r\n @Input() domLayout?: 'normal' | 'autoHeight' | 'print';\r\n\r\n /** Row selection mode: 'single', 'multiple', or false (disabled). */\r\n @Input() rowSelection?: 'single' | 'multiple' | false;\r\n\r\n /** When true, enables client-side pagination. */\r\n @Input() pagination?: boolean;\r\n\r\n /** ARIA label for the grid root element (screen reader accessibility). */\r\n @Input() ariaLabel?: string;\r\n\r\n // ── Outputs ──\r\n\r\n /** Emitted when the grid engine is fully initialized and the API is ready. */\r\n @Output() gridReady = new EventEmitter<GridApi>();\r\n\r\n /** Emitted when row data changes. */\r\n @Output() rowDataChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when the selection state changes. */\r\n @Output() selectionChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when the sort model changes. */\r\n @Output() sortChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when the filter model changes. */\r\n @Output() filterChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when a cell value is changed through editing. */\r\n @Output() cellValueChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when a cell is clicked. */\r\n @Output() cellClicked = new EventEmitter<any>();\r\n\r\n /** Emitted when a cell is double-clicked. */\r\n @Output() cellDoubleClicked = new EventEmitter<any>();\r\n\r\n /** Emitted when a row is clicked. */\r\n @Output() rowClicked = new EventEmitter<any>();\r\n\r\n /** Emitted when pagination state changes. */\r\n @Output() paginationChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when a column is resized. */\r\n @Output() columnResized = new EventEmitter<any>();\r\n\r\n /** Emitted when an error occurs during grid initialization or operation. */\r\n @Output() gridError = new EventEmitter<Error>();\r\n\r\n // ── Template reference ──\r\n\r\n @ViewChild('gridContainer', { static: true })\r\n private gridContainerRef!: ElementRef<HTMLElement>;\r\n\r\n // ── Internal state ──\r\n\r\n private engine: GridEngine | null = null;\r\n private renderer: DomRenderer | null = null;\r\n private eventUnsubscribers: Array<() => void> = [];\r\n\r\n // ── Lifecycle ──\r\n\r\n ngOnInit(): void {\r\n try {\r\n this.initGrid();\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n console.error('[GridStorm Angular] Initialization error:', error);\r\n this.gridError.emit(error);\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (!this.engine) return;\r\n\r\n // Sync rowData changes to the engine\r\n if (changes['rowData'] && !changes['rowData'].firstChange) {\r\n this.engine.api.setRowData(this.rowData);\r\n }\r\n\r\n // Sync column definition changes to the engine\r\n if (changes['columns'] && !changes['columns'].firstChange) {\r\n this.engine.api.setColumnDefs(this.columns);\r\n }\r\n\r\n // Sync pagination page size\r\n if (changes['paginationPageSize'] && !changes['paginationPageSize'].firstChange) {\r\n if (this.paginationPageSize != null) {\r\n this.engine.api.setGridOption('paginationPageSize', this.paginationPageSize);\r\n }\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.destroyGrid();\r\n }\r\n\r\n // ── Public API ──\r\n\r\n /**\r\n * Returns the underlying GridApi instance, or null if the grid\r\n * has not been initialized yet.\r\n */\r\n getApi(): GridApi | null {\r\n return this.engine?.api ?? null;\r\n }\r\n\r\n /**\r\n * Returns the underlying GridEngine instance, or null if the grid\r\n * has not been initialized yet.\r\n */\r\n getEngine(): GridEngine | null {\r\n return this.engine;\r\n }\r\n\r\n // ── Initialization ──\r\n\r\n private initGrid(): void {\r\n const container = this.gridContainerRef.nativeElement;\r\n if (!container) return;\r\n\r\n // Build core config from inputs\r\n const config: GridConfig = {\r\n columns: this.columns,\r\n rowData: this.rowData,\r\n plugins: this.plugins,\r\n getRowId: this.getRowId,\r\n rowHeight: this.rowHeight,\r\n headerHeight: this.headerHeight,\r\n defaultColDef: this.defaultColDef,\r\n domLayout: this.domLayout,\r\n rowSelection: this.rowSelection,\r\n pagination: this.pagination,\r\n paginationPageSize: this.paginationPageSize,\r\n ariaLabel: this.ariaLabel,\r\n theme: this.theme,\r\n };\r\n\r\n // Create the headless grid engine\r\n this.engine = createGrid(config);\r\n\r\n // Create and mount the DOM renderer\r\n this.renderer = new DomRenderer({\r\n container,\r\n engine: this.engine,\r\n });\r\n this.renderer.mount();\r\n\r\n // Wire up event bridge: core events -> Angular EventEmitters\r\n this.setupEventBridge();\r\n\r\n // Emit gridReady\r\n this.gridReady.emit(this.engine.api);\r\n }\r\n\r\n private setupEventBridge(): void {\r\n if (!this.engine) return;\r\n\r\n const eb = this.engine.eventBus;\r\n\r\n this.eventUnsubscribers = [\r\n eb.on('rowData:changed', (e) => this.rowDataChanged.emit(e)),\r\n eb.on('selection:changed', (e) => this.selectionChanged.emit(e)),\r\n eb.on('column:sort:changed', (e) => this.sortChanged.emit(e)),\r\n eb.on('filter:changed', (e) => this.filterChanged.emit(e)),\r\n eb.on('cell:valueChanged', (e) => this.cellValueChanged.emit(e)),\r\n eb.on('cell:clicked', (e) => this.cellClicked.emit(e)),\r\n eb.on('cell:doubleClicked', (e) => this.cellDoubleClicked.emit(e)),\r\n eb.on('row:clicked', (e) => this.rowClicked.emit(e)),\r\n eb.on('pagination:changed', (e) => this.paginationChanged.emit(e)),\r\n eb.on('column:resized', (e) => this.columnResized.emit(e)),\r\n ];\r\n }\r\n\r\n private destroyGrid(): void {\r\n // Unsubscribe from all core events\r\n for (const unsub of this.eventUnsubscribers) {\r\n unsub();\r\n }\r\n this.eventUnsubscribers = [];\r\n\r\n // Destroy the DOM renderer\r\n if (this.renderer) {\r\n this.renderer.destroy();\r\n this.renderer = null;\r\n }\r\n\r\n // Destroy the grid engine\r\n if (this.engine) {\r\n this.engine.destroy();\r\n this.engine = null;\r\n }\r\n }\r\n}\r\n","// ─── GridStorm Service ───\r\n// Injectable service for managing multiple GridStorm instances.\r\n// Allows Angular components to access grid APIs by a unique identifier,\r\n// useful in applications with multiple grids or cross-component communication.\r\n\r\nimport { Injectable } from '@angular/core';\r\nimport type { GridApi } from '@gridstorm/core';\r\n\r\n/**\r\n * Service for registering and retrieving GridStorm API instances.\r\n *\r\n * Use this service when you have multiple grids in your application\r\n * and need to access their APIs from different components or services.\r\n *\r\n * @example\r\n * ```typescript\r\n * // In a component that hosts the grid:\r\n * constructor(private gridService: GridStormService) {}\r\n *\r\n * onGridReady(api: GridApi) {\r\n * this.gridService.registerApi('employees', api);\r\n * }\r\n *\r\n * // In another component that needs to interact with the grid:\r\n * constructor(private gridService: GridStormService) {}\r\n *\r\n * exportData() {\r\n * const api = this.gridService.getApi('employees');\r\n * if (api) {\r\n * const rows = api.getSelectedRows();\r\n * // ... do something with rows\r\n * }\r\n * }\r\n * ```\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class GridStormService {\r\n private apiMap = new Map<string, GridApi>();\r\n\r\n /**\r\n * Registers a GridApi instance under a unique identifier.\r\n *\r\n * Call this from the `(gridReady)` output handler of the `<gridstorm>` component.\r\n * If an API with the same ID already exists, it will be replaced.\r\n *\r\n * @param id - A unique string identifier for this grid instance.\r\n * @param api - The GridApi instance to register.\r\n */\r\n registerApi(id: string, api: GridApi): void {\r\n this.apiMap.set(id, api);\r\n }\r\n\r\n /**\r\n * Retrieves a previously registered GridApi by its identifier.\r\n *\r\n * @param id - The unique identifier used during registration.\r\n * @returns The GridApi instance, or `undefined` if not found.\r\n */\r\n getApi(id: string): GridApi | undefined {\r\n return this.apiMap.get(id);\r\n }\r\n\r\n /**\r\n * Removes a registered GridApi by its identifier.\r\n *\r\n * Call this when a grid component is destroyed to prevent memory leaks.\r\n *\r\n * @param id - The unique identifier of the API to remove.\r\n */\r\n removeApi(id: string): void {\r\n this.apiMap.delete(id);\r\n }\r\n\r\n /**\r\n * Returns all registered grid API identifiers.\r\n *\r\n * @returns An array of registered grid IDs.\r\n */\r\n getRegisteredIds(): string[] {\r\n return Array.from(this.apiMap.keys());\r\n }\r\n\r\n /**\r\n * Checks whether a grid API is registered under the given identifier.\r\n *\r\n * @param id - The unique identifier to check.\r\n * @returns `true` if an API is registered with that ID.\r\n */\r\n hasApi(id: string): boolean {\r\n return this.apiMap.has(id);\r\n }\r\n\r\n /**\r\n * Removes all registered grid APIs.\r\n *\r\n * Useful during application teardown or testing.\r\n */\r\n clear(): void {\r\n this.apiMap.clear();\r\n }\r\n}\r\n"]}
package/dist/index.d.cts CHANGED
@@ -53,6 +53,8 @@ declare class GridStormComponent implements OnInit, OnDestroy, OnChanges {
53
53
  paginationChanged: EventEmitter<any>;
54
54
  /** Emitted when a column is resized. */
55
55
  columnResized: EventEmitter<any>;
56
+ /** Emitted when an error occurs during grid initialization or operation. */
57
+ gridError: EventEmitter<Error>;
56
58
  private gridContainerRef;
57
59
  private engine;
58
60
  private renderer;
package/dist/index.d.ts CHANGED
@@ -53,6 +53,8 @@ declare class GridStormComponent implements OnInit, OnDestroy, OnChanges {
53
53
  paginationChanged: EventEmitter<any>;
54
54
  /** Emitted when a column is resized. */
55
55
  columnResized: EventEmitter<any>;
56
+ /** Emitted when an error occurs during grid initialization or operation. */
57
+ gridError: EventEmitter<Error>;
56
58
  private gridContainerRef;
57
59
  private engine;
58
60
  private renderer;
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ var GridStormComponent = class {
31
31
  this.rowClicked = new EventEmitter();
32
32
  this.paginationChanged = new EventEmitter();
33
33
  this.columnResized = new EventEmitter();
34
+ this.gridError = new EventEmitter();
34
35
  // ── Internal state ──
35
36
  this.engine = null;
36
37
  this.renderer = null;
@@ -38,7 +39,13 @@ var GridStormComponent = class {
38
39
  }
39
40
  // ── Lifecycle ──
40
41
  ngOnInit() {
41
- this.initGrid();
42
+ try {
43
+ this.initGrid();
44
+ } catch (err) {
45
+ const error = err instanceof Error ? err : new Error(String(err));
46
+ console.error("[GridStorm Angular] Initialization error:", error);
47
+ this.gridError.emit(error);
48
+ }
42
49
  }
43
50
  ngOnChanges(changes) {
44
51
  if (!this.engine) return;
@@ -206,6 +213,9 @@ __decorateClass([
206
213
  __decorateClass([
207
214
  Output()
208
215
  ], GridStormComponent.prototype, "columnResized", 2);
216
+ __decorateClass([
217
+ Output()
218
+ ], GridStormComponent.prototype, "gridError", 2);
209
219
  __decorateClass([
210
220
  ViewChild("gridContainer", { static: true })
211
221
  ], GridStormComponent.prototype, "gridContainerRef", 2);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/gridstorm.component.ts","../src/gridstorm.service.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ─── GridStorm Angular Component ───\n// Standalone Angular component wrapping the headless core engine + DOM renderer.\n// Uses ElementRef + ViewChild to mount the DomRenderer into the template container.\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n OnInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ViewChild,\n} from '@angular/core';\nimport { createGrid } from '@gridstorm/core';\nimport { DomRenderer } from '@gridstorm/dom-renderer';\nimport type { GridConfig, GridApi, GridEngine, ColumnDef, GridPlugin } from '@gridstorm/core';\n\n@Component({\n selector: 'gridstorm',\n standalone: true,\n template: `\n <div\n #gridContainer\n class=\"gridstorm-wrapper\"\n [attr.data-theme]=\"theme\"\n [attr.data-density]=\"density\"\n style=\"width:100%;height:100%\"\n ></div>\n `,\n})\nexport class GridStormComponent implements OnInit, OnDestroy, OnChanges {\n // ── Inputs ──\n\n /** Column definitions describing each column's structure and behavior. */\n @Input() columns: ColumnDef[] = [];\n\n /** Client-side row data array. */\n @Input() rowData: any[] = [];\n\n /** Array of plugins to install during grid initialization. */\n @Input() plugins: GridPlugin[] = [];\n\n /** Callback to generate a unique string ID for each row. */\n @Input() getRowId?: (params: any) => string;\n\n /** Height of each data row in pixels. */\n @Input() rowHeight = 40;\n\n /** Theme identifier: 'light', 'dark', or custom theme name. */\n @Input() theme = 'light';\n\n /** Density mode: 'compact', 'normal', or 'comfortable'. */\n @Input() density = 'normal';\n\n /** Default column definition applied to all columns as fallback. */\n @Input() defaultColDef?: Partial<ColumnDef>;\n\n /** Number of rows per page when pagination is enabled. */\n @Input() paginationPageSize?: number;\n\n /** Height of the header row in pixels. */\n @Input() headerHeight?: number;\n\n /** Controls how the grid's DOM height is determined. */\n @Input() domLayout?: 'normal' | 'autoHeight' | 'print';\n\n /** Row selection mode: 'single', 'multiple', or false (disabled). */\n @Input() rowSelection?: 'single' | 'multiple' | false;\n\n /** When true, enables client-side pagination. */\n @Input() pagination?: boolean;\n\n /** ARIA label for the grid root element (screen reader accessibility). */\n @Input() ariaLabel?: string;\n\n // ── Outputs ──\n\n /** Emitted when the grid engine is fully initialized and the API is ready. */\n @Output() gridReady = new EventEmitter<GridApi>();\n\n /** Emitted when row data changes. */\n @Output() rowDataChanged = new EventEmitter<any>();\n\n /** Emitted when the selection state changes. */\n @Output() selectionChanged = new EventEmitter<any>();\n\n /** Emitted when the sort model changes. */\n @Output() sortChanged = new EventEmitter<any>();\n\n /** Emitted when the filter model changes. */\n @Output() filterChanged = new EventEmitter<any>();\n\n /** Emitted when a cell value is changed through editing. */\n @Output() cellValueChanged = new EventEmitter<any>();\n\n /** Emitted when a cell is clicked. */\n @Output() cellClicked = new EventEmitter<any>();\n\n /** Emitted when a cell is double-clicked. */\n @Output() cellDoubleClicked = new EventEmitter<any>();\n\n /** Emitted when a row is clicked. */\n @Output() rowClicked = new EventEmitter<any>();\n\n /** Emitted when pagination state changes. */\n @Output() paginationChanged = new EventEmitter<any>();\n\n /** Emitted when a column is resized. */\n @Output() columnResized = new EventEmitter<any>();\n\n // ── Template reference ──\n\n @ViewChild('gridContainer', { static: true })\n private gridContainerRef!: ElementRef<HTMLElement>;\n\n // ── Internal state ──\n\n private engine: GridEngine | null = null;\n private renderer: DomRenderer | null = null;\n private eventUnsubscribers: Array<() => void> = [];\n\n // ── Lifecycle ──\n\n ngOnInit(): void {\n this.initGrid();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (!this.engine) return;\n\n // Sync rowData changes to the engine\n if (changes['rowData'] && !changes['rowData'].firstChange) {\n this.engine.api.setRowData(this.rowData);\n }\n\n // Sync column definition changes to the engine\n if (changes['columns'] && !changes['columns'].firstChange) {\n this.engine.api.setColumnDefs(this.columns);\n }\n\n // Sync pagination page size\n if (changes['paginationPageSize'] && !changes['paginationPageSize'].firstChange) {\n if (this.paginationPageSize != null) {\n this.engine.api.setGridOption('paginationPageSize', this.paginationPageSize);\n }\n }\n }\n\n ngOnDestroy(): void {\n this.destroyGrid();\n }\n\n // ── Public API ──\n\n /**\n * Returns the underlying GridApi instance, or null if the grid\n * has not been initialized yet.\n */\n getApi(): GridApi | null {\n return this.engine?.api ?? null;\n }\n\n /**\n * Returns the underlying GridEngine instance, or null if the grid\n * has not been initialized yet.\n */\n getEngine(): GridEngine | null {\n return this.engine;\n }\n\n // ── Initialization ──\n\n private initGrid(): void {\n const container = this.gridContainerRef.nativeElement;\n if (!container) return;\n\n // Build core config from inputs\n const config: GridConfig = {\n columns: this.columns,\n rowData: this.rowData,\n plugins: this.plugins,\n getRowId: this.getRowId,\n rowHeight: this.rowHeight,\n headerHeight: this.headerHeight,\n defaultColDef: this.defaultColDef,\n domLayout: this.domLayout,\n rowSelection: this.rowSelection,\n pagination: this.pagination,\n paginationPageSize: this.paginationPageSize,\n ariaLabel: this.ariaLabel,\n theme: this.theme,\n };\n\n // Create the headless grid engine\n this.engine = createGrid(config);\n\n // Create and mount the DOM renderer\n this.renderer = new DomRenderer({\n container,\n engine: this.engine,\n });\n this.renderer.mount();\n\n // Wire up event bridge: core events -> Angular EventEmitters\n this.setupEventBridge();\n\n // Emit gridReady\n this.gridReady.emit(this.engine.api);\n }\n\n private setupEventBridge(): void {\n if (!this.engine) return;\n\n const eb = this.engine.eventBus;\n\n this.eventUnsubscribers = [\n eb.on('rowData:changed', (e) => this.rowDataChanged.emit(e)),\n eb.on('selection:changed', (e) => this.selectionChanged.emit(e)),\n eb.on('column:sort:changed', (e) => this.sortChanged.emit(e)),\n eb.on('filter:changed', (e) => this.filterChanged.emit(e)),\n eb.on('cell:valueChanged', (e) => this.cellValueChanged.emit(e)),\n eb.on('cell:clicked', (e) => this.cellClicked.emit(e)),\n eb.on('cell:doubleClicked', (e) => this.cellDoubleClicked.emit(e)),\n eb.on('row:clicked', (e) => this.rowClicked.emit(e)),\n eb.on('pagination:changed', (e) => this.paginationChanged.emit(e)),\n eb.on('column:resized', (e) => this.columnResized.emit(e)),\n ];\n }\n\n private destroyGrid(): void {\n // Unsubscribe from all core events\n for (const unsub of this.eventUnsubscribers) {\n unsub();\n }\n this.eventUnsubscribers = [];\n\n // Destroy the DOM renderer\n if (this.renderer) {\n this.renderer.destroy();\n this.renderer = null;\n }\n\n // Destroy the grid engine\n if (this.engine) {\n this.engine.destroy();\n this.engine = null;\n }\n }\n}\n","// ─── GridStorm Service ───\n// Injectable service for managing multiple GridStorm instances.\n// Allows Angular components to access grid APIs by a unique identifier,\n// useful in applications with multiple grids or cross-component communication.\n\nimport { Injectable } from '@angular/core';\nimport type { GridApi } from '@gridstorm/core';\n\n/**\n * Service for registering and retrieving GridStorm API instances.\n *\n * Use this service when you have multiple grids in your application\n * and need to access their APIs from different components or services.\n *\n * @example\n * ```typescript\n * // In a component that hosts the grid:\n * constructor(private gridService: GridStormService) {}\n *\n * onGridReady(api: GridApi) {\n * this.gridService.registerApi('employees', api);\n * }\n *\n * // In another component that needs to interact with the grid:\n * constructor(private gridService: GridStormService) {}\n *\n * exportData() {\n * const api = this.gridService.getApi('employees');\n * if (api) {\n * const rows = api.getSelectedRows();\n * // ... do something with rows\n * }\n * }\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class GridStormService {\n private apiMap = new Map<string, GridApi>();\n\n /**\n * Registers a GridApi instance under a unique identifier.\n *\n * Call this from the `(gridReady)` output handler of the `<gridstorm>` component.\n * If an API with the same ID already exists, it will be replaced.\n *\n * @param id - A unique string identifier for this grid instance.\n * @param api - The GridApi instance to register.\n */\n registerApi(id: string, api: GridApi): void {\n this.apiMap.set(id, api);\n }\n\n /**\n * Retrieves a previously registered GridApi by its identifier.\n *\n * @param id - The unique identifier used during registration.\n * @returns The GridApi instance, or `undefined` if not found.\n */\n getApi(id: string): GridApi | undefined {\n return this.apiMap.get(id);\n }\n\n /**\n * Removes a registered GridApi by its identifier.\n *\n * Call this when a grid component is destroyed to prevent memory leaks.\n *\n * @param id - The unique identifier of the API to remove.\n */\n removeApi(id: string): void {\n this.apiMap.delete(id);\n }\n\n /**\n * Returns all registered grid API identifiers.\n *\n * @returns An array of registered grid IDs.\n */\n getRegisteredIds(): string[] {\n return Array.from(this.apiMap.keys());\n }\n\n /**\n * Checks whether a grid API is registered under the given identifier.\n *\n * @param id - The unique identifier to check.\n * @returns `true` if an API is registered with that ID.\n */\n hasApi(id: string): boolean {\n return this.apiMap.has(id);\n }\n\n /**\n * Removes all registered grid APIs.\n *\n * Useful during application teardown or testing.\n */\n clear(): void {\n this.apiMap.clear();\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/gridstorm.component.ts","../src/gridstorm.service.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ─── GridStorm Angular Component ───\r\n// Standalone Angular component wrapping the headless core engine + DOM renderer.\r\n// Uses ElementRef + ViewChild to mount the DomRenderer into the template container.\r\n\r\nimport {\r\n Component,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ElementRef,\r\n OnInit,\r\n OnDestroy,\r\n OnChanges,\r\n SimpleChanges,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { createGrid } from '@gridstorm/core';\r\nimport { DomRenderer } from '@gridstorm/dom-renderer';\r\nimport type { GridConfig, GridApi, GridEngine, ColumnDef, GridPlugin } from '@gridstorm/core';\r\n\r\n@Component({\r\n selector: 'gridstorm',\r\n standalone: true,\r\n template: `\r\n <div\r\n #gridContainer\r\n class=\"gridstorm-wrapper\"\r\n [attr.data-theme]=\"theme\"\r\n [attr.data-density]=\"density\"\r\n style=\"width:100%;height:100%\"\r\n ></div>\r\n `,\r\n})\r\nexport class GridStormComponent implements OnInit, OnDestroy, OnChanges {\r\n // ── Inputs ──\r\n\r\n /** Column definitions describing each column's structure and behavior. */\r\n @Input() columns: ColumnDef[] = [];\r\n\r\n /** Client-side row data array. */\r\n @Input() rowData: any[] = [];\r\n\r\n /** Array of plugins to install during grid initialization. */\r\n @Input() plugins: GridPlugin[] = [];\r\n\r\n /** Callback to generate a unique string ID for each row. */\r\n @Input() getRowId?: (params: any) => string;\r\n\r\n /** Height of each data row in pixels. */\r\n @Input() rowHeight = 40;\r\n\r\n /** Theme identifier: 'light', 'dark', or custom theme name. */\r\n @Input() theme = 'light';\r\n\r\n /** Density mode: 'compact', 'normal', or 'comfortable'. */\r\n @Input() density = 'normal';\r\n\r\n /** Default column definition applied to all columns as fallback. */\r\n @Input() defaultColDef?: Partial<ColumnDef>;\r\n\r\n /** Number of rows per page when pagination is enabled. */\r\n @Input() paginationPageSize?: number;\r\n\r\n /** Height of the header row in pixels. */\r\n @Input() headerHeight?: number;\r\n\r\n /** Controls how the grid's DOM height is determined. */\r\n @Input() domLayout?: 'normal' | 'autoHeight' | 'print';\r\n\r\n /** Row selection mode: 'single', 'multiple', or false (disabled). */\r\n @Input() rowSelection?: 'single' | 'multiple' | false;\r\n\r\n /** When true, enables client-side pagination. */\r\n @Input() pagination?: boolean;\r\n\r\n /** ARIA label for the grid root element (screen reader accessibility). */\r\n @Input() ariaLabel?: string;\r\n\r\n // ── Outputs ──\r\n\r\n /** Emitted when the grid engine is fully initialized and the API is ready. */\r\n @Output() gridReady = new EventEmitter<GridApi>();\r\n\r\n /** Emitted when row data changes. */\r\n @Output() rowDataChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when the selection state changes. */\r\n @Output() selectionChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when the sort model changes. */\r\n @Output() sortChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when the filter model changes. */\r\n @Output() filterChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when a cell value is changed through editing. */\r\n @Output() cellValueChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when a cell is clicked. */\r\n @Output() cellClicked = new EventEmitter<any>();\r\n\r\n /** Emitted when a cell is double-clicked. */\r\n @Output() cellDoubleClicked = new EventEmitter<any>();\r\n\r\n /** Emitted when a row is clicked. */\r\n @Output() rowClicked = new EventEmitter<any>();\r\n\r\n /** Emitted when pagination state changes. */\r\n @Output() paginationChanged = new EventEmitter<any>();\r\n\r\n /** Emitted when a column is resized. */\r\n @Output() columnResized = new EventEmitter<any>();\r\n\r\n /** Emitted when an error occurs during grid initialization or operation. */\r\n @Output() gridError = new EventEmitter<Error>();\r\n\r\n // ── Template reference ──\r\n\r\n @ViewChild('gridContainer', { static: true })\r\n private gridContainerRef!: ElementRef<HTMLElement>;\r\n\r\n // ── Internal state ──\r\n\r\n private engine: GridEngine | null = null;\r\n private renderer: DomRenderer | null = null;\r\n private eventUnsubscribers: Array<() => void> = [];\r\n\r\n // ── Lifecycle ──\r\n\r\n ngOnInit(): void {\r\n try {\r\n this.initGrid();\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n console.error('[GridStorm Angular] Initialization error:', error);\r\n this.gridError.emit(error);\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (!this.engine) return;\r\n\r\n // Sync rowData changes to the engine\r\n if (changes['rowData'] && !changes['rowData'].firstChange) {\r\n this.engine.api.setRowData(this.rowData);\r\n }\r\n\r\n // Sync column definition changes to the engine\r\n if (changes['columns'] && !changes['columns'].firstChange) {\r\n this.engine.api.setColumnDefs(this.columns);\r\n }\r\n\r\n // Sync pagination page size\r\n if (changes['paginationPageSize'] && !changes['paginationPageSize'].firstChange) {\r\n if (this.paginationPageSize != null) {\r\n this.engine.api.setGridOption('paginationPageSize', this.paginationPageSize);\r\n }\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.destroyGrid();\r\n }\r\n\r\n // ── Public API ──\r\n\r\n /**\r\n * Returns the underlying GridApi instance, or null if the grid\r\n * has not been initialized yet.\r\n */\r\n getApi(): GridApi | null {\r\n return this.engine?.api ?? null;\r\n }\r\n\r\n /**\r\n * Returns the underlying GridEngine instance, or null if the grid\r\n * has not been initialized yet.\r\n */\r\n getEngine(): GridEngine | null {\r\n return this.engine;\r\n }\r\n\r\n // ── Initialization ──\r\n\r\n private initGrid(): void {\r\n const container = this.gridContainerRef.nativeElement;\r\n if (!container) return;\r\n\r\n // Build core config from inputs\r\n const config: GridConfig = {\r\n columns: this.columns,\r\n rowData: this.rowData,\r\n plugins: this.plugins,\r\n getRowId: this.getRowId,\r\n rowHeight: this.rowHeight,\r\n headerHeight: this.headerHeight,\r\n defaultColDef: this.defaultColDef,\r\n domLayout: this.domLayout,\r\n rowSelection: this.rowSelection,\r\n pagination: this.pagination,\r\n paginationPageSize: this.paginationPageSize,\r\n ariaLabel: this.ariaLabel,\r\n theme: this.theme,\r\n };\r\n\r\n // Create the headless grid engine\r\n this.engine = createGrid(config);\r\n\r\n // Create and mount the DOM renderer\r\n this.renderer = new DomRenderer({\r\n container,\r\n engine: this.engine,\r\n });\r\n this.renderer.mount();\r\n\r\n // Wire up event bridge: core events -> Angular EventEmitters\r\n this.setupEventBridge();\r\n\r\n // Emit gridReady\r\n this.gridReady.emit(this.engine.api);\r\n }\r\n\r\n private setupEventBridge(): void {\r\n if (!this.engine) return;\r\n\r\n const eb = this.engine.eventBus;\r\n\r\n this.eventUnsubscribers = [\r\n eb.on('rowData:changed', (e) => this.rowDataChanged.emit(e)),\r\n eb.on('selection:changed', (e) => this.selectionChanged.emit(e)),\r\n eb.on('column:sort:changed', (e) => this.sortChanged.emit(e)),\r\n eb.on('filter:changed', (e) => this.filterChanged.emit(e)),\r\n eb.on('cell:valueChanged', (e) => this.cellValueChanged.emit(e)),\r\n eb.on('cell:clicked', (e) => this.cellClicked.emit(e)),\r\n eb.on('cell:doubleClicked', (e) => this.cellDoubleClicked.emit(e)),\r\n eb.on('row:clicked', (e) => this.rowClicked.emit(e)),\r\n eb.on('pagination:changed', (e) => this.paginationChanged.emit(e)),\r\n eb.on('column:resized', (e) => this.columnResized.emit(e)),\r\n ];\r\n }\r\n\r\n private destroyGrid(): void {\r\n // Unsubscribe from all core events\r\n for (const unsub of this.eventUnsubscribers) {\r\n unsub();\r\n }\r\n this.eventUnsubscribers = [];\r\n\r\n // Destroy the DOM renderer\r\n if (this.renderer) {\r\n this.renderer.destroy();\r\n this.renderer = null;\r\n }\r\n\r\n // Destroy the grid engine\r\n if (this.engine) {\r\n this.engine.destroy();\r\n this.engine = null;\r\n }\r\n }\r\n}\r\n","// ─── GridStorm Service ───\r\n// Injectable service for managing multiple GridStorm instances.\r\n// Allows Angular components to access grid APIs by a unique identifier,\r\n// useful in applications with multiple grids or cross-component communication.\r\n\r\nimport { Injectable } from '@angular/core';\r\nimport type { GridApi } from '@gridstorm/core';\r\n\r\n/**\r\n * Service for registering and retrieving GridStorm API instances.\r\n *\r\n * Use this service when you have multiple grids in your application\r\n * and need to access their APIs from different components or services.\r\n *\r\n * @example\r\n * ```typescript\r\n * // In a component that hosts the grid:\r\n * constructor(private gridService: GridStormService) {}\r\n *\r\n * onGridReady(api: GridApi) {\r\n * this.gridService.registerApi('employees', api);\r\n * }\r\n *\r\n * // In another component that needs to interact with the grid:\r\n * constructor(private gridService: GridStormService) {}\r\n *\r\n * exportData() {\r\n * const api = this.gridService.getApi('employees');\r\n * if (api) {\r\n * const rows = api.getSelectedRows();\r\n * // ... do something with rows\r\n * }\r\n * }\r\n * ```\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class GridStormService {\r\n private apiMap = new Map<string, GridApi>();\r\n\r\n /**\r\n * Registers a GridApi instance under a unique identifier.\r\n *\r\n * Call this from the `(gridReady)` output handler of the `<gridstorm>` component.\r\n * If an API with the same ID already exists, it will be replaced.\r\n *\r\n * @param id - A unique string identifier for this grid instance.\r\n * @param api - The GridApi instance to register.\r\n */\r\n registerApi(id: string, api: GridApi): void {\r\n this.apiMap.set(id, api);\r\n }\r\n\r\n /**\r\n * Retrieves a previously registered GridApi by its identifier.\r\n *\r\n * @param id - The unique identifier used during registration.\r\n * @returns The GridApi instance, or `undefined` if not found.\r\n */\r\n getApi(id: string): GridApi | undefined {\r\n return this.apiMap.get(id);\r\n }\r\n\r\n /**\r\n * Removes a registered GridApi by its identifier.\r\n *\r\n * Call this when a grid component is destroyed to prevent memory leaks.\r\n *\r\n * @param id - The unique identifier of the API to remove.\r\n */\r\n removeApi(id: string): void {\r\n this.apiMap.delete(id);\r\n }\r\n\r\n /**\r\n * Returns all registered grid API identifiers.\r\n *\r\n * @returns An array of registered grid IDs.\r\n */\r\n getRegisteredIds(): string[] {\r\n return Array.from(this.apiMap.keys());\r\n }\r\n\r\n /**\r\n * Checks whether a grid API is registered under the given identifier.\r\n *\r\n * @param id - The unique identifier to check.\r\n * @returns `true` if an API is registered with that ID.\r\n */\r\n hasApi(id: string): boolean {\r\n return this.apiMap.has(id);\r\n }\r\n\r\n /**\r\n * Removes all registered grid APIs.\r\n *\r\n * Useful during application teardown or testing.\r\n */\r\n clear(): void {\r\n this.apiMap.clear();\r\n }\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridstorm/angular",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "GridStorm Angular adapter — standalone component for Angular 16+",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -35,12 +35,12 @@
35
35
  "@gridstorm/dom-renderer": "workspace:*"
36
36
  },
37
37
  "peerDependencies": {
38
- "@angular/core": ">=16.0.0",
39
- "@angular/common": ">=16.0.0"
38
+ "@angular/core": ">=21.2.6",
39
+ "@angular/common": ">=21.2.6"
40
40
  },
41
41
  "devDependencies": {
42
- "@angular/core": ">=16.0.0",
43
- "@angular/common": ">=16.0.0",
42
+ "@angular/core": ">=21.2.6",
43
+ "@angular/common": ">=21.2.6",
44
44
  "tsup": "^8.2.0",
45
45
  "typescript": "^5.5.0"
46
46
  },
@@ -65,10 +65,10 @@
65
65
  "bugs": {
66
66
  "url": "https://github.com/007krcs/grid-data/issues"
67
67
  },
68
- "homepage": "https://grid-data-analytics-explorer.vercel.app/",
68
+ "homepage": "https://gridstorm.tekivex.com",
69
69
  "author": {
70
70
  "name": "GridStorm",
71
- "url": "https://grid-data-analytics-explorer.vercel.app/"
71
+ "url": "https://gridstorm.tekivex.com/"
72
72
  },
73
73
  "funding": {
74
74
  "type": "github",
@@ -0,0 +1,379 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { GridStormComponent } from '../gridstorm.component';
3
+ import { GridStormService } from '../gridstorm.service';
4
+ import type { GridStormInputs, GridStormOutputs, GridRegistration } from '../types';
5
+
6
+ // ─── GridStormComponent Structure ───
7
+
8
+ describe('GridStormComponent', () => {
9
+ it('can be instantiated', () => {
10
+ const component = new GridStormComponent();
11
+ expect(component).toBeDefined();
12
+ expect(component).toBeInstanceOf(GridStormComponent);
13
+ });
14
+
15
+ it('has default values for columns and rowData inputs', () => {
16
+ const component = new GridStormComponent();
17
+ expect(component.columns).toEqual([]);
18
+ expect(component.rowData).toEqual([]);
19
+ });
20
+
21
+ it('has default values for plugins', () => {
22
+ const component = new GridStormComponent();
23
+ expect(component.plugins).toEqual([]);
24
+ });
25
+
26
+ it('has default rowHeight of 40', () => {
27
+ const component = new GridStormComponent();
28
+ expect(component.rowHeight).toBe(40);
29
+ });
30
+
31
+ it('has default theme of light', () => {
32
+ const component = new GridStormComponent();
33
+ expect(component.theme).toBe('light');
34
+ });
35
+
36
+ it('has default density of normal', () => {
37
+ const component = new GridStormComponent();
38
+ expect(component.density).toBe('normal');
39
+ });
40
+
41
+ it('has optional inputs that default to undefined', () => {
42
+ const component = new GridStormComponent();
43
+ expect(component.getRowId).toBeUndefined();
44
+ expect(component.defaultColDef).toBeUndefined();
45
+ expect(component.paginationPageSize).toBeUndefined();
46
+ expect(component.headerHeight).toBeUndefined();
47
+ expect(component.domLayout).toBeUndefined();
48
+ expect(component.rowSelection).toBeUndefined();
49
+ expect(component.pagination).toBeUndefined();
50
+ expect(component.ariaLabel).toBeUndefined();
51
+ });
52
+
53
+ it('allows setting input properties', () => {
54
+ const component = new GridStormComponent();
55
+ const testColumns = [{ field: 'name' }, { field: 'age' }];
56
+ const testData = [{ name: 'Alice', age: 30 }];
57
+
58
+ component.columns = testColumns as any;
59
+ component.rowData = testData;
60
+ component.rowHeight = 50;
61
+ component.theme = 'dark';
62
+ component.density = 'compact';
63
+
64
+ expect(component.columns).toBe(testColumns);
65
+ expect(component.rowData).toBe(testData);
66
+ expect(component.rowHeight).toBe(50);
67
+ expect(component.theme).toBe('dark');
68
+ expect(component.density).toBe('compact');
69
+ });
70
+
71
+ it('has EventEmitter outputs', () => {
72
+ const component = new GridStormComponent();
73
+ // All output EventEmitters should be defined and have an emit method
74
+ expect(component.gridReady).toBeDefined();
75
+ expect(typeof component.gridReady.emit).toBe('function');
76
+
77
+ expect(component.rowDataChanged).toBeDefined();
78
+ expect(typeof component.rowDataChanged.emit).toBe('function');
79
+
80
+ expect(component.selectionChanged).toBeDefined();
81
+ expect(typeof component.selectionChanged.emit).toBe('function');
82
+
83
+ expect(component.sortChanged).toBeDefined();
84
+ expect(typeof component.sortChanged.emit).toBe('function');
85
+
86
+ expect(component.filterChanged).toBeDefined();
87
+ expect(typeof component.filterChanged.emit).toBe('function');
88
+
89
+ expect(component.cellValueChanged).toBeDefined();
90
+ expect(typeof component.cellValueChanged.emit).toBe('function');
91
+
92
+ expect(component.cellClicked).toBeDefined();
93
+ expect(typeof component.cellClicked.emit).toBe('function');
94
+
95
+ expect(component.cellDoubleClicked).toBeDefined();
96
+ expect(typeof component.cellDoubleClicked.emit).toBe('function');
97
+
98
+ expect(component.rowClicked).toBeDefined();
99
+ expect(typeof component.rowClicked.emit).toBe('function');
100
+
101
+ expect(component.paginationChanged).toBeDefined();
102
+ expect(typeof component.paginationChanged.emit).toBe('function');
103
+
104
+ expect(component.columnResized).toBeDefined();
105
+ expect(typeof component.columnResized.emit).toBe('function');
106
+ });
107
+
108
+ it('has getApi method that returns null before initialization', () => {
109
+ const component = new GridStormComponent();
110
+ expect(component.getApi()).toBeNull();
111
+ });
112
+
113
+ it('has getEngine method that returns null before initialization', () => {
114
+ const component = new GridStormComponent();
115
+ expect(component.getEngine()).toBeNull();
116
+ });
117
+
118
+ it('has lifecycle methods defined', () => {
119
+ const component = new GridStormComponent();
120
+ expect(typeof component.ngOnInit).toBe('function');
121
+ expect(typeof component.ngOnDestroy).toBe('function');
122
+ expect(typeof component.ngOnChanges).toBe('function');
123
+ });
124
+
125
+ it('ngOnChanges is safe to call before engine is initialized', () => {
126
+ const component = new GridStormComponent();
127
+ // Should not throw when engine is null
128
+ expect(() => {
129
+ component.ngOnChanges({
130
+ rowData: {
131
+ currentValue: [{ id: 1 }],
132
+ previousValue: [],
133
+ firstChange: false,
134
+ isFirstChange: () => false,
135
+ },
136
+ });
137
+ }).not.toThrow();
138
+ });
139
+
140
+ it('ngOnChanges ignores first change events', () => {
141
+ const component = new GridStormComponent();
142
+ // Should not throw even with firstChange=true
143
+ expect(() => {
144
+ component.ngOnChanges({
145
+ rowData: {
146
+ currentValue: [{ id: 1 }],
147
+ previousValue: undefined,
148
+ firstChange: true,
149
+ isFirstChange: () => true,
150
+ },
151
+ });
152
+ }).not.toThrow();
153
+ });
154
+ });
155
+
156
+ // ─── GridStormService ───
157
+
158
+ describe('GridStormService', () => {
159
+ it('can be instantiated', () => {
160
+ const service = new GridStormService();
161
+ expect(service).toBeDefined();
162
+ expect(service).toBeInstanceOf(GridStormService);
163
+ });
164
+
165
+ it('starts with no registered APIs', () => {
166
+ const service = new GridStormService();
167
+ expect(service.getRegisteredIds()).toEqual([]);
168
+ });
169
+
170
+ it('registers and retrieves an API by id', () => {
171
+ const service = new GridStormService();
172
+ const mockApi = { getSelectedRows: () => [] } as any;
173
+
174
+ service.registerApi('employees', mockApi);
175
+ expect(service.getApi('employees')).toBe(mockApi);
176
+ });
177
+
178
+ it('returns undefined for unregistered ids', () => {
179
+ const service = new GridStormService();
180
+ expect(service.getApi('nonexistent')).toBeUndefined();
181
+ });
182
+
183
+ it('reports whether an API is registered via hasApi', () => {
184
+ const service = new GridStormService();
185
+ const mockApi = {} as any;
186
+
187
+ expect(service.hasApi('grid1')).toBe(false);
188
+ service.registerApi('grid1', mockApi);
189
+ expect(service.hasApi('grid1')).toBe(true);
190
+ });
191
+
192
+ it('removes a registered API', () => {
193
+ const service = new GridStormService();
194
+ const mockApi = {} as any;
195
+
196
+ service.registerApi('grid1', mockApi);
197
+ expect(service.hasApi('grid1')).toBe(true);
198
+
199
+ service.removeApi('grid1');
200
+ expect(service.hasApi('grid1')).toBe(false);
201
+ expect(service.getApi('grid1')).toBeUndefined();
202
+ });
203
+
204
+ it('removeApi is safe to call with unregistered id', () => {
205
+ const service = new GridStormService();
206
+ expect(() => service.removeApi('nonexistent')).not.toThrow();
207
+ });
208
+
209
+ it('lists all registered IDs', () => {
210
+ const service = new GridStormService();
211
+ const mockApi1 = {} as any;
212
+ const mockApi2 = {} as any;
213
+
214
+ service.registerApi('grid-a', mockApi1);
215
+ service.registerApi('grid-b', mockApi2);
216
+
217
+ const ids = service.getRegisteredIds();
218
+ expect(ids).toContain('grid-a');
219
+ expect(ids).toContain('grid-b');
220
+ expect(ids).toHaveLength(2);
221
+ });
222
+
223
+ it('replaces an existing API when registering with the same id', () => {
224
+ const service = new GridStormService();
225
+ const mockApi1 = { version: 1 } as any;
226
+ const mockApi2 = { version: 2 } as any;
227
+
228
+ service.registerApi('grid', mockApi1);
229
+ expect(service.getApi('grid')).toBe(mockApi1);
230
+
231
+ service.registerApi('grid', mockApi2);
232
+ expect(service.getApi('grid')).toBe(mockApi2);
233
+ // Should still only be one entry
234
+ expect(service.getRegisteredIds()).toHaveLength(1);
235
+ });
236
+
237
+ it('clears all registered APIs', () => {
238
+ const service = new GridStormService();
239
+ service.registerApi('a', {} as any);
240
+ service.registerApi('b', {} as any);
241
+ service.registerApi('c', {} as any);
242
+
243
+ expect(service.getRegisteredIds()).toHaveLength(3);
244
+
245
+ service.clear();
246
+ expect(service.getRegisteredIds()).toHaveLength(0);
247
+ expect(service.getApi('a')).toBeUndefined();
248
+ });
249
+
250
+ it('clear is safe to call when no APIs are registered', () => {
251
+ const service = new GridStormService();
252
+ expect(() => service.clear()).not.toThrow();
253
+ expect(service.getRegisteredIds()).toEqual([]);
254
+ });
255
+ });
256
+
257
+ // ─── Exported Types ───
258
+
259
+ describe('exported types', () => {
260
+ it('GridStormInputs type is usable', () => {
261
+ // Verify the type compiles and can be used in a type annotation
262
+ const inputs: GridStormInputs = {
263
+ columns: [],
264
+ rowData: [],
265
+ plugins: [],
266
+ rowHeight: 40,
267
+ theme: 'light',
268
+ density: 'normal',
269
+ };
270
+ expect(inputs.columns).toEqual([]);
271
+ expect(inputs.rowHeight).toBe(40);
272
+ expect(inputs.theme).toBe('light');
273
+ });
274
+
275
+ it('GridStormInputs supports optional properties', () => {
276
+ const inputs: GridStormInputs = {
277
+ columns: [],
278
+ rowData: [],
279
+ plugins: [],
280
+ rowHeight: 40,
281
+ theme: 'light',
282
+ density: 'normal',
283
+ pagination: true,
284
+ paginationPageSize: 25,
285
+ headerHeight: 48,
286
+ domLayout: 'autoHeight',
287
+ rowSelection: 'multiple',
288
+ ariaLabel: 'Data grid',
289
+ };
290
+ expect(inputs.pagination).toBe(true);
291
+ expect(inputs.paginationPageSize).toBe(25);
292
+ expect(inputs.domLayout).toBe('autoHeight');
293
+ expect(inputs.rowSelection).toBe('multiple');
294
+ });
295
+
296
+ it('GridStormOutputs type is usable', () => {
297
+ const outputs: Partial<GridStormOutputs> = {
298
+ rowDataChanged: { rowData: [{ id: 1 }] },
299
+ selectionChanged: { selectedNodes: [], source: 'api' },
300
+ sortChanged: { sortModel: [] },
301
+ filterChanged: { filterModel: {} },
302
+ cellValueChanged: { node: null, colId: 'name', oldValue: 'a', newValue: 'b' },
303
+ };
304
+ expect(outputs.rowDataChanged?.rowData).toHaveLength(1);
305
+ expect(outputs.selectionChanged?.source).toBe('api');
306
+ });
307
+
308
+ it('GridRegistration type is usable', () => {
309
+ const reg: GridRegistration = {
310
+ id: 'my-grid',
311
+ api: {} as any,
312
+ };
313
+ expect(reg.id).toBe('my-grid');
314
+ expect(reg.api).toBeDefined();
315
+ });
316
+ });
317
+
318
+ // ─── Event Emission Mapping ───
319
+
320
+ describe('event emission mapping', () => {
321
+ it('component has all expected event outputs for the documented core events', () => {
322
+ const component = new GridStormComponent();
323
+ // These correspond to the core event bus events bridged in setupEventBridge
324
+ const expectedOutputs = [
325
+ 'gridReady',
326
+ 'rowDataChanged',
327
+ 'selectionChanged',
328
+ 'sortChanged',
329
+ 'filterChanged',
330
+ 'cellValueChanged',
331
+ 'cellClicked',
332
+ 'cellDoubleClicked',
333
+ 'rowClicked',
334
+ 'paginationChanged',
335
+ 'columnResized',
336
+ ];
337
+
338
+ for (const outputName of expectedOutputs) {
339
+ const emitter = (component as any)[outputName];
340
+ expect(emitter).toBeDefined();
341
+ expect(typeof emitter.emit).toBe('function');
342
+ expect(typeof emitter.subscribe).toBe('function');
343
+ }
344
+ });
345
+
346
+ it('EventEmitters can be subscribed to and receive values', () => {
347
+ const component = new GridStormComponent();
348
+ const received: any[] = [];
349
+
350
+ component.cellClicked.subscribe((val: any) => received.push(val));
351
+ component.cellClicked.emit({ rowIndex: 0, colId: 'name' });
352
+
353
+ expect(received).toHaveLength(1);
354
+ expect(received[0]).toEqual({ rowIndex: 0, colId: 'name' });
355
+ });
356
+ });
357
+
358
+ // ─── Cleanup on Destroy ───
359
+
360
+ describe('cleanup on destroy', () => {
361
+ it('ngOnDestroy does not throw when called before initialization', () => {
362
+ const component = new GridStormComponent();
363
+ // Engine and renderer are null, should handle gracefully
364
+ expect(() => component.ngOnDestroy()).not.toThrow();
365
+ });
366
+
367
+ it('getApi returns null after destroy is called', () => {
368
+ const component = new GridStormComponent();
369
+ // Even without initialization, calling destroy then getApi should be safe
370
+ component.ngOnDestroy();
371
+ expect(component.getApi()).toBeNull();
372
+ });
373
+
374
+ it('getEngine returns null after destroy is called', () => {
375
+ const component = new GridStormComponent();
376
+ component.ngOnDestroy();
377
+ expect(component.getEngine()).toBeNull();
378
+ });
379
+ });
@@ -111,6 +111,9 @@ export class GridStormComponent implements OnInit, OnDestroy, OnChanges {
111
111
  /** Emitted when a column is resized. */
112
112
  @Output() columnResized = new EventEmitter<any>();
113
113
 
114
+ /** Emitted when an error occurs during grid initialization or operation. */
115
+ @Output() gridError = new EventEmitter<Error>();
116
+
114
117
  // ── Template reference ──
115
118
 
116
119
  @ViewChild('gridContainer', { static: true })
@@ -125,7 +128,13 @@ export class GridStormComponent implements OnInit, OnDestroy, OnChanges {
125
128
  // ── Lifecycle ──
126
129
 
127
130
  ngOnInit(): void {
128
- this.initGrid();
131
+ try {
132
+ this.initGrid();
133
+ } catch (err) {
134
+ const error = err instanceof Error ? err : new Error(String(err));
135
+ console.error('[GridStorm Angular] Initialization error:', error);
136
+ this.gridError.emit(error);
137
+ }
129
138
  }
130
139
 
131
140
  ngOnChanges(changes: SimpleChanges): void {