photon-grid-angular 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,593 @@
1
+ import * as i0 from '@angular/core';
2
+ import { TemplateRef, createComponent, EventEmitter, ElementRef, ChangeDetectionStrategy, Component, Output, Input, ViewChild, NgModule } from '@angular/core';
3
+ import { GridCore, GridEventType } from 'photon-grid-core';
4
+ export { GridEventType } from 'photon-grid-core';
5
+
6
+ /** Narrowing helpers used by the adapter (and usable by consumers/tests). */
7
+ function isComponentRendererSpec(value) {
8
+ return !!value && typeof value === 'object' && value.kind === 'component';
9
+ }
10
+ function isTemplateRendererSpec(value) {
11
+ return !!value && typeof value === 'object' && value.kind === 'template';
12
+ }
13
+
14
+ /**
15
+ * Bridges the framework-agnostic `ColumnRendererMap` (plain functions
16
+ * returning `HTMLElement | string`) to Angular components and templates.
17
+ *
18
+ * One instance is owned per `<photon-grid>` and lives for the component's
19
+ * lifetime: {@link adaptColumns} is called whenever columns are (re)supplied,
20
+ * and {@link dispose} is called once, in `ngOnDestroy`, to guarantee nothing
21
+ * outlives the host component.
22
+ *
23
+ * ### Why a MutationObserver
24
+ * The core owns cell/row virtualization and recycling; it has no lifecycle
25
+ * hook that tells us "this cell's DOM node is gone, destroy whatever you
26
+ * mounted into it". Since every renderer invocation mounts a *fresh*
27
+ * component/embedded view (the core's contract is "produce output for these
28
+ * params", not "patch existing output"), the only reliable place to catch
29
+ * disposal is watching the DOM itself: when the core removes/replaces a
30
+ * mounted host element, a MutationObserver on the grid root sees it and we
31
+ * destroy the corresponding Angular view right then. This is what keeps
32
+ * scrolling/virtualized grids from leaking components.
33
+ */
34
+ class RendererAdapter {
35
+ appRef;
36
+ environmentInjector;
37
+ elementInjector;
38
+ /** Host element -> live Angular view mounted into it. */
39
+ mounts = new Map();
40
+ observer;
41
+ constructor(appRef, environmentInjector, elementInjector) {
42
+ this.appRef = appRef;
43
+ this.environmentInjector = environmentInjector;
44
+ this.elementInjector = elementInjector;
45
+ }
46
+ /**
47
+ * Starts watching `root` for node removals so mounted views can be torn
48
+ * down as soon as the core discards their host element. Safe to call
49
+ * repeatedly (e.g. across grid recreation) — any previous observer is
50
+ * disconnected first, but existing mounts are left untouched.
51
+ */
52
+ observe(root) {
53
+ this.observer?.disconnect();
54
+ this.observer = new MutationObserver((mutations) => {
55
+ // Bounded by the number of *currently mounted* renderer views
56
+ // (i.e. visible custom cells), not by row/data-set size, because
57
+ // virtualization keeps that number small regardless of grid size.
58
+ if (this.mounts.size === 0) {
59
+ return;
60
+ }
61
+ for (const mutation of mutations) {
62
+ mutation.removedNodes.forEach((node) => this.cleanupRemovedNode(node));
63
+ }
64
+ });
65
+ this.observer.observe(root, { childList: true, subtree: true });
66
+ }
67
+ /** Recursively converts `GridColumnDef[]` into plain `ColumnDef[]`. */
68
+ adaptColumns(columns) {
69
+ return columns.map((column) => this.adaptColumn(column));
70
+ }
71
+ /**
72
+ * Destroys every currently-mounted component/embedded view and stops
73
+ * observing. Call exactly once, when the owning grid is torn down.
74
+ * Deliberately unconditional (does not wait on the MutationObserver):
75
+ * `disconnect()` drops any in-flight mutation records, so relying on it
76
+ * for final teardown would risk leaking whatever hadn't been flushed yet.
77
+ */
78
+ dispose() {
79
+ this.observer?.disconnect();
80
+ this.observer = undefined;
81
+ for (const [element, mount] of this.mounts) {
82
+ this.destroyMount(mount);
83
+ this.mounts.delete(element);
84
+ }
85
+ }
86
+ adaptColumn(column) {
87
+ const { renderer, children, ...rest } = column;
88
+ const adapted = { ...rest };
89
+ if (renderer) {
90
+ // Adapted per-slot (rather than via a generic loop over the
91
+ // slot names) so each call keeps its own TParams/TOutput pair —
92
+ // looping over a union of keys would force `adaptRenderer` to
93
+ // accept the union of every slot's renderer type at once, which
94
+ // doesn't type-check (an EditorRendererParams renderer isn't
95
+ // assignable where a TooltipRendererParams renderer is expected).
96
+ const adaptedRenderer = {
97
+ display: this.adaptRenderer(renderer.display),
98
+ editor: this.adaptRenderer(renderer.editor),
99
+ option: this.adaptRenderer(renderer.option),
100
+ filter: this.adaptRenderer(renderer.filter),
101
+ tooltip: this.adaptRenderer(renderer.tooltip),
102
+ group: this.adaptRenderer(renderer.group),
103
+ header: this.adaptRenderer(renderer.header),
104
+ summary: this.adaptRenderer(renderer.summary),
105
+ };
106
+ adapted.renderer = adaptedRenderer;
107
+ }
108
+ if (children) {
109
+ adapted.children = children.map((child) => this.adaptColumn(child));
110
+ }
111
+ return adapted;
112
+ }
113
+ adaptRenderer(renderer) {
114
+ if (!renderer) {
115
+ return undefined;
116
+ }
117
+ if (isComponentRendererSpec(renderer)) {
118
+ return (params) => this.mountComponent(renderer, params);
119
+ }
120
+ if (isTemplateRendererSpec(renderer)) {
121
+ return (params) => this.mountTemplate(renderer, params);
122
+ }
123
+ if (renderer instanceof TemplateRef) {
124
+ return (params) => this.mountTemplate({ template: renderer }, params);
125
+ }
126
+ if (this.isComponentType(renderer)) {
127
+ return (params) => this.mountComponent({ component: renderer }, params);
128
+ }
129
+ if (typeof renderer === 'function') {
130
+ return renderer;
131
+ }
132
+ return undefined;
133
+ }
134
+ isComponentType(value) {
135
+ return typeof value === 'function' && !!value.ɵcmp;
136
+ }
137
+ mountComponent(spec, params) {
138
+ const componentRef = createComponent(spec.component, {
139
+ environmentInjector: this.environmentInjector,
140
+ elementInjector: this.elementInjector,
141
+ });
142
+ const normalizedParams = this.normalizeRendererParams(params);
143
+ const inputs = spec.inputs ? spec.inputs(normalizedParams) : this.inferInputs(componentRef, normalizedParams);
144
+ for (const [key, value] of Object.entries(inputs)) {
145
+ this.setComponentInput(componentRef, key, value);
146
+ }
147
+ this.setComponentInput(componentRef, 'params', normalizedParams);
148
+ // Attaching to ApplicationRef is what makes this component
149
+ // participate in normal change detection (zone triggers, async
150
+ // pipe, etc.) for as long as it stays mounted — required for any
151
+ // interactivity inside the component itself (clicks, forms, ...).
152
+ this.appRef.attachView(componentRef.hostView);
153
+ componentRef.changeDetectorRef.detectChanges();
154
+ this.invokeLifecycle(componentRef.instance, 'pgGridInit', normalizedParams);
155
+ this.invokeLifecycle(componentRef.instance, 'pgGridRefresh', normalizedParams);
156
+ const element = componentRef.location.nativeElement;
157
+ this.mounts.set(element, { kind: 'component', ref: componentRef });
158
+ return element;
159
+ }
160
+ setComponentInput(componentRef, key, value) {
161
+ try {
162
+ componentRef.setInput(key, value);
163
+ return;
164
+ }
165
+ catch {
166
+ const instance = componentRef.instance;
167
+ instance[key] = value;
168
+ }
169
+ }
170
+ normalizeRendererParams(params) {
171
+ const normalized = { ...params };
172
+ const option = normalized['option'];
173
+ if (normalized['value'] === undefined && option && typeof option === 'object') {
174
+ const optionValue = option.value;
175
+ normalized['value'] = optionValue;
176
+ normalized['rawValue'] = optionValue;
177
+ }
178
+ return normalized;
179
+ }
180
+ inferInputs(componentRef, params) {
181
+ const instance = componentRef.instance;
182
+ const inputs = {};
183
+ const paramBag = params;
184
+ const row = paramBag['row'] && typeof paramBag['row'] === 'object'
185
+ ? paramBag['row']
186
+ : undefined;
187
+ for (const key of Object.keys(instance)) {
188
+ if (key === '__ngContext__' || key === 'constructor') {
189
+ continue;
190
+ }
191
+ if (paramBag[key] !== undefined) {
192
+ inputs[key] = paramBag[key];
193
+ continue;
194
+ }
195
+ if (row && row[key] !== undefined) {
196
+ inputs[key] = row[key];
197
+ continue;
198
+ }
199
+ if (key === 'seed' && row && row['id'] !== undefined) {
200
+ inputs[key] = row['id'];
201
+ continue;
202
+ }
203
+ if (key === 'value' && paramBag['value'] !== undefined) {
204
+ inputs[key] = paramBag['value'];
205
+ }
206
+ }
207
+ return inputs;
208
+ }
209
+ invokeLifecycle(instance, lifecycle, params) {
210
+ const candidate = instance?.[lifecycle];
211
+ if (typeof candidate === 'function') {
212
+ candidate.call(instance, params);
213
+ }
214
+ }
215
+ mountTemplate(spec, params) {
216
+ const normalizedParams = this.normalizeRendererParams(params);
217
+ const context = {
218
+ $implicit: normalizedParams,
219
+ params: normalizedParams,
220
+ ...(spec.context ? spec.context(normalizedParams) : {}),
221
+ };
222
+ const viewRef = spec.template.createEmbeddedView(context);
223
+ this.appRef.attachView(viewRef);
224
+ viewRef.detectChanges();
225
+ const host = this.resolveTemplateHost(viewRef);
226
+ this.mounts.set(host, { kind: 'template', ref: viewRef });
227
+ return host;
228
+ }
229
+ /**
230
+ * Uses the template's single root element directly when possible (so
231
+ * `editor`/`filter` slots that expect the returned element to *be* the
232
+ * control keep working); falls back to a wrapper `<div>` for templates
233
+ * with multiple root nodes or text-only content.
234
+ */
235
+ resolveTemplateHost(viewRef) {
236
+ const [singleRoot] = viewRef.rootNodes;
237
+ if (viewRef.rootNodes.length === 1 && singleRoot instanceof HTMLElement) {
238
+ return singleRoot;
239
+ }
240
+ const host = document.createElement('div');
241
+ host.classList.add('photon-grid__template-host');
242
+ host.append(...viewRef.rootNodes);
243
+ return host;
244
+ }
245
+ cleanupRemovedNode(node) {
246
+ if (!(node instanceof HTMLElement)) {
247
+ return;
248
+ }
249
+ const directMount = this.mounts.get(node);
250
+ if (directMount) {
251
+ this.mounts.delete(node);
252
+ this.destroyMount(directMount);
253
+ }
254
+ // The core may remove an ancestor (e.g. an entire recycled row)
255
+ // rather than the mounted element itself; sweep for any tracked
256
+ // host contained in the removed subtree.
257
+ if (node.childElementCount === 0) {
258
+ return;
259
+ }
260
+ for (const [element, mount] of this.mounts) {
261
+ if (node.contains(element)) {
262
+ this.mounts.delete(element);
263
+ this.destroyMount(mount);
264
+ }
265
+ }
266
+ }
267
+ destroyMount(mount) {
268
+ if (mount.kind === 'component') {
269
+ this.invokeLifecycle(mount.ref.instance, 'pgGridDestroy', undefined);
270
+ const viewRef = mount.ref.hostView;
271
+ this.appRef.detachView(viewRef);
272
+ mount.ref.destroy();
273
+ return;
274
+ }
275
+ const viewRef = mount.ref;
276
+ this.appRef.detachView(viewRef);
277
+ mount.ref.destroy();
278
+ }
279
+ }
280
+
281
+ const _c0 = ["gridHost"];
282
+ /**
283
+ * Angular wrapper around the framework-agnostic Photon Grid core.
284
+ *
285
+ * The component owns a single {@link GridCore} instance, keeps it in sync
286
+ * with the `columns`, `dataSet` and `options` inputs, and re-emits the
287
+ * grid's internal events as strongly-typed Angular `@Output`s.
288
+ *
289
+ * Cell/header/editor/etc. renderers may be plain functions (raw
290
+ * `HTMLElement`/`string`, identical to the core's own API), or declarative
291
+ * Angular specs — `{ kind: 'component', component: MyBadge }` or
292
+ * `{ kind: 'template', template: myTemplateRef }` — via {@link GridColumnDef}.
293
+ * An internal {@link RendererAdapter} mounts/unmounts those for every
294
+ * renderer invocation and disposes them as soon as the core discards their
295
+ * host element, so virtualization/recycling never leaks components.
296
+ *
297
+ * @example
298
+ * ```html
299
+ * <photon-grid
300
+ * [columns]="columns"
301
+ * [dataSet]="rows"
302
+ * [options]="{ theme: 'light', showCheckboxes: true }"
303
+ * (gridReady)="onReady($event)"
304
+ * (rowClicked)="onRowClicked($event)">
305
+ * </photon-grid>
306
+ * ```
307
+ *
308
+ * @example Component-based cell renderer
309
+ * ```ts
310
+ * columns: GridColumnDef[] = [{
311
+ * colId: 'status', field: 'status', header: 'Status', type: 'string',
312
+ * renderer: {
313
+ * display: {
314
+ * kind: 'component',
315
+ * component: StatusBadgeComponent,
316
+ * inputs: (params) => ({ value: params.value }),
317
+ * },
318
+ * },
319
+ * }];
320
+ * ```
321
+ *
322
+ * @example Template-based cell renderer
323
+ * ```html
324
+ * <ng-template #statusTpl let-params>
325
+ * <span class="badge">{{ params.value }}</span>
326
+ * </ng-template>
327
+ * ```
328
+ * ```ts
329
+ * @ViewChild('statusTpl') statusTpl!: TemplateRef<RendererContext<DisplayRendererParams>>;
330
+ * columns: GridColumnDef[] = [{
331
+ * colId: 'status', field: 'status', header: 'Status', type: 'string',
332
+ * renderer: { display: { kind: 'template', template: this.statusTpl } },
333
+ * }];
334
+ * ```
335
+ */
336
+ class PhotonGridComponent {
337
+ /** Container the grid renders into. */
338
+ gridHost;
339
+ /**
340
+ * Column definitions. Bound to {@link GridApi.setColumns} on change.
341
+ * Provided as a dedicated input rather than through `options.columns`.
342
+ * Renderer slots accept Angular components/templates in addition to
343
+ * plain functions — see {@link GridColumnDef}.
344
+ */
345
+ columns = [];
346
+ /**
347
+ * Row data. Bound to {@link GridApi.setData} on change. Provided as a
348
+ * dedicated input rather than through `options.data`.
349
+ */
350
+ dataSet = [];
351
+ /**
352
+ * Remaining grid options (theme, selection, editing, pagination, …).
353
+ * `columns` and `data` are supplied by the dedicated inputs above and are
354
+ * merged in automatically. The core has no runtime options setter, so a
355
+ * change to this input transparently recreates the grid.
356
+ */
357
+ options = {};
358
+ /** Emitted once the grid is constructed, carrying its {@link GridApi}. */
359
+ gridReady = new EventEmitter();
360
+ /** Row data changed (count delta). */
361
+ dataChanged = new EventEmitter();
362
+ /** A row was clicked. */
363
+ rowClicked = new EventEmitter();
364
+ /** A row was double-clicked. */
365
+ rowDoubleClicked = new EventEmitter();
366
+ /** Row selection changed. */
367
+ rowSelected = new EventEmitter();
368
+ /** A cell was clicked. */
369
+ cellClicked = new EventEmitter();
370
+ /** A cell was double-clicked. */
371
+ cellDoubleClicked = new EventEmitter();
372
+ /** A cell value was committed. */
373
+ cellValueChanged = new EventEmitter();
374
+ /** Range/cell selection changed. */
375
+ cellSelectionChanged = new EventEmitter();
376
+ /** A column was resized. */
377
+ columnResized = new EventEmitter();
378
+ /** A column was reordered. */
379
+ columnMoved = new EventEmitter();
380
+ /** Sort state changed. */
381
+ sortChanged = new EventEmitter();
382
+ /** Filter model changed. */
383
+ filterChanged = new EventEmitter();
384
+ /** Pagination page or size changed. */
385
+ pageChanged = new EventEmitter();
386
+ /** Column state (width/visibility/pin/order) changed. */
387
+ columnsStateChanged = new EventEmitter();
388
+ /** Active theme changed. */
389
+ themeChanged = new EventEmitter();
390
+ /** An export finished. */
391
+ exportComplete = new EventEmitter();
392
+ /** The live core instance, created in {@link ngAfterViewInit}. */
393
+ grid;
394
+ /** Unsubscribe callbacks for every wired core event; drained on teardown. */
395
+ disposers = [];
396
+ /**
397
+ * Bridges Angular component/template renderer specs to the core's
398
+ * plain-function `ColumnRendererMap`. Owned for the lifetime of this
399
+ * component (not DI-provided) so its mounted-view tracking is scoped
400
+ * to this grid instance alone.
401
+ *
402
+ * Built explicitly in the constructor body (rather than as a field
403
+ * initializer referencing constructor-parameter properties) so it isn't
404
+ * subject to field-initializer-vs-parameter-property ordering: at this
405
+ * point `appRef`/`environmentInjector`/`elementInjector` are plain local
406
+ * constructor arguments, guaranteed to already be set.
407
+ */
408
+ rendererAdapter;
409
+ constructor(appRef, environmentInjector, elementInjector) {
410
+ this.rendererAdapter = new RendererAdapter(appRef, environmentInjector, elementInjector);
411
+ }
412
+ ngAfterViewInit() {
413
+ this.createGrid();
414
+ }
415
+ ngOnChanges(changes) {
416
+ // Initial inputs are consumed by createGrid() in ngAfterViewInit.
417
+ if (!this.grid) {
418
+ return;
419
+ }
420
+ // An options change cannot be applied incrementally (no core setter),
421
+ // so recreate; this already re-seeds columns and data.
422
+ if (changes['options'] && !changes['options'].firstChange) {
423
+ this.recreateGrid();
424
+ return;
425
+ }
426
+ if (changes['columns'] && !changes['columns'].firstChange) {
427
+ this.grid.api.setColumns(this.rendererAdapter.adaptColumns(this.columns));
428
+ }
429
+ if (changes['dataSet'] && !changes['dataSet'].firstChange) {
430
+ this.grid.api.setData(this.dataSet);
431
+ }
432
+ }
433
+ ngOnDestroy() {
434
+ this.teardownGrid();
435
+ }
436
+ /** Builds the core from the current inputs and wires its events. */
437
+ createGrid() {
438
+ // Must be observing *before* the core mounts anything, so every
439
+ // renderer-produced element is tracked for cleanup from the start.
440
+ this.rendererAdapter.observe(this.gridHost.nativeElement);
441
+ const mergedOptions = {
442
+ ...this.options,
443
+ columns: this.rendererAdapter.adaptColumns(this.columns),
444
+ data: this.dataSet,
445
+ };
446
+ const grid = new GridCore(this.gridHost.nativeElement, mergedOptions);
447
+ this.grid = grid;
448
+ this.wireEvents(grid.api);
449
+ // GridCore emits READY synchronously during construction (before we can
450
+ // attach a listener), so surface the ready signal explicitly here.
451
+ this.gridReady.emit(grid.api);
452
+ }
453
+ /** Destroys the current core, drops it, and builds a fresh one. */
454
+ recreateGrid() {
455
+ this.teardownGrid();
456
+ this.createGrid();
457
+ }
458
+ /**
459
+ * Disposes event subscriptions, the renderer adapter (stopping its
460
+ * observer and destroying every mounted component/embedded view), and
461
+ * the core instance itself, if any.
462
+ *
463
+ * Order matters: the adapter is disposed *after* `grid.destroy()` so
464
+ * any teardown-time DOM removal is irrelevant (dispose() unconditionally
465
+ * destroys all remaining mounts rather than depending on the
466
+ * MutationObserver having flushed).
467
+ */
468
+ teardownGrid() {
469
+ for (const dispose of this.disposers) {
470
+ dispose();
471
+ }
472
+ this.disposers.length = 0;
473
+ this.grid?.destroy();
474
+ this.grid = undefined;
475
+ this.rendererAdapter.dispose();
476
+ }
477
+ /** Subscribes every core event to its corresponding `@Output` emitter. */
478
+ wireEvents(api) {
479
+ this.subscribe(api, GridEventType.DATA_CHANGED, this.dataChanged);
480
+ this.subscribe(api, GridEventType.ROW_CLICKED, this.rowClicked);
481
+ this.subscribe(api, GridEventType.ROW_DOUBLE_CLICKED, this.rowDoubleClicked);
482
+ this.subscribe(api, GridEventType.ROW_SELECTED, this.rowSelected);
483
+ this.subscribe(api, GridEventType.CELL_CLICKED, this.cellClicked);
484
+ this.subscribe(api, GridEventType.CELL_DOUBLE_CLICKED, this.cellDoubleClicked);
485
+ this.subscribe(api, GridEventType.CELL_VALUE_CHANGED, this.cellValueChanged);
486
+ this.subscribe(api, GridEventType.CELL_SELECTION_CHANGED, this.cellSelectionChanged);
487
+ this.subscribe(api, GridEventType.COLUMN_RESIZED, this.columnResized);
488
+ this.subscribe(api, GridEventType.COLUMN_MOVED, this.columnMoved);
489
+ this.subscribe(api, GridEventType.SORT_CHANGED, this.sortChanged);
490
+ this.subscribe(api, GridEventType.FILTER_CHANGED, this.filterChanged);
491
+ this.subscribe(api, GridEventType.PAGE_CHANGED, this.pageChanged);
492
+ this.subscribe(api, GridEventType.COLUMNS_STATE_CHANGED, this.columnsStateChanged);
493
+ this.subscribe(api, GridEventType.THEME_CHANGED, this.themeChanged);
494
+ this.subscribe(api, GridEventType.EXPORT_COMPLETE, this.exportComplete);
495
+ }
496
+ /**
497
+ * Forwards a single strongly-typed core event to an Angular emitter and
498
+ * records its unsubscribe callback. The generic constraint guarantees the
499
+ * emitter's payload type matches the event's payload in {@link GridEventMap}.
500
+ */
501
+ subscribe(api, event, emitter) {
502
+ const dispose = api.on(event, (payload) => emitter.emit(payload));
503
+ this.disposers.push(dispose);
504
+ }
505
+ static ɵfac = function PhotonGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PhotonGridComponent)(i0.ɵɵdirectiveInject(i0.ApplicationRef), i0.ɵɵdirectiveInject(i0.EnvironmentInjector), i0.ɵɵdirectiveInject(i0.Injector)); };
506
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PhotonGridComponent, selectors: [["photon-grid"]], viewQuery: function PhotonGridComponent_Query(rf, ctx) { if (rf & 1) {
507
+ i0.ɵɵviewQuery(_c0, 7, ElementRef);
508
+ } if (rf & 2) {
509
+ let _t;
510
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.gridHost = _t.first);
511
+ } }, inputs: { columns: "columns", dataSet: "dataSet", options: "options" }, outputs: { gridReady: "gridReady", dataChanged: "dataChanged", rowClicked: "rowClicked", rowDoubleClicked: "rowDoubleClicked", rowSelected: "rowSelected", cellClicked: "cellClicked", cellDoubleClicked: "cellDoubleClicked", cellValueChanged: "cellValueChanged", cellSelectionChanged: "cellSelectionChanged", columnResized: "columnResized", columnMoved: "columnMoved", sortChanged: "sortChanged", filterChanged: "filterChanged", pageChanged: "pageChanged", columnsStateChanged: "columnsStateChanged", themeChanged: "themeChanged", exportComplete: "exportComplete" }, standalone: true, features: [i0.ɵɵNgOnChangesFeature, i0.ɵɵStandaloneFeature], decls: 2, vars: 0, consts: [["gridHost", ""], [1, "photon-grid__host"]], template: function PhotonGridComponent_Template(rf, ctx) { if (rf & 1) {
512
+ i0.ɵɵelement(0, "div", 1, 0);
513
+ } }, styles: ["[_nghost-%COMP%]{display:block;width:100%;height:100%}.photon-grid__host[_ngcontent-%COMP%]{width:100%;height:100%}"], changeDetection: 0 });
514
+ }
515
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PhotonGridComponent, [{
516
+ type: Component,
517
+ args: [{ selector: 'photon-grid', standalone: true, template: `<div #gridHost class="photon-grid__host"></div>`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%;height:100%}.photon-grid__host{width:100%;height:100%}\n"] }]
518
+ }], () => [{ type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }, { type: i0.Injector }], { gridHost: [{
519
+ type: ViewChild,
520
+ args: ['gridHost', { static: true, read: ElementRef }]
521
+ }], columns: [{
522
+ type: Input
523
+ }], dataSet: [{
524
+ type: Input
525
+ }], options: [{
526
+ type: Input
527
+ }], gridReady: [{
528
+ type: Output
529
+ }], dataChanged: [{
530
+ type: Output
531
+ }], rowClicked: [{
532
+ type: Output
533
+ }], rowDoubleClicked: [{
534
+ type: Output
535
+ }], rowSelected: [{
536
+ type: Output
537
+ }], cellClicked: [{
538
+ type: Output
539
+ }], cellDoubleClicked: [{
540
+ type: Output
541
+ }], cellValueChanged: [{
542
+ type: Output
543
+ }], cellSelectionChanged: [{
544
+ type: Output
545
+ }], columnResized: [{
546
+ type: Output
547
+ }], columnMoved: [{
548
+ type: Output
549
+ }], sortChanged: [{
550
+ type: Output
551
+ }], filterChanged: [{
552
+ type: Output
553
+ }], pageChanged: [{
554
+ type: Output
555
+ }], columnsStateChanged: [{
556
+ type: Output
557
+ }], themeChanged: [{
558
+ type: Output
559
+ }], exportComplete: [{
560
+ type: Output
561
+ }] }); })();
562
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PhotonGridComponent, { className: "PhotonGridComponent", filePath: "library\\photon-grid.component.ts", lineNumber: 104 }); })();
563
+
564
+ /**
565
+ * Convenience NgModule for consumers that are not yet on standalone APIs.
566
+ * {@link PhotonGridComponent} is itself standalone; this module simply
567
+ * imports and re-exports it so it can be dropped into an NgModule's
568
+ * `imports`/`exports` and used in that module's templates.
569
+ */
570
+ class PhotonGridModule {
571
+ static ɵfac = function PhotonGridModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PhotonGridModule)(); };
572
+ static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: PhotonGridModule });
573
+ static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({});
574
+ }
575
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PhotonGridModule, [{
576
+ type: NgModule,
577
+ args: [{
578
+ imports: [PhotonGridComponent],
579
+ exports: [PhotonGridComponent],
580
+ }]
581
+ }], null, null); })();
582
+ (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(PhotonGridModule, { imports: [PhotonGridComponent], exports: [PhotonGridComponent] }); })();
583
+
584
+ /*
585
+ * Public API surface of photon-grid-angular.
586
+ */
587
+
588
+ /**
589
+ * Generated bundle index. Do not edit.
590
+ */
591
+
592
+ export { PhotonGridComponent, PhotonGridModule, isComponentRendererSpec, isTemplateRendererSpec };
593
+ //# sourceMappingURL=photon-grid-angular.mjs.map