@gridstorm/angular 0.1.2
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 +203 -0
- package/dist/index.cjs +294 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +237 -0
- package/dist/index.d.ts +237 -0
- package/dist/index.js +294 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
- package/src/gridstorm.component.ts +252 -0
- package/src/gridstorm.service.ts +101 -0
- package/src/index.ts +23 -0
- package/src/types.ts +94 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { Input, Output, ViewChild, Component, Injectable, EventEmitter } from '@angular/core';
|
|
2
|
+
import { createGrid } from '@gridstorm/core';
|
|
3
|
+
import { DomRenderer } from '@gridstorm/dom-renderer';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
8
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
9
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
10
|
+
if (decorator = decorators[i])
|
|
11
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
12
|
+
if (kind && result) __defProp(target, key, result);
|
|
13
|
+
return result;
|
|
14
|
+
};
|
|
15
|
+
var GridStormComponent = class {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.columns = [];
|
|
18
|
+
this.rowData = [];
|
|
19
|
+
this.plugins = [];
|
|
20
|
+
this.rowHeight = 40;
|
|
21
|
+
this.theme = "light";
|
|
22
|
+
this.density = "normal";
|
|
23
|
+
this.gridReady = new EventEmitter();
|
|
24
|
+
this.rowDataChanged = new EventEmitter();
|
|
25
|
+
this.selectionChanged = new EventEmitter();
|
|
26
|
+
this.sortChanged = new EventEmitter();
|
|
27
|
+
this.filterChanged = new EventEmitter();
|
|
28
|
+
this.cellValueChanged = new EventEmitter();
|
|
29
|
+
this.cellClicked = new EventEmitter();
|
|
30
|
+
this.cellDoubleClicked = new EventEmitter();
|
|
31
|
+
this.rowClicked = new EventEmitter();
|
|
32
|
+
this.paginationChanged = new EventEmitter();
|
|
33
|
+
this.columnResized = new EventEmitter();
|
|
34
|
+
// ── Internal state ──
|
|
35
|
+
this.engine = null;
|
|
36
|
+
this.renderer = null;
|
|
37
|
+
this.eventUnsubscribers = [];
|
|
38
|
+
}
|
|
39
|
+
// ── Lifecycle ──
|
|
40
|
+
ngOnInit() {
|
|
41
|
+
this.initGrid();
|
|
42
|
+
}
|
|
43
|
+
ngOnChanges(changes) {
|
|
44
|
+
if (!this.engine) return;
|
|
45
|
+
if (changes["rowData"] && !changes["rowData"].firstChange) {
|
|
46
|
+
this.engine.api.setRowData(this.rowData);
|
|
47
|
+
}
|
|
48
|
+
if (changes["columns"] && !changes["columns"].firstChange) {
|
|
49
|
+
this.engine.api.setColumnDefs(this.columns);
|
|
50
|
+
}
|
|
51
|
+
if (changes["paginationPageSize"] && !changes["paginationPageSize"].firstChange) {
|
|
52
|
+
if (this.paginationPageSize != null) {
|
|
53
|
+
this.engine.api.setGridOption("paginationPageSize", this.paginationPageSize);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
ngOnDestroy() {
|
|
58
|
+
this.destroyGrid();
|
|
59
|
+
}
|
|
60
|
+
// ── Public API ──
|
|
61
|
+
/**
|
|
62
|
+
* Returns the underlying GridApi instance, or null if the grid
|
|
63
|
+
* has not been initialized yet.
|
|
64
|
+
*/
|
|
65
|
+
getApi() {
|
|
66
|
+
return this.engine?.api ?? null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns the underlying GridEngine instance, or null if the grid
|
|
70
|
+
* has not been initialized yet.
|
|
71
|
+
*/
|
|
72
|
+
getEngine() {
|
|
73
|
+
return this.engine;
|
|
74
|
+
}
|
|
75
|
+
// ── Initialization ──
|
|
76
|
+
initGrid() {
|
|
77
|
+
const container = this.gridContainerRef.nativeElement;
|
|
78
|
+
if (!container) return;
|
|
79
|
+
const config = {
|
|
80
|
+
columns: this.columns,
|
|
81
|
+
rowData: this.rowData,
|
|
82
|
+
plugins: this.plugins,
|
|
83
|
+
getRowId: this.getRowId,
|
|
84
|
+
rowHeight: this.rowHeight,
|
|
85
|
+
headerHeight: this.headerHeight,
|
|
86
|
+
defaultColDef: this.defaultColDef,
|
|
87
|
+
domLayout: this.domLayout,
|
|
88
|
+
rowSelection: this.rowSelection,
|
|
89
|
+
pagination: this.pagination,
|
|
90
|
+
paginationPageSize: this.paginationPageSize,
|
|
91
|
+
ariaLabel: this.ariaLabel,
|
|
92
|
+
theme: this.theme
|
|
93
|
+
};
|
|
94
|
+
this.engine = createGrid(config);
|
|
95
|
+
this.renderer = new DomRenderer({
|
|
96
|
+
container,
|
|
97
|
+
engine: this.engine
|
|
98
|
+
});
|
|
99
|
+
this.renderer.mount();
|
|
100
|
+
this.setupEventBridge();
|
|
101
|
+
this.gridReady.emit(this.engine.api);
|
|
102
|
+
}
|
|
103
|
+
setupEventBridge() {
|
|
104
|
+
if (!this.engine) return;
|
|
105
|
+
const eb = this.engine.eventBus;
|
|
106
|
+
this.eventUnsubscribers = [
|
|
107
|
+
eb.on("rowData:changed", (e) => this.rowDataChanged.emit(e)),
|
|
108
|
+
eb.on("selection:changed", (e) => this.selectionChanged.emit(e)),
|
|
109
|
+
eb.on("column:sort:changed", (e) => this.sortChanged.emit(e)),
|
|
110
|
+
eb.on("filter:changed", (e) => this.filterChanged.emit(e)),
|
|
111
|
+
eb.on("cell:valueChanged", (e) => this.cellValueChanged.emit(e)),
|
|
112
|
+
eb.on("cell:clicked", (e) => this.cellClicked.emit(e)),
|
|
113
|
+
eb.on("cell:doubleClicked", (e) => this.cellDoubleClicked.emit(e)),
|
|
114
|
+
eb.on("row:clicked", (e) => this.rowClicked.emit(e)),
|
|
115
|
+
eb.on("pagination:changed", (e) => this.paginationChanged.emit(e)),
|
|
116
|
+
eb.on("column:resized", (e) => this.columnResized.emit(e))
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
destroyGrid() {
|
|
120
|
+
for (const unsub of this.eventUnsubscribers) {
|
|
121
|
+
unsub();
|
|
122
|
+
}
|
|
123
|
+
this.eventUnsubscribers = [];
|
|
124
|
+
if (this.renderer) {
|
|
125
|
+
this.renderer.destroy();
|
|
126
|
+
this.renderer = null;
|
|
127
|
+
}
|
|
128
|
+
if (this.engine) {
|
|
129
|
+
this.engine.destroy();
|
|
130
|
+
this.engine = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
__decorateClass([
|
|
135
|
+
Input()
|
|
136
|
+
], GridStormComponent.prototype, "columns", 2);
|
|
137
|
+
__decorateClass([
|
|
138
|
+
Input()
|
|
139
|
+
], GridStormComponent.prototype, "rowData", 2);
|
|
140
|
+
__decorateClass([
|
|
141
|
+
Input()
|
|
142
|
+
], GridStormComponent.prototype, "plugins", 2);
|
|
143
|
+
__decorateClass([
|
|
144
|
+
Input()
|
|
145
|
+
], GridStormComponent.prototype, "getRowId", 2);
|
|
146
|
+
__decorateClass([
|
|
147
|
+
Input()
|
|
148
|
+
], GridStormComponent.prototype, "rowHeight", 2);
|
|
149
|
+
__decorateClass([
|
|
150
|
+
Input()
|
|
151
|
+
], GridStormComponent.prototype, "theme", 2);
|
|
152
|
+
__decorateClass([
|
|
153
|
+
Input()
|
|
154
|
+
], GridStormComponent.prototype, "density", 2);
|
|
155
|
+
__decorateClass([
|
|
156
|
+
Input()
|
|
157
|
+
], GridStormComponent.prototype, "defaultColDef", 2);
|
|
158
|
+
__decorateClass([
|
|
159
|
+
Input()
|
|
160
|
+
], GridStormComponent.prototype, "paginationPageSize", 2);
|
|
161
|
+
__decorateClass([
|
|
162
|
+
Input()
|
|
163
|
+
], GridStormComponent.prototype, "headerHeight", 2);
|
|
164
|
+
__decorateClass([
|
|
165
|
+
Input()
|
|
166
|
+
], GridStormComponent.prototype, "domLayout", 2);
|
|
167
|
+
__decorateClass([
|
|
168
|
+
Input()
|
|
169
|
+
], GridStormComponent.prototype, "rowSelection", 2);
|
|
170
|
+
__decorateClass([
|
|
171
|
+
Input()
|
|
172
|
+
], GridStormComponent.prototype, "pagination", 2);
|
|
173
|
+
__decorateClass([
|
|
174
|
+
Input()
|
|
175
|
+
], GridStormComponent.prototype, "ariaLabel", 2);
|
|
176
|
+
__decorateClass([
|
|
177
|
+
Output()
|
|
178
|
+
], GridStormComponent.prototype, "gridReady", 2);
|
|
179
|
+
__decorateClass([
|
|
180
|
+
Output()
|
|
181
|
+
], GridStormComponent.prototype, "rowDataChanged", 2);
|
|
182
|
+
__decorateClass([
|
|
183
|
+
Output()
|
|
184
|
+
], GridStormComponent.prototype, "selectionChanged", 2);
|
|
185
|
+
__decorateClass([
|
|
186
|
+
Output()
|
|
187
|
+
], GridStormComponent.prototype, "sortChanged", 2);
|
|
188
|
+
__decorateClass([
|
|
189
|
+
Output()
|
|
190
|
+
], GridStormComponent.prototype, "filterChanged", 2);
|
|
191
|
+
__decorateClass([
|
|
192
|
+
Output()
|
|
193
|
+
], GridStormComponent.prototype, "cellValueChanged", 2);
|
|
194
|
+
__decorateClass([
|
|
195
|
+
Output()
|
|
196
|
+
], GridStormComponent.prototype, "cellClicked", 2);
|
|
197
|
+
__decorateClass([
|
|
198
|
+
Output()
|
|
199
|
+
], GridStormComponent.prototype, "cellDoubleClicked", 2);
|
|
200
|
+
__decorateClass([
|
|
201
|
+
Output()
|
|
202
|
+
], GridStormComponent.prototype, "rowClicked", 2);
|
|
203
|
+
__decorateClass([
|
|
204
|
+
Output()
|
|
205
|
+
], GridStormComponent.prototype, "paginationChanged", 2);
|
|
206
|
+
__decorateClass([
|
|
207
|
+
Output()
|
|
208
|
+
], GridStormComponent.prototype, "columnResized", 2);
|
|
209
|
+
__decorateClass([
|
|
210
|
+
ViewChild("gridContainer", { static: true })
|
|
211
|
+
], GridStormComponent.prototype, "gridContainerRef", 2);
|
|
212
|
+
GridStormComponent = __decorateClass([
|
|
213
|
+
Component({
|
|
214
|
+
selector: "gridstorm",
|
|
215
|
+
standalone: true,
|
|
216
|
+
template: `
|
|
217
|
+
<div
|
|
218
|
+
#gridContainer
|
|
219
|
+
class="gridstorm-wrapper"
|
|
220
|
+
[attr.data-theme]="theme"
|
|
221
|
+
[attr.data-density]="density"
|
|
222
|
+
style="width:100%;height:100%"
|
|
223
|
+
></div>
|
|
224
|
+
`
|
|
225
|
+
})
|
|
226
|
+
], GridStormComponent);
|
|
227
|
+
var GridStormService = class {
|
|
228
|
+
constructor() {
|
|
229
|
+
this.apiMap = /* @__PURE__ */ new Map();
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Registers a GridApi instance under a unique identifier.
|
|
233
|
+
*
|
|
234
|
+
* Call this from the `(gridReady)` output handler of the `<gridstorm>` component.
|
|
235
|
+
* If an API with the same ID already exists, it will be replaced.
|
|
236
|
+
*
|
|
237
|
+
* @param id - A unique string identifier for this grid instance.
|
|
238
|
+
* @param api - The GridApi instance to register.
|
|
239
|
+
*/
|
|
240
|
+
registerApi(id, api) {
|
|
241
|
+
this.apiMap.set(id, api);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Retrieves a previously registered GridApi by its identifier.
|
|
245
|
+
*
|
|
246
|
+
* @param id - The unique identifier used during registration.
|
|
247
|
+
* @returns The GridApi instance, or `undefined` if not found.
|
|
248
|
+
*/
|
|
249
|
+
getApi(id) {
|
|
250
|
+
return this.apiMap.get(id);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Removes a registered GridApi by its identifier.
|
|
254
|
+
*
|
|
255
|
+
* Call this when a grid component is destroyed to prevent memory leaks.
|
|
256
|
+
*
|
|
257
|
+
* @param id - The unique identifier of the API to remove.
|
|
258
|
+
*/
|
|
259
|
+
removeApi(id) {
|
|
260
|
+
this.apiMap.delete(id);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Returns all registered grid API identifiers.
|
|
264
|
+
*
|
|
265
|
+
* @returns An array of registered grid IDs.
|
|
266
|
+
*/
|
|
267
|
+
getRegisteredIds() {
|
|
268
|
+
return Array.from(this.apiMap.keys());
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Checks whether a grid API is registered under the given identifier.
|
|
272
|
+
*
|
|
273
|
+
* @param id - The unique identifier to check.
|
|
274
|
+
* @returns `true` if an API is registered with that ID.
|
|
275
|
+
*/
|
|
276
|
+
hasApi(id) {
|
|
277
|
+
return this.apiMap.has(id);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Removes all registered grid APIs.
|
|
281
|
+
*
|
|
282
|
+
* Useful during application teardown or testing.
|
|
283
|
+
*/
|
|
284
|
+
clear() {
|
|
285
|
+
this.apiMap.clear();
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
GridStormService = __decorateClass([
|
|
289
|
+
Injectable({ providedIn: "root" })
|
|
290
|
+
], GridStormService);
|
|
291
|
+
|
|
292
|
+
export { GridStormComponent, GridStormService };
|
|
293
|
+
//# sourceMappingURL=index.js.map
|
|
294
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gridstorm/angular",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "GridStorm Angular adapter — standalone component for Angular 16+",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"source": "./src/index.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src",
|
|
25
|
+
"LICENSE.md"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"clean": "rm -rf dist"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@gridstorm/core": "workspace:*",
|
|
35
|
+
"@gridstorm/dom-renderer": "workspace:*"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@angular/core": ">=16.0.0",
|
|
39
|
+
"@angular/common": ">=16.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@angular/core": ">=16.0.0",
|
|
43
|
+
"@angular/common": ">=16.0.0",
|
|
44
|
+
"tsup": "^8.2.0",
|
|
45
|
+
"typescript": "^5.5.0"
|
|
46
|
+
},
|
|
47
|
+
"sideEffects": false,
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"keywords": [
|
|
53
|
+
"datagrid",
|
|
54
|
+
"angular",
|
|
55
|
+
"angular-grid",
|
|
56
|
+
"angular-table",
|
|
57
|
+
"gridstorm",
|
|
58
|
+
"standalone-component"
|
|
59
|
+
],
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/007krcs/grid-data.git",
|
|
63
|
+
"directory": "packages/angular-adapter"
|
|
64
|
+
},
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/007krcs/grid-data/issues"
|
|
67
|
+
},
|
|
68
|
+
"homepage": "https://grid-data-analytics-explorer.vercel.app/",
|
|
69
|
+
"author": {
|
|
70
|
+
"name": "GridStorm",
|
|
71
|
+
"url": "https://grid-data-analytics-explorer.vercel.app/"
|
|
72
|
+
},
|
|
73
|
+
"funding": {
|
|
74
|
+
"type": "github",
|
|
75
|
+
"url": "https://github.com/sponsors/gridstorm"
|
|
76
|
+
},
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=18.0.0"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// ─── GridStorm Angular Component ───
|
|
2
|
+
// Standalone Angular component wrapping the headless core engine + DOM renderer.
|
|
3
|
+
// Uses ElementRef + ViewChild to mount the DomRenderer into the template container.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Component,
|
|
7
|
+
Input,
|
|
8
|
+
Output,
|
|
9
|
+
EventEmitter,
|
|
10
|
+
ElementRef,
|
|
11
|
+
OnInit,
|
|
12
|
+
OnDestroy,
|
|
13
|
+
OnChanges,
|
|
14
|
+
SimpleChanges,
|
|
15
|
+
ViewChild,
|
|
16
|
+
} from '@angular/core';
|
|
17
|
+
import { createGrid } from '@gridstorm/core';
|
|
18
|
+
import { DomRenderer } from '@gridstorm/dom-renderer';
|
|
19
|
+
import type { GridConfig, GridApi, GridEngine, ColumnDef, GridPlugin } from '@gridstorm/core';
|
|
20
|
+
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'gridstorm',
|
|
23
|
+
standalone: true,
|
|
24
|
+
template: `
|
|
25
|
+
<div
|
|
26
|
+
#gridContainer
|
|
27
|
+
class="gridstorm-wrapper"
|
|
28
|
+
[attr.data-theme]="theme"
|
|
29
|
+
[attr.data-density]="density"
|
|
30
|
+
style="width:100%;height:100%"
|
|
31
|
+
></div>
|
|
32
|
+
`,
|
|
33
|
+
})
|
|
34
|
+
export class GridStormComponent implements OnInit, OnDestroy, OnChanges {
|
|
35
|
+
// ── Inputs ──
|
|
36
|
+
|
|
37
|
+
/** Column definitions describing each column's structure and behavior. */
|
|
38
|
+
@Input() columns: ColumnDef[] = [];
|
|
39
|
+
|
|
40
|
+
/** Client-side row data array. */
|
|
41
|
+
@Input() rowData: any[] = [];
|
|
42
|
+
|
|
43
|
+
/** Array of plugins to install during grid initialization. */
|
|
44
|
+
@Input() plugins: GridPlugin[] = [];
|
|
45
|
+
|
|
46
|
+
/** Callback to generate a unique string ID for each row. */
|
|
47
|
+
@Input() getRowId?: (params: any) => string;
|
|
48
|
+
|
|
49
|
+
/** Height of each data row in pixels. */
|
|
50
|
+
@Input() rowHeight = 40;
|
|
51
|
+
|
|
52
|
+
/** Theme identifier: 'light', 'dark', or custom theme name. */
|
|
53
|
+
@Input() theme = 'light';
|
|
54
|
+
|
|
55
|
+
/** Density mode: 'compact', 'normal', or 'comfortable'. */
|
|
56
|
+
@Input() density = 'normal';
|
|
57
|
+
|
|
58
|
+
/** Default column definition applied to all columns as fallback. */
|
|
59
|
+
@Input() defaultColDef?: Partial<ColumnDef>;
|
|
60
|
+
|
|
61
|
+
/** Number of rows per page when pagination is enabled. */
|
|
62
|
+
@Input() paginationPageSize?: number;
|
|
63
|
+
|
|
64
|
+
/** Height of the header row in pixels. */
|
|
65
|
+
@Input() headerHeight?: number;
|
|
66
|
+
|
|
67
|
+
/** Controls how the grid's DOM height is determined. */
|
|
68
|
+
@Input() domLayout?: 'normal' | 'autoHeight' | 'print';
|
|
69
|
+
|
|
70
|
+
/** Row selection mode: 'single', 'multiple', or false (disabled). */
|
|
71
|
+
@Input() rowSelection?: 'single' | 'multiple' | false;
|
|
72
|
+
|
|
73
|
+
/** When true, enables client-side pagination. */
|
|
74
|
+
@Input() pagination?: boolean;
|
|
75
|
+
|
|
76
|
+
/** ARIA label for the grid root element (screen reader accessibility). */
|
|
77
|
+
@Input() ariaLabel?: string;
|
|
78
|
+
|
|
79
|
+
// ── Outputs ──
|
|
80
|
+
|
|
81
|
+
/** Emitted when the grid engine is fully initialized and the API is ready. */
|
|
82
|
+
@Output() gridReady = new EventEmitter<GridApi>();
|
|
83
|
+
|
|
84
|
+
/** Emitted when row data changes. */
|
|
85
|
+
@Output() rowDataChanged = new EventEmitter<any>();
|
|
86
|
+
|
|
87
|
+
/** Emitted when the selection state changes. */
|
|
88
|
+
@Output() selectionChanged = new EventEmitter<any>();
|
|
89
|
+
|
|
90
|
+
/** Emitted when the sort model changes. */
|
|
91
|
+
@Output() sortChanged = new EventEmitter<any>();
|
|
92
|
+
|
|
93
|
+
/** Emitted when the filter model changes. */
|
|
94
|
+
@Output() filterChanged = new EventEmitter<any>();
|
|
95
|
+
|
|
96
|
+
/** Emitted when a cell value is changed through editing. */
|
|
97
|
+
@Output() cellValueChanged = new EventEmitter<any>();
|
|
98
|
+
|
|
99
|
+
/** Emitted when a cell is clicked. */
|
|
100
|
+
@Output() cellClicked = new EventEmitter<any>();
|
|
101
|
+
|
|
102
|
+
/** Emitted when a cell is double-clicked. */
|
|
103
|
+
@Output() cellDoubleClicked = new EventEmitter<any>();
|
|
104
|
+
|
|
105
|
+
/** Emitted when a row is clicked. */
|
|
106
|
+
@Output() rowClicked = new EventEmitter<any>();
|
|
107
|
+
|
|
108
|
+
/** Emitted when pagination state changes. */
|
|
109
|
+
@Output() paginationChanged = new EventEmitter<any>();
|
|
110
|
+
|
|
111
|
+
/** Emitted when a column is resized. */
|
|
112
|
+
@Output() columnResized = new EventEmitter<any>();
|
|
113
|
+
|
|
114
|
+
// ── Template reference ──
|
|
115
|
+
|
|
116
|
+
@ViewChild('gridContainer', { static: true })
|
|
117
|
+
private gridContainerRef!: ElementRef<HTMLElement>;
|
|
118
|
+
|
|
119
|
+
// ── Internal state ──
|
|
120
|
+
|
|
121
|
+
private engine: GridEngine | null = null;
|
|
122
|
+
private renderer: DomRenderer | null = null;
|
|
123
|
+
private eventUnsubscribers: Array<() => void> = [];
|
|
124
|
+
|
|
125
|
+
// ── Lifecycle ──
|
|
126
|
+
|
|
127
|
+
ngOnInit(): void {
|
|
128
|
+
this.initGrid();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
132
|
+
if (!this.engine) return;
|
|
133
|
+
|
|
134
|
+
// Sync rowData changes to the engine
|
|
135
|
+
if (changes['rowData'] && !changes['rowData'].firstChange) {
|
|
136
|
+
this.engine.api.setRowData(this.rowData);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Sync column definition changes to the engine
|
|
140
|
+
if (changes['columns'] && !changes['columns'].firstChange) {
|
|
141
|
+
this.engine.api.setColumnDefs(this.columns);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Sync pagination page size
|
|
145
|
+
if (changes['paginationPageSize'] && !changes['paginationPageSize'].firstChange) {
|
|
146
|
+
if (this.paginationPageSize != null) {
|
|
147
|
+
this.engine.api.setGridOption('paginationPageSize', this.paginationPageSize);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
ngOnDestroy(): void {
|
|
153
|
+
this.destroyGrid();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Public API ──
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Returns the underlying GridApi instance, or null if the grid
|
|
160
|
+
* has not been initialized yet.
|
|
161
|
+
*/
|
|
162
|
+
getApi(): GridApi | null {
|
|
163
|
+
return this.engine?.api ?? null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns the underlying GridEngine instance, or null if the grid
|
|
168
|
+
* has not been initialized yet.
|
|
169
|
+
*/
|
|
170
|
+
getEngine(): GridEngine | null {
|
|
171
|
+
return this.engine;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Initialization ──
|
|
175
|
+
|
|
176
|
+
private initGrid(): void {
|
|
177
|
+
const container = this.gridContainerRef.nativeElement;
|
|
178
|
+
if (!container) return;
|
|
179
|
+
|
|
180
|
+
// Build core config from inputs
|
|
181
|
+
const config: GridConfig = {
|
|
182
|
+
columns: this.columns,
|
|
183
|
+
rowData: this.rowData,
|
|
184
|
+
plugins: this.plugins,
|
|
185
|
+
getRowId: this.getRowId,
|
|
186
|
+
rowHeight: this.rowHeight,
|
|
187
|
+
headerHeight: this.headerHeight,
|
|
188
|
+
defaultColDef: this.defaultColDef,
|
|
189
|
+
domLayout: this.domLayout,
|
|
190
|
+
rowSelection: this.rowSelection,
|
|
191
|
+
pagination: this.pagination,
|
|
192
|
+
paginationPageSize: this.paginationPageSize,
|
|
193
|
+
ariaLabel: this.ariaLabel,
|
|
194
|
+
theme: this.theme,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Create the headless grid engine
|
|
198
|
+
this.engine = createGrid(config);
|
|
199
|
+
|
|
200
|
+
// Create and mount the DOM renderer
|
|
201
|
+
this.renderer = new DomRenderer({
|
|
202
|
+
container,
|
|
203
|
+
engine: this.engine,
|
|
204
|
+
});
|
|
205
|
+
this.renderer.mount();
|
|
206
|
+
|
|
207
|
+
// Wire up event bridge: core events -> Angular EventEmitters
|
|
208
|
+
this.setupEventBridge();
|
|
209
|
+
|
|
210
|
+
// Emit gridReady
|
|
211
|
+
this.gridReady.emit(this.engine.api);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private setupEventBridge(): void {
|
|
215
|
+
if (!this.engine) return;
|
|
216
|
+
|
|
217
|
+
const eb = this.engine.eventBus;
|
|
218
|
+
|
|
219
|
+
this.eventUnsubscribers = [
|
|
220
|
+
eb.on('rowData:changed', (e) => this.rowDataChanged.emit(e)),
|
|
221
|
+
eb.on('selection:changed', (e) => this.selectionChanged.emit(e)),
|
|
222
|
+
eb.on('column:sort:changed', (e) => this.sortChanged.emit(e)),
|
|
223
|
+
eb.on('filter:changed', (e) => this.filterChanged.emit(e)),
|
|
224
|
+
eb.on('cell:valueChanged', (e) => this.cellValueChanged.emit(e)),
|
|
225
|
+
eb.on('cell:clicked', (e) => this.cellClicked.emit(e)),
|
|
226
|
+
eb.on('cell:doubleClicked', (e) => this.cellDoubleClicked.emit(e)),
|
|
227
|
+
eb.on('row:clicked', (e) => this.rowClicked.emit(e)),
|
|
228
|
+
eb.on('pagination:changed', (e) => this.paginationChanged.emit(e)),
|
|
229
|
+
eb.on('column:resized', (e) => this.columnResized.emit(e)),
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private destroyGrid(): void {
|
|
234
|
+
// Unsubscribe from all core events
|
|
235
|
+
for (const unsub of this.eventUnsubscribers) {
|
|
236
|
+
unsub();
|
|
237
|
+
}
|
|
238
|
+
this.eventUnsubscribers = [];
|
|
239
|
+
|
|
240
|
+
// Destroy the DOM renderer
|
|
241
|
+
if (this.renderer) {
|
|
242
|
+
this.renderer.destroy();
|
|
243
|
+
this.renderer = null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Destroy the grid engine
|
|
247
|
+
if (this.engine) {
|
|
248
|
+
this.engine.destroy();
|
|
249
|
+
this.engine = null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|