@cocoar/data-grid 0.1.0 → 0.2.0
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 +553 -4
- package/fesm2022/cocoar-data-grid.mjs +466 -14
- package/fesm2022/cocoar-data-grid.mjs.map +1 -1
- package/package.json +5 -3
- package/types/cocoar-data-grid.d.ts +116 -6
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { Subject, takeUntil } from 'rxjs';
|
|
2
|
-
import { themeQuartz } from 'ag-grid-community';
|
|
1
|
+
import { Subject, takeUntil, Observable } from 'rxjs';
|
|
3
2
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, Input, Directive } from '@angular/core';
|
|
3
|
+
import { inject, ChangeDetectionStrategy, Component, HostListener, Input, Directive } from '@angular/core';
|
|
4
|
+
import { CoarTagComponent, CoarIconComponent } from '@cocoar/ui/components';
|
|
5
|
+
import { COAR_I18N_PROVIDER, CoarDatePipe } from '@cocoar/localization';
|
|
6
|
+
import { themeQuartz } from 'ag-grid-community';
|
|
5
7
|
import { AgGridAngular } from 'ag-grid-angular';
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -192,6 +194,44 @@ class CoarGridColumnBuilder {
|
|
|
192
194
|
return this;
|
|
193
195
|
}
|
|
194
196
|
// ============================================================
|
|
197
|
+
// Quick Filter
|
|
198
|
+
// ============================================================
|
|
199
|
+
/** Set quick filter text extractor or disable quick filtering for this column */
|
|
200
|
+
quickFilter(fn) {
|
|
201
|
+
if (typeof fn === 'boolean') {
|
|
202
|
+
this.#colDef.getQuickFilterText = fn ? undefined : () => '';
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
this.#colDef.getQuickFilterText = fn;
|
|
206
|
+
}
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
// ============================================================
|
|
210
|
+
// Sorting
|
|
211
|
+
// ============================================================
|
|
212
|
+
/** Set custom sort comparator */
|
|
213
|
+
comparator(fn) {
|
|
214
|
+
this.#colDef.comparator = fn;
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
// ============================================================
|
|
218
|
+
// Row Drag
|
|
219
|
+
// ============================================================
|
|
220
|
+
/** Enable row drag on this column */
|
|
221
|
+
rowDrag(value = true) {
|
|
222
|
+
this.#colDef.rowDrag = value;
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
// ============================================================
|
|
226
|
+
// Cell Renderer (config pattern)
|
|
227
|
+
// ============================================================
|
|
228
|
+
/** Set cell renderer with config object (params wrapped in `config` key) */
|
|
229
|
+
cellRendererConfig(component, config) {
|
|
230
|
+
this.#colDef.cellRenderer = component;
|
|
231
|
+
this.#colDef.cellRendererParams = { ...this.#colDef.cellRendererParams, config };
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
// ============================================================
|
|
195
235
|
// Advanced Options
|
|
196
236
|
// ============================================================
|
|
197
237
|
/** Set any AG Grid ColDef option directly */
|
|
@@ -213,6 +253,158 @@ class CoarGridColumnBuilder {
|
|
|
213
253
|
}
|
|
214
254
|
}
|
|
215
255
|
|
|
256
|
+
class CoarTagCellRendererComponent {
|
|
257
|
+
tags = [];
|
|
258
|
+
size = 's';
|
|
259
|
+
config = {};
|
|
260
|
+
i18n = inject(COAR_I18N_PROVIDER, { optional: true });
|
|
261
|
+
agInit(params) {
|
|
262
|
+
this.config = params.config ?? {};
|
|
263
|
+
this.size = this.config.size ?? 's';
|
|
264
|
+
this.updateTags(params.value);
|
|
265
|
+
}
|
|
266
|
+
refresh(params) {
|
|
267
|
+
this.updateTags(params.value);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
updateTags(value) {
|
|
271
|
+
const rawLabels = this.extractLabels(value);
|
|
272
|
+
this.tags = rawLabels.map((label) => ({
|
|
273
|
+
label: this.translateLabel(label),
|
|
274
|
+
variant: this.resolveVariant(label),
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
extractLabels(value) {
|
|
278
|
+
if (value == null)
|
|
279
|
+
return [];
|
|
280
|
+
if (Array.isArray(value)) {
|
|
281
|
+
return value.map((item) => this.labelFromItem(item));
|
|
282
|
+
}
|
|
283
|
+
if (typeof value === 'string') {
|
|
284
|
+
const separator = this.config.separator ?? ',';
|
|
285
|
+
return value
|
|
286
|
+
.split(separator)
|
|
287
|
+
.map((s) => s.trim())
|
|
288
|
+
.filter(Boolean);
|
|
289
|
+
}
|
|
290
|
+
return [String(value)];
|
|
291
|
+
}
|
|
292
|
+
labelFromItem(item) {
|
|
293
|
+
if (item != null && typeof item === 'object' && this.config.labelProperty) {
|
|
294
|
+
return String(item[this.config.labelProperty] ?? '');
|
|
295
|
+
}
|
|
296
|
+
return String(item ?? '');
|
|
297
|
+
}
|
|
298
|
+
translateLabel(label) {
|
|
299
|
+
if (this.config.i18nPrefix && this.i18n) {
|
|
300
|
+
return this.i18n.t(this.config.i18nPrefix + label);
|
|
301
|
+
}
|
|
302
|
+
return label;
|
|
303
|
+
}
|
|
304
|
+
resolveVariant(label) {
|
|
305
|
+
return this.config.variantMap?.[label] ?? this.config.variant ?? 'neutral';
|
|
306
|
+
}
|
|
307
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarTagCellRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
308
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CoarTagCellRendererComponent, isStandalone: true, selector: "coar-tag-cell-renderer", ngImport: i0, template: `
|
|
309
|
+
@for (tag of tags; track tag.label) {
|
|
310
|
+
<coar-tag [variant]="tag.variant" [size]="size">{{ tag.label }}</coar-tag>
|
|
311
|
+
}
|
|
312
|
+
`, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--coar-spacing-xs, 4px);flex-wrap:wrap;height:100%}\n"], dependencies: [{ kind: "component", type: CoarTagComponent, selector: "coar-tag", inputs: ["elevated", "borderless", "variant", "size", "closable"], outputs: ["closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
313
|
+
}
|
|
314
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarTagCellRendererComponent, decorators: [{
|
|
315
|
+
type: Component,
|
|
316
|
+
args: [{ selector: 'coar-tag-cell-renderer', standalone: true, imports: [CoarTagComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
317
|
+
@for (tag of tags; track tag.label) {
|
|
318
|
+
<coar-tag [variant]="tag.variant" [size]="size">{{ tag.label }}</coar-tag>
|
|
319
|
+
}
|
|
320
|
+
`, styles: [":host{display:flex;align-items:center;gap:var(--coar-spacing-xs, 4px);flex-wrap:wrap;height:100%}\n"] }]
|
|
321
|
+
}] });
|
|
322
|
+
|
|
323
|
+
class CoarIconCellRendererComponent {
|
|
324
|
+
iconName = '';
|
|
325
|
+
size = 's';
|
|
326
|
+
source;
|
|
327
|
+
color = 'inherit';
|
|
328
|
+
onClick;
|
|
329
|
+
params;
|
|
330
|
+
agInit(params) {
|
|
331
|
+
this.params = params;
|
|
332
|
+
const config = params.config ?? {};
|
|
333
|
+
this.size = config.size ?? 's';
|
|
334
|
+
this.source = config.source;
|
|
335
|
+
this.color = config.color ?? 'inherit';
|
|
336
|
+
this.onClick = config.onClick;
|
|
337
|
+
this.iconName = params.value ?? '';
|
|
338
|
+
}
|
|
339
|
+
refresh(params) {
|
|
340
|
+
this.params = params;
|
|
341
|
+
this.iconName = params.value ?? '';
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
handleClick() {
|
|
345
|
+
this.onClick?.(this.params);
|
|
346
|
+
}
|
|
347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarIconCellRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
348
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CoarIconCellRendererComponent, isStandalone: true, selector: "coar-icon-cell-renderer", host: { listeners: { "click": "handleClick()" }, properties: { "class.clickable": "!!onClick" } }, ngImport: i0, template: `
|
|
349
|
+
@if (iconName) {
|
|
350
|
+
<coar-icon
|
|
351
|
+
[name]="iconName"
|
|
352
|
+
[size]="size"
|
|
353
|
+
[source]="source"
|
|
354
|
+
[color]="color"
|
|
355
|
+
/>
|
|
356
|
+
}
|
|
357
|
+
`, isInline: true, styles: [":host{display:flex;align-items:center;justify-content:center;height:100%}:host(.clickable){cursor:pointer}\n"], dependencies: [{ kind: "component", type: CoarIconComponent, selector: "coar-icon", inputs: ["name", "source", "size", "rotate", "rotateTransition", "spin", "color", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
358
|
+
}
|
|
359
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarIconCellRendererComponent, decorators: [{
|
|
360
|
+
type: Component,
|
|
361
|
+
args: [{ selector: 'coar-icon-cell-renderer', standalone: true, imports: [CoarIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
362
|
+
@if (iconName) {
|
|
363
|
+
<coar-icon
|
|
364
|
+
[name]="iconName"
|
|
365
|
+
[size]="size"
|
|
366
|
+
[source]="source"
|
|
367
|
+
[color]="color"
|
|
368
|
+
/>
|
|
369
|
+
}
|
|
370
|
+
`, host: {
|
|
371
|
+
'[class.clickable]': '!!onClick',
|
|
372
|
+
'(click)': 'handleClick()',
|
|
373
|
+
}, styles: [":host{display:flex;align-items:center;justify-content:center;height:100%}:host(.clickable){cursor:pointer}\n"] }]
|
|
374
|
+
}] });
|
|
375
|
+
|
|
376
|
+
class CoarDateCellRendererComponent {
|
|
377
|
+
dateValue = null;
|
|
378
|
+
agInit(params) {
|
|
379
|
+
this.updateValue(params.value);
|
|
380
|
+
}
|
|
381
|
+
refresh(params) {
|
|
382
|
+
this.updateValue(params.value);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
updateValue(value) {
|
|
386
|
+
if (!value) {
|
|
387
|
+
this.dateValue = null;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (value instanceof Date) {
|
|
391
|
+
this.dateValue = value;
|
|
392
|
+
}
|
|
393
|
+
else if (typeof value === 'string') {
|
|
394
|
+
this.dateValue = value;
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
this.dateValue = null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarDateCellRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
401
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: CoarDateCellRendererComponent, isStandalone: true, selector: "coar-date-cell-renderer", ngImport: i0, template: `{{ dateValue | coarDate }}`, isInline: true, styles: [":host{display:flex;align-items:center;height:100%}\n"], dependencies: [{ kind: "pipe", type: CoarDatePipe, name: "coarDate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
402
|
+
}
|
|
403
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarDateCellRendererComponent, decorators: [{
|
|
404
|
+
type: Component,
|
|
405
|
+
args: [{ selector: 'coar-date-cell-renderer', standalone: true, imports: [CoarDatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: `{{ dateValue | coarDate }}`, styles: [":host{display:flex;align-items:center;height:100%}\n"] }]
|
|
406
|
+
}] });
|
|
407
|
+
|
|
216
408
|
/**
|
|
217
409
|
* Factory for creating typed column builders.
|
|
218
410
|
* Provides convenient methods for common column types.
|
|
@@ -224,6 +416,8 @@ class CoarGridColumnBuilder {
|
|
|
224
416
|
* .columns([
|
|
225
417
|
* col => col.field('name').header('Name').flex(1),
|
|
226
418
|
* col => col.field('createdAt').header('Created').width(150),
|
|
419
|
+
* col => col.tag('status', { variantMap: { active: 'success' } }),
|
|
420
|
+
* col => col.icon('type', { size: 's' }),
|
|
227
421
|
* ])
|
|
228
422
|
* ```
|
|
229
423
|
*/
|
|
@@ -235,11 +429,12 @@ class CoarGridColumnFactory {
|
|
|
235
429
|
return new CoarGridColumnBuilder(fieldName);
|
|
236
430
|
}
|
|
237
431
|
/**
|
|
238
|
-
* Create a date column with standard formatting
|
|
432
|
+
* Create a date column with standard or custom formatting.
|
|
433
|
+
*
|
|
434
|
+
* @param format - Preset name ('short' | 'long' | 'datetime'), Intl options object, or custom formatter function
|
|
239
435
|
*/
|
|
240
436
|
date(fieldName, format = 'short') {
|
|
241
437
|
const builder = new CoarGridColumnBuilder(fieldName);
|
|
242
|
-
// Add basic date formatting
|
|
243
438
|
builder.valueFormatter((params) => {
|
|
244
439
|
const value = params.value;
|
|
245
440
|
if (!value)
|
|
@@ -247,7 +442,15 @@ class CoarGridColumnFactory {
|
|
|
247
442
|
const date = value instanceof Date ? value : new Date(value);
|
|
248
443
|
if (isNaN(date.getTime()))
|
|
249
444
|
return String(value);
|
|
250
|
-
//
|
|
445
|
+
// Custom formatter function
|
|
446
|
+
if (typeof format === 'function') {
|
|
447
|
+
return format(date);
|
|
448
|
+
}
|
|
449
|
+
// Intl options object
|
|
450
|
+
if (typeof format === 'object') {
|
|
451
|
+
return new Intl.DateTimeFormat(undefined, format).format(date);
|
|
452
|
+
}
|
|
453
|
+
// Preset string
|
|
251
454
|
switch (format) {
|
|
252
455
|
case 'short':
|
|
253
456
|
return date.toLocaleDateString();
|
|
@@ -314,6 +517,56 @@ class CoarGridColumnFactory {
|
|
|
314
517
|
});
|
|
315
518
|
return builder;
|
|
316
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Create a tag column that renders values as `<coar-tag>` elements.
|
|
522
|
+
*
|
|
523
|
+
* Supports string (split by separator), array, and object array values.
|
|
524
|
+
*
|
|
525
|
+
* @param config - Tag rendering configuration (variantMap, size, i18nPrefix, etc.)
|
|
526
|
+
*/
|
|
527
|
+
tag(fieldName, config) {
|
|
528
|
+
const builder = new CoarGridColumnBuilder(fieldName);
|
|
529
|
+
builder.cellRendererConfig(CoarTagCellRendererComponent, config ?? {});
|
|
530
|
+
builder.sortable();
|
|
531
|
+
// Alphabetical comparator on joined tag labels
|
|
532
|
+
const separator = config?.separator ?? ',';
|
|
533
|
+
builder.comparator((valueA, valueB) => {
|
|
534
|
+
const normalize = (v) => {
|
|
535
|
+
if (Array.isArray(v))
|
|
536
|
+
return v.map(String).sort().join(',');
|
|
537
|
+
if (typeof v === 'string')
|
|
538
|
+
return v.split(separator).map((s) => s.trim()).sort().join(',');
|
|
539
|
+
return String(v ?? '');
|
|
540
|
+
};
|
|
541
|
+
return normalize(valueA).localeCompare(normalize(valueB));
|
|
542
|
+
});
|
|
543
|
+
return builder;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Create an icon column that renders values as `<coar-icon>` elements.
|
|
547
|
+
*
|
|
548
|
+
* The cell value is used as the icon name.
|
|
549
|
+
*
|
|
550
|
+
* @param config - Icon rendering configuration (size, source, color, onClick)
|
|
551
|
+
*/
|
|
552
|
+
icon(fieldName, config) {
|
|
553
|
+
const builder = new CoarGridColumnBuilder(fieldName);
|
|
554
|
+
builder.cellRendererConfig(CoarIconCellRendererComponent, config ?? {});
|
|
555
|
+
return builder;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Create a date column with locale-aware rendering via `CoarDatePipe`.
|
|
559
|
+
*
|
|
560
|
+
* Unlike `date()`, this uses a cell renderer component for full locale integration.
|
|
561
|
+
*
|
|
562
|
+
* @param config - Date rendering configuration (showSeconds, customFormat)
|
|
563
|
+
*/
|
|
564
|
+
localDate(fieldName, config) {
|
|
565
|
+
const builder = new CoarGridColumnBuilder(fieldName);
|
|
566
|
+
builder.cellRendererConfig(CoarDateCellRendererComponent, config ?? {});
|
|
567
|
+
builder.sortable();
|
|
568
|
+
return builder;
|
|
569
|
+
}
|
|
317
570
|
}
|
|
318
571
|
|
|
319
572
|
/**
|
|
@@ -402,6 +655,14 @@ class CoarGridBuilder {
|
|
|
402
655
|
#columnDefs = [];
|
|
403
656
|
#rowData = null;
|
|
404
657
|
#rowData$;
|
|
658
|
+
// Deferred state (applied after grid ready)
|
|
659
|
+
#columnState;
|
|
660
|
+
#openRows$;
|
|
661
|
+
#sortFilterTriggers = [];
|
|
662
|
+
#externalFilterTriggers = [];
|
|
663
|
+
// Viewport event handlers (wired by directive)
|
|
664
|
+
#viewportClickHandler;
|
|
665
|
+
#viewportContextMenuHandler;
|
|
405
666
|
/** Observable that emits when grid is ready */
|
|
406
667
|
gridReady$ = this.#gridReady$.asObservable();
|
|
407
668
|
constructor() {
|
|
@@ -421,7 +682,6 @@ class CoarGridBuilder {
|
|
|
421
682
|
#createDefaultOptions() {
|
|
422
683
|
return {
|
|
423
684
|
theme: cocoarTheme,
|
|
424
|
-
suppressPropertyNamesCheck: true,
|
|
425
685
|
animateRows: true,
|
|
426
686
|
rowSelection: undefined,
|
|
427
687
|
};
|
|
@@ -429,6 +689,15 @@ class CoarGridBuilder {
|
|
|
429
689
|
#mergeOptions(options) {
|
|
430
690
|
this.#gridOptions = { ...this.#gridOptions, ...options };
|
|
431
691
|
}
|
|
692
|
+
#composeHandler(existing, handler) {
|
|
693
|
+
if (existing) {
|
|
694
|
+
return (event) => {
|
|
695
|
+
existing(event);
|
|
696
|
+
handler(event);
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
return handler;
|
|
700
|
+
}
|
|
432
701
|
// ============================================================
|
|
433
702
|
// Column Configuration
|
|
434
703
|
// ============================================================
|
|
@@ -479,7 +748,8 @@ class CoarGridBuilder {
|
|
|
479
748
|
// ============================================================
|
|
480
749
|
/** Enable row selection */
|
|
481
750
|
rowSelection(mode) {
|
|
482
|
-
|
|
751
|
+
const mapped = mode === 'single' ? 'singleRow' : 'multiRow';
|
|
752
|
+
this.#mergeOptions({ rowSelection: { mode: mapped } });
|
|
483
753
|
return this;
|
|
484
754
|
}
|
|
485
755
|
// ============================================================
|
|
@@ -496,15 +766,71 @@ class CoarGridBuilder {
|
|
|
496
766
|
return this;
|
|
497
767
|
}
|
|
498
768
|
// ============================================================
|
|
769
|
+
// Sorting
|
|
770
|
+
// ============================================================
|
|
771
|
+
/** Set initial sort column and direction */
|
|
772
|
+
defaultSort(field, direction) {
|
|
773
|
+
this.#gridOptions.initialState = {
|
|
774
|
+
...this.#gridOptions.initialState,
|
|
775
|
+
sort: {
|
|
776
|
+
sortModel: [{ colId: field, sort: direction }],
|
|
777
|
+
},
|
|
778
|
+
};
|
|
779
|
+
return this;
|
|
780
|
+
}
|
|
781
|
+
/** Set custom post-sort function to reorder rows after AG Grid sorts */
|
|
782
|
+
sortFunction(fn) {
|
|
783
|
+
this.#mergeOptions({ postSortRows: fn });
|
|
784
|
+
return this;
|
|
785
|
+
}
|
|
786
|
+
/** Re-trigger sort and filter when the given observable emits */
|
|
787
|
+
updateSortAndFilterWhen(trigger) {
|
|
788
|
+
this.#sortFilterTriggers.push(trigger);
|
|
789
|
+
return this;
|
|
790
|
+
}
|
|
791
|
+
// ============================================================
|
|
792
|
+
// Column State
|
|
793
|
+
// ============================================================
|
|
794
|
+
/** Merge column state to restore column widths, order, visibility */
|
|
795
|
+
columnState(state) {
|
|
796
|
+
this.#columnState = state;
|
|
797
|
+
return this;
|
|
798
|
+
}
|
|
799
|
+
// ============================================================
|
|
800
|
+
// Tree / Group Data
|
|
801
|
+
// ============================================================
|
|
802
|
+
/** Set which parent rows are expanded (observable of row IDs) */
|
|
803
|
+
openRows(openRows$) {
|
|
804
|
+
this.#openRows$ = openRows$;
|
|
805
|
+
return this;
|
|
806
|
+
}
|
|
807
|
+
// ============================================================
|
|
808
|
+
// Editing
|
|
809
|
+
// ============================================================
|
|
810
|
+
/** Enable full-row editing mode */
|
|
811
|
+
fullRowEdit(value = true) {
|
|
812
|
+
this.#mergeOptions({ editType: value ? 'fullRow' : undefined });
|
|
813
|
+
return this;
|
|
814
|
+
}
|
|
815
|
+
/** Stop cell editing when cells lose focus */
|
|
816
|
+
stopEditingWhenCellsLoseFocus(value = true) {
|
|
817
|
+
this.#mergeOptions({ stopEditingWhenCellsLoseFocus: value });
|
|
818
|
+
return this;
|
|
819
|
+
}
|
|
820
|
+
// ============================================================
|
|
821
|
+
// Resize
|
|
822
|
+
// ============================================================
|
|
823
|
+
/** Enable shift-key column resize mode */
|
|
824
|
+
shiftResizeMode(value = true) {
|
|
825
|
+
this.#mergeOptions({ colResizeDefault: value ? 'shift' : undefined });
|
|
826
|
+
return this;
|
|
827
|
+
}
|
|
828
|
+
// ============================================================
|
|
499
829
|
// Event Handlers
|
|
500
830
|
// ============================================================
|
|
501
831
|
/** Handle grid ready event */
|
|
502
832
|
onGridReady(handler) {
|
|
503
|
-
|
|
504
|
-
this.#gridOptions.onGridReady = (event) => {
|
|
505
|
-
existing?.(event);
|
|
506
|
-
handler(event);
|
|
507
|
-
};
|
|
833
|
+
this.#gridOptions.onGridReady = this.#composeHandler(this.#gridOptions.onGridReady, handler);
|
|
508
834
|
return this;
|
|
509
835
|
}
|
|
510
836
|
/** Handle row click */
|
|
@@ -527,6 +853,39 @@ class CoarGridBuilder {
|
|
|
527
853
|
this.#mergeOptions({ onCellDoubleClicked: handler });
|
|
528
854
|
return this;
|
|
529
855
|
}
|
|
856
|
+
/** Handle grid size changed event */
|
|
857
|
+
onGridSizeChanged(handler) {
|
|
858
|
+
this.#gridOptions.onGridSizeChanged = this.#composeHandler(this.#gridOptions.onGridSizeChanged, handler);
|
|
859
|
+
return this;
|
|
860
|
+
}
|
|
861
|
+
/** Handle cell context menu (right-click). Ctrl+click is passed through to the browser. */
|
|
862
|
+
onCellContextMenu(handler) {
|
|
863
|
+
this.#mergeOptions({
|
|
864
|
+
onCellContextMenu: (event) => {
|
|
865
|
+
const mouseEvent = event.event;
|
|
866
|
+
if (mouseEvent?.ctrlKey)
|
|
867
|
+
return;
|
|
868
|
+
handler(event);
|
|
869
|
+
},
|
|
870
|
+
});
|
|
871
|
+
return this;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Handle click on the grid viewport (empty area outside cells).
|
|
875
|
+
* Wired by the directive via HostListener.
|
|
876
|
+
*/
|
|
877
|
+
onViewportClick(handler) {
|
|
878
|
+
this.#viewportClickHandler = handler;
|
|
879
|
+
return this;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Handle context menu on the grid viewport (empty area outside cells).
|
|
883
|
+
* Wired by the directive via HostListener.
|
|
884
|
+
*/
|
|
885
|
+
onViewportContextMenu(handler) {
|
|
886
|
+
this.#viewportContextMenuHandler = handler;
|
|
887
|
+
return this;
|
|
888
|
+
}
|
|
530
889
|
// ============================================================
|
|
531
890
|
// External Filtering
|
|
532
891
|
// ============================================================
|
|
@@ -538,6 +897,11 @@ class CoarGridBuilder {
|
|
|
538
897
|
});
|
|
539
898
|
return this;
|
|
540
899
|
}
|
|
900
|
+
/** Re-trigger external filter when the given observable emits */
|
|
901
|
+
updateExternalFilterWhen(trigger) {
|
|
902
|
+
this.#externalFilterTriggers.push(trigger);
|
|
903
|
+
return this;
|
|
904
|
+
}
|
|
541
905
|
// ============================================================
|
|
542
906
|
// Grid Options
|
|
543
907
|
// ============================================================
|
|
@@ -585,6 +949,47 @@ class CoarGridBuilder {
|
|
|
585
949
|
}
|
|
586
950
|
});
|
|
587
951
|
}
|
|
952
|
+
// Apply column state
|
|
953
|
+
if (this.#columnState) {
|
|
954
|
+
if (this.#columnState instanceof Observable) {
|
|
955
|
+
this.#columnState
|
|
956
|
+
.pipe(takeUntil(this.#destroy$))
|
|
957
|
+
.subscribe((state) => {
|
|
958
|
+
if (state) {
|
|
959
|
+
event.api.applyColumnState({ state, applyOrder: true });
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
event.api.applyColumnState({ state: this.#columnState, applyOrder: true });
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
// Subscribe to sort/filter triggers
|
|
968
|
+
for (const trigger of this.#sortFilterTriggers) {
|
|
969
|
+
trigger.pipe(takeUntil(this.#destroy$)).subscribe(() => {
|
|
970
|
+
event.api.onSortChanged();
|
|
971
|
+
event.api.onFilterChanged();
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
// Subscribe to external filter triggers
|
|
975
|
+
for (const trigger of this.#externalFilterTriggers) {
|
|
976
|
+
trigger.pipe(takeUntil(this.#destroy$)).subscribe(() => {
|
|
977
|
+
event.api.onFilterChanged();
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
// Subscribe to open rows
|
|
981
|
+
if (this.#openRows$) {
|
|
982
|
+
this.#openRows$.pipe(takeUntil(this.#destroy$)).subscribe((openRowIds) => {
|
|
983
|
+
event.api.forEachNode((node) => {
|
|
984
|
+
if (node.group || node.master) {
|
|
985
|
+
const shouldBeOpen = openRowIds.includes(node.key ?? node.id ?? '');
|
|
986
|
+
if (node.expanded !== shouldBeOpen) {
|
|
987
|
+
node.setExpanded(shouldBeOpen);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
});
|
|
992
|
+
}
|
|
588
993
|
});
|
|
589
994
|
}
|
|
590
995
|
/** @internal Called by the directive on destroy */
|
|
@@ -593,6 +998,18 @@ class CoarGridBuilder {
|
|
|
593
998
|
this.#destroy$.complete();
|
|
594
999
|
this.#gridReady$.complete();
|
|
595
1000
|
}
|
|
1001
|
+
/** @internal Get viewport click handler (for directive) */
|
|
1002
|
+
_getViewportClickHandler() {
|
|
1003
|
+
return this.#viewportClickHandler;
|
|
1004
|
+
}
|
|
1005
|
+
/** @internal Get viewport context menu handler (for directive) */
|
|
1006
|
+
_getViewportContextMenuHandler() {
|
|
1007
|
+
return this.#viewportContextMenuHandler;
|
|
1008
|
+
}
|
|
1009
|
+
/** @internal Check if a cell context menu handler is registered (for directive) */
|
|
1010
|
+
_hasCellContextMenuHandler() {
|
|
1011
|
+
return this.#gridOptions.onCellContextMenu != null;
|
|
1012
|
+
}
|
|
596
1013
|
/** Get column definitions (for directive) */
|
|
597
1014
|
_getColumnDefs() {
|
|
598
1015
|
return this.#columnDefs;
|
|
@@ -607,6 +1024,11 @@ class CoarGridBuilder {
|
|
|
607
1024
|
}
|
|
608
1025
|
}
|
|
609
1026
|
|
|
1027
|
+
const VIEWPORT_CLASSES = ['ag-body-viewport', 'ag-center-cols-viewport'];
|
|
1028
|
+
function isViewportTarget(event) {
|
|
1029
|
+
const target = event.target;
|
|
1030
|
+
return VIEWPORT_CLASSES.some((cls) => target.classList.contains(cls));
|
|
1031
|
+
}
|
|
610
1032
|
/**
|
|
611
1033
|
* Directive that binds a CoarGridBuilder to an AG Grid instance.
|
|
612
1034
|
*
|
|
@@ -649,8 +1071,32 @@ class CoarDataGridDirective {
|
|
|
649
1071
|
ngOnDestroy() {
|
|
650
1072
|
this.#gridBuilder?._destroy();
|
|
651
1073
|
}
|
|
1074
|
+
onHostClick($event) {
|
|
1075
|
+
if ($event.ctrlKey)
|
|
1076
|
+
return;
|
|
1077
|
+
const handler = this.#gridBuilder?._getViewportClickHandler();
|
|
1078
|
+
if (handler && isViewportTarget($event)) {
|
|
1079
|
+
$event.preventDefault();
|
|
1080
|
+
handler($event, this.#agGrid.api);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
onHostContextMenu($event) {
|
|
1084
|
+
if ($event.ctrlKey)
|
|
1085
|
+
return;
|
|
1086
|
+
if (isViewportTarget($event)) {
|
|
1087
|
+
const handler = this.#gridBuilder?._getViewportContextMenuHandler();
|
|
1088
|
+
if (handler) {
|
|
1089
|
+
$event.preventDefault();
|
|
1090
|
+
handler($event, this.#agGrid.api);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
else if (this.#gridBuilder?._hasCellContextMenuHandler()) {
|
|
1094
|
+
// Suppress the browser context menu — AG Grid's onCellContextMenu handles it
|
|
1095
|
+
$event.preventDefault();
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
652
1098
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarDataGridDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
653
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: CoarDataGridDirective, isStandalone: true, selector: "ag-grid-angular[coarDataGrid]", inputs: { coarDataGrid: "coarDataGrid" }, host: { styleAttribute: "display: flex; flex-direction: column; flex-grow: 1;", classAttribute: "ag-theme-cocoar" }, exportAs: ["coarDataGrid"], ngImport: i0 });
|
|
1099
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: CoarDataGridDirective, isStandalone: true, selector: "ag-grid-angular[coarDataGrid]", inputs: { coarDataGrid: "coarDataGrid" }, host: { listeners: { "click": "onHostClick($event)", "contextmenu": "onHostContextMenu($event)" }, styleAttribute: "display: flex; flex-direction: column; flex-grow: 1;", classAttribute: "ag-theme-cocoar" }, exportAs: ["coarDataGrid"], ngImport: i0 });
|
|
654
1100
|
}
|
|
655
1101
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoarDataGridDirective, decorators: [{
|
|
656
1102
|
type: Directive,
|
|
@@ -665,6 +1111,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
665
1111
|
}]
|
|
666
1112
|
}], propDecorators: { coarDataGrid: [{
|
|
667
1113
|
type: Input
|
|
1114
|
+
}], onHostClick: [{
|
|
1115
|
+
type: HostListener,
|
|
1116
|
+
args: ['click', ['$event']]
|
|
1117
|
+
}], onHostContextMenu: [{
|
|
1118
|
+
type: HostListener,
|
|
1119
|
+
args: ['contextmenu', ['$event']]
|
|
668
1120
|
}] } });
|
|
669
1121
|
|
|
670
1122
|
/**
|