@esfaenza/es-table 20.3.53 → 20.3.55
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/fesm2022/esfaenza-es-table.mjs +121 -14
- package/fesm2022/esfaenza-es-table.mjs.map +1 -1
- package/index.d.ts +60 -4
- package/package.json +1 -1
|
@@ -341,8 +341,13 @@ class EsThDirective {
|
|
|
341
341
|
this._multi_content = "";
|
|
342
342
|
/** Id della colonna, dev'essere lo stesso per tutti i **td** relativi */
|
|
343
343
|
this.th = "";
|
|
344
|
-
/** Indica se la colonna è pinnata o no */
|
|
344
|
+
/** Indica se la colonna è pinnata o no (bloccata a SINISTRA) */
|
|
345
345
|
this.thPinned = false;
|
|
346
|
+
/**
|
|
347
|
+
* Indica se la colonna è bloccata a DESTRA, in coda alla tabella. Usato solo
|
|
348
|
+
* dall'`<es-table2>`: la tabella originale ignora questo input.
|
|
349
|
+
*/
|
|
350
|
+
this.thPinnedRight = false;
|
|
346
351
|
/** Indica se la colonna è pinnabile o no */
|
|
347
352
|
this.thPinnable = true;
|
|
348
353
|
/** Indica le dimensioni che deve avere questa colonna quando viene pinnata, senza questo tutte le colonne mi collasserebbero l'una sull'altra */
|
|
@@ -379,7 +384,7 @@ class EsThDirective {
|
|
|
379
384
|
this.thEditorOptions = {};
|
|
380
385
|
}
|
|
381
386
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: EsThDirective, deps: [{ token: i0.TemplateRef, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
382
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.31", type: EsThDirective, isStandalone: false, selector: "[th]", inputs: { th: "th", thPinned: "thPinned", thPinnable: "thPinnable", thPinnedWidth: "thPinnedWidth", thVisible: "thVisible", thFixed: "thFixed", thParent: "thParent", thOrder: "thOrder", thOrderable: "thOrderable", thWrap: "thWrap", thResizable: "thResizable", thClass: "thClass", thShrink: "thShrink", thGroupClass: "thGroupClass", thIf: "thIf", thEmpty: "thEmpty", thGroup: "thGroup", thAggregation: "thAggregation", thProperty: "thProperty", thType: "thType", thSource: "thSource", thMulti: "thMulti", thStaticContent: "thStaticContent", thBackgroundColor: "thBackgroundColor", thAlignment: "thAlignment", thFormat: "thFormat", thEditorName: "thEditorName", thEditorOptions: "thEditorOptions", thRendererName: "thRendererName", thRoutePath: "thRoutePath", thRouteParameterProperties: "thRouteParameterProperties" }, ngImport: i0 }); }
|
|
387
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.31", type: EsThDirective, isStandalone: false, selector: "[th]", inputs: { th: "th", thPinned: "thPinned", thPinnedRight: "thPinnedRight", thPinnable: "thPinnable", thPinnedWidth: "thPinnedWidth", thVisible: "thVisible", thFixed: "thFixed", thParent: "thParent", thOrder: "thOrder", thOrderable: "thOrderable", thWrap: "thWrap", thResizable: "thResizable", thClass: "thClass", thShrink: "thShrink", thGroupClass: "thGroupClass", thIf: "thIf", thEmpty: "thEmpty", thGroup: "thGroup", thAggregation: "thAggregation", thProperty: "thProperty", thType: "thType", thSource: "thSource", thMulti: "thMulti", thStaticContent: "thStaticContent", thBackgroundColor: "thBackgroundColor", thAlignment: "thAlignment", thFormat: "thFormat", thEditorName: "thEditorName", thEditorOptions: "thEditorOptions", thRendererName: "thRendererName", thRoutePath: "thRoutePath", thRouteParameterProperties: "thRouteParameterProperties" }, ngImport: i0 }); }
|
|
383
388
|
}
|
|
384
389
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: EsThDirective, decorators: [{
|
|
385
390
|
type: Directive,
|
|
@@ -393,6 +398,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
|
|
|
393
398
|
type: Input
|
|
394
399
|
}], thPinned: [{
|
|
395
400
|
type: Input
|
|
401
|
+
}], thPinnedRight: [{
|
|
402
|
+
type: Input
|
|
396
403
|
}], thPinnable: [{
|
|
397
404
|
type: Input
|
|
398
405
|
}], thPinnedWidth: [{
|
|
@@ -6379,7 +6386,7 @@ function makeColumn(partial) {
|
|
|
6379
6386
|
visible: true, orderable: false, alignment: 'left',
|
|
6380
6387
|
property: null, type: null, source: null, format: null,
|
|
6381
6388
|
cssClass: '', headerClass: '', wrap: false, isGroup: false, aggSpecs: [],
|
|
6382
|
-
headerText: null, headerBg: null, pinned: false, pinnedWidth: null, fixed: false,
|
|
6389
|
+
headerText: null, headerBg: null, pinned: false, pinnedRight: false, pinnedWidth: null, fixed: false,
|
|
6383
6390
|
...partial
|
|
6384
6391
|
};
|
|
6385
6392
|
}
|
|
@@ -6420,7 +6427,10 @@ function mapDirectiveColumns(ths, tdByCol, parentIds, defAlignment) {
|
|
|
6420
6427
|
routePath: th.thRoutePath,
|
|
6421
6428
|
routeParams: th.thRouteParameterProperties,
|
|
6422
6429
|
rendererName: th.thRendererName,
|
|
6423
|
-
|
|
6430
|
+
// `PinnedRight` vince su `Pinned`: i due lati sono esclusivi e dichiararli
|
|
6431
|
+
// entrambi è un errore di stesura, non la richiesta di stare da due parti.
|
|
6432
|
+
pinned: !!th.thPinned && !th.thPinnedRight,
|
|
6433
|
+
pinnedRight: !!th.thPinnedRight,
|
|
6424
6434
|
pinnedWidth: th.thPinnedWidth ?? null,
|
|
6425
6435
|
// Parità v1: una colonna `Fixed` — o una chiave di raggruppamento (`Group`) —
|
|
6426
6436
|
// è sempre visibile e non riposizionabile (ColumnMetadataHandler: `th.thFixed || th.thGroup`)
|
|
@@ -6756,11 +6766,13 @@ class EsTable2Loc extends LocalizationService {
|
|
|
6756
6766
|
super.set("en->it", "Grouped columns cannot be locked", ["Le colonne di un gruppo non sono bloccabili"]);
|
|
6757
6767
|
super.set("en->it", "Unlock", ["Sblocca"]);
|
|
6758
6768
|
super.set("en->it", "Lock to the left", ["Blocca a sinistra"]);
|
|
6769
|
+
super.set("en->it", "Lock to the right", ["Blocca a destra"]);
|
|
6759
6770
|
super.set("en->it", "Up", ["Su"]);
|
|
6760
6771
|
super.set("en->it", "Down", ["Giù"]);
|
|
6761
6772
|
// --- Header di colonna -------------------------------------------------
|
|
6762
6773
|
super.set("en->it", "Unlock column", ["Sblocca colonna"]);
|
|
6763
6774
|
super.set("en->it", "Lock column to the left", ["Blocca colonna a sinistra"]);
|
|
6775
|
+
super.set("en->it", "Lock column to the right", ["Blocca colonna a destra"]);
|
|
6764
6776
|
super.set("en->it", "Drag to resize", ["Trascina per ridimensionare"]);
|
|
6765
6777
|
// --- Azioni di riga ----------------------------------------------------
|
|
6766
6778
|
super.set("en->it", "Remove", ["Rimuovi"]);
|
|
@@ -7003,7 +7015,7 @@ class EsTable2Component {
|
|
|
7003
7015
|
this.measureHeader(el);
|
|
7004
7016
|
}
|
|
7005
7017
|
}
|
|
7006
|
-
/** Misura header (altezza) e
|
|
7018
|
+
/** Misura header (altezza) e colonne di coda (larghezze) per select-bar e pin a destra */
|
|
7007
7019
|
measureHeader(el) {
|
|
7008
7020
|
let dirty = false;
|
|
7009
7021
|
const h = el.offsetHeight;
|
|
@@ -7017,6 +7029,12 @@ class EsTable2Component {
|
|
|
7017
7029
|
this.chromeWidth.set(w);
|
|
7018
7030
|
dirty = true;
|
|
7019
7031
|
}
|
|
7032
|
+
let trail = 0;
|
|
7033
|
+
el.querySelectorAll('th.est2-trail-th').forEach(t => trail += t.offsetWidth);
|
|
7034
|
+
if (trail !== this.trailingWidth()) {
|
|
7035
|
+
this.trailingWidth.set(trail);
|
|
7036
|
+
dirty = true;
|
|
7037
|
+
}
|
|
7020
7038
|
if (dirty)
|
|
7021
7039
|
this.cdr.markForCheck();
|
|
7022
7040
|
}
|
|
@@ -7028,12 +7046,14 @@ class EsTable2Component {
|
|
|
7028
7046
|
* Una colonna `Fixed` resta quindi sempre in testa e solo una pinnata può precederla.
|
|
7029
7047
|
*/
|
|
7030
7048
|
sortStructural(cols) {
|
|
7031
|
-
if (!cols.some(c => c.pinned || c.fixed))
|
|
7049
|
+
if (!cols.some(c => c.pinned || c.pinnedRight || c.fixed))
|
|
7032
7050
|
return cols;
|
|
7051
|
+
const free = (c) => !c.pinned && !c.pinnedRight;
|
|
7033
7052
|
return [
|
|
7034
7053
|
...cols.filter(c => c.pinned),
|
|
7035
|
-
...cols.filter(c =>
|
|
7036
|
-
...cols.filter(c =>
|
|
7054
|
+
...cols.filter(c => free(c) && c.fixed),
|
|
7055
|
+
...cols.filter(c => free(c) && !c.fixed),
|
|
7056
|
+
...cols.filter(c => !c.pinned && c.pinnedRight)
|
|
7037
7057
|
];
|
|
7038
7058
|
}
|
|
7039
7059
|
/** Scroll dello scroller: sposta la finestra solo quando cambia davvero riga */
|
|
@@ -7097,7 +7117,7 @@ class EsTable2Component {
|
|
|
7097
7117
|
colWidthPx(col) {
|
|
7098
7118
|
if (col.width != null)
|
|
7099
7119
|
return col.width;
|
|
7100
|
-
return col.pinned ? this.pinnedWidthPx(col) : null;
|
|
7120
|
+
return (col.pinned || col.pinnedRight) ? this.pinnedWidthPx(col) : null;
|
|
7101
7121
|
}
|
|
7102
7122
|
/** Offset `left` (px) di una colonna sticky all'indice `ci` in visibleColumns */
|
|
7103
7123
|
pinnedLeftPx(ci) { return this.pinnedOffsets()[ci] ?? 0; }
|
|
@@ -7121,6 +7141,11 @@ class EsTable2Component {
|
|
|
7121
7141
|
* sticky (che sta più in alto nello stacking per restare cliccabile durante la selezione).
|
|
7122
7142
|
*/
|
|
7123
7143
|
this.chromeWidth = signal(0, ...(ngDevMode ? [{ debugName: "chromeWidth" }] : []));
|
|
7144
|
+
/**
|
|
7145
|
+
* Larghezza totale (px) delle colonne di SERVIZIO in coda alla tabella (rimozione
|
|
7146
|
+
* e chrome). È il punto da cui partono gli offset delle colonne bloccate a destra.
|
|
7147
|
+
*/
|
|
7148
|
+
this.trailingWidth = signal(0, ...(ngDevMode ? [{ debugName: "trailingWidth" }] : []));
|
|
7124
7149
|
// ===========================================================================
|
|
7125
7150
|
// Superficie pubblica — Input risolti (pattern _alias -> signal risolto)
|
|
7126
7151
|
// ===========================================================================
|
|
@@ -7350,10 +7375,12 @@ class EsTable2Component {
|
|
|
7350
7375
|
this.colStyles = computed(() => {
|
|
7351
7376
|
const cols = this.visibleColumns();
|
|
7352
7377
|
const offsets = this.pinnedOffsets();
|
|
7378
|
+
const rights = this.pinnedRightOffsets();
|
|
7353
7379
|
const bounds = this.columnGroupBoundaries();
|
|
7354
7380
|
return cols.map((c, ci) => ({
|
|
7355
7381
|
width: this.colWidthPx(c),
|
|
7356
7382
|
left: c.pinned ? (offsets[ci] ?? 0) : null,
|
|
7383
|
+
right: c.pinnedRight ? (rights[ci] ?? 0) : null,
|
|
7357
7384
|
groupStart: bounds.has(ci)
|
|
7358
7385
|
}));
|
|
7359
7386
|
}, ...(ngDevMode ? [{ debugName: "colStyles" }] : []));
|
|
@@ -7423,6 +7450,26 @@ class EsTable2Component {
|
|
|
7423
7450
|
return cols.map(c => { const at = left; if (c.pinned)
|
|
7424
7451
|
left += this.colWidthPx(c); return at; });
|
|
7425
7452
|
}, ...(ngDevMode ? [{ debugName: "pinnedOffsets" }] : []));
|
|
7453
|
+
/**
|
|
7454
|
+
* Offset `right` (px) cumulativi delle colonne bloccate a DESTRA, memoizzati come
|
|
7455
|
+
* i corrispettivi di sinistra. Si parte da `trailingWidth()` — la larghezza delle
|
|
7456
|
+
* colonne di servizio in coda (rimozione e chrome), che sono già sticky a destra —
|
|
7457
|
+
* e si risale verso sinistra, così le colonne bloccate si impilano appena prima di
|
|
7458
|
+
* quelle e non ci finiscono sopra.
|
|
7459
|
+
*/
|
|
7460
|
+
this.pinnedRightOffsets = computed(() => {
|
|
7461
|
+
const cols = this.visibleColumns();
|
|
7462
|
+
const out = new Array(cols.length).fill(0);
|
|
7463
|
+
let right = this.trailingWidth();
|
|
7464
|
+
for (let i = cols.length - 1; i >= 0; i--) {
|
|
7465
|
+
out[i] = right;
|
|
7466
|
+
if (cols[i].pinnedRight)
|
|
7467
|
+
right += this.colWidthPx(cols[i]);
|
|
7468
|
+
}
|
|
7469
|
+
return out;
|
|
7470
|
+
}, ...(ngDevMode ? [{ debugName: "pinnedRightOffsets" }] : []));
|
|
7471
|
+
/** true se almeno una colonna visibile è bloccata a destra */
|
|
7472
|
+
this.hasPinnedRight = computed(() => this.visibleColumns().some(c => c.pinnedRight), ...(ngDevMode ? [{ debugName: "hasPinnedRight" }] : []));
|
|
7426
7473
|
/** true se la tabella renderizza colonne strutturate (direttive/dinamica/report), non template semplici */
|
|
7427
7474
|
this.usesColumns = computed(() => this.columns().length > 0, ...(ngDevMode ? [{ debugName: "usesColumns" }] : []));
|
|
7428
7475
|
/**
|
|
@@ -8529,6 +8576,10 @@ class EsTable2Component {
|
|
|
8529
8576
|
}
|
|
8530
8577
|
/** Click su una riga: espande/collassa un gruppo, oppure gestisce la selezione. */
|
|
8531
8578
|
handleRowClick(item, event) {
|
|
8579
|
+
// Trascinamento per evidenziare il testo di una cella: il click che lo conclude
|
|
8580
|
+
// non è un click di riga. Vedi `textSelectionActive()`.
|
|
8581
|
+
if (this.textSelectionActive())
|
|
8582
|
+
return;
|
|
8532
8583
|
if (item?._group) {
|
|
8533
8584
|
this.toggleGroup(item);
|
|
8534
8585
|
return;
|
|
@@ -8560,6 +8611,25 @@ class EsTable2Component {
|
|
|
8560
8611
|
// l'ancora per un eventuale Shift+click successivo.
|
|
8561
8612
|
this.toggleRow(item);
|
|
8562
8613
|
}
|
|
8614
|
+
/**
|
|
8615
|
+
* true se l'utente ha appena EVIDENZIATO DEL TESTO dentro questa tabella.
|
|
8616
|
+
*
|
|
8617
|
+
* Serve a non trasformare un trascinamento fatto per copiare il contenuto di una
|
|
8618
|
+
* cella in una selezione di riga. Funziona per come il browser ordina gli eventi:
|
|
8619
|
+
* il `mousedown` azzera la selezione corrente, quindi su un click SEMPLICE qui la
|
|
8620
|
+
* selezione è sempre collassata e la riga si seleziona come prima; dopo un
|
|
8621
|
+
* trascinamento invece c'è del testo selezionato e il click viene ignorato.
|
|
8622
|
+
*
|
|
8623
|
+
* Il controllo sul nodo di ancoraggio evita che una selezione lasciata da qualche
|
|
8624
|
+
* altra parte nella pagina blocchi i click di riga di questa tabella.
|
|
8625
|
+
*/
|
|
8626
|
+
textSelectionActive() {
|
|
8627
|
+
const s = typeof window !== 'undefined' && window.getSelection ? window.getSelection() : null;
|
|
8628
|
+
if (!s || s.isCollapsed || !s.toString().trim())
|
|
8629
|
+
return false;
|
|
8630
|
+
const node = s.anchorNode;
|
|
8631
|
+
return !!node && this.hostEl.nativeElement.contains(node);
|
|
8632
|
+
}
|
|
8563
8633
|
/**
|
|
8564
8634
|
* Shift/Ctrl+click stile Esplora risorse, attivo solo con `[ShiftClick]`.
|
|
8565
8635
|
*
|
|
@@ -9330,6 +9400,8 @@ class EsTable2Component {
|
|
|
9330
9400
|
// il pin salvato sovrascrive il default della direttiva (solo per colonne note alle prefs)
|
|
9331
9401
|
if (p.pinned && (!known || known.has(c.id)))
|
|
9332
9402
|
c.pinned = p.pinned.includes(c.id);
|
|
9403
|
+
if (p.pinnedRight && (!known || known.has(c.id)))
|
|
9404
|
+
c.pinnedRight = p.pinnedRight.includes(c.id);
|
|
9333
9405
|
// larghezza salvata (resize) → sovrascrive l'auto/pinnedWidth della direttiva
|
|
9334
9406
|
if (p.widths && p.widths[c.id] != null)
|
|
9335
9407
|
c.width = p.widths[c.id];
|
|
@@ -9402,6 +9474,7 @@ class EsTable2Component {
|
|
|
9402
9474
|
// `pinned` è un'estensione retro-compatibile: se assente (prefs vecchie/originali)
|
|
9403
9475
|
// resta il pin di default delle direttive.
|
|
9404
9476
|
const pinned = Array.isArray(prefs.pinned) ? prefs.pinned.filter(id => currentIds.has(id)) : undefined;
|
|
9477
|
+
const pinnedRight = Array.isArray(prefs.pinnedRight) ? prefs.pinnedRight.filter(id => currentIds.has(id)) : undefined;
|
|
9405
9478
|
// Nascondo SOLO le colonne che le preferenze conoscevano davvero (presenti
|
|
9406
9479
|
// nell'ordinamento o nell'elenco visibili) ma non marcate come visibili.
|
|
9407
9480
|
// Una colonna del tutto assente dalle prefs è NUOVA (aggiunta dopo il
|
|
@@ -9418,7 +9491,7 @@ class EsTable2Component {
|
|
|
9418
9491
|
if (currentIds.has(id))
|
|
9419
9492
|
widths[id] = prefs.widths[id];
|
|
9420
9493
|
}
|
|
9421
|
-
this.columnPrefs = { order, hidden, pinned, widths, known: [...knownToPrefs] };
|
|
9494
|
+
this.columnPrefs = { order, hidden, pinned, pinnedRight, widths, known: [...knownToPrefs] };
|
|
9422
9495
|
this.setColumns(this.columns());
|
|
9423
9496
|
if (prefs.orders?.length) {
|
|
9424
9497
|
this.orders.set([...prefs.orders]);
|
|
@@ -9451,6 +9524,7 @@ class EsTable2Component {
|
|
|
9451
9524
|
orders: this.orders(),
|
|
9452
9525
|
// estensioni es-table2 (retro-compatibili: l'originale ignora i campi extra)
|
|
9453
9526
|
pinned: cols.filter(c => c.pinned).map(c => c.id),
|
|
9527
|
+
pinnedRight: cols.filter(c => c.pinnedRight).map(c => c.id),
|
|
9454
9528
|
widths
|
|
9455
9529
|
};
|
|
9456
9530
|
// allineo anche i campi wire dell'AppSearch (parità col server-side)
|
|
@@ -9699,6 +9773,7 @@ class EsTable2Component {
|
|
|
9699
9773
|
order: cols.map(c => c.id),
|
|
9700
9774
|
hidden: cols.filter(c => !c.visible).map(c => c.id),
|
|
9701
9775
|
pinned: cols.filter(c => c.pinned).map(c => c.id),
|
|
9776
|
+
pinnedRight: cols.filter(c => c.pinnedRight).map(c => c.id),
|
|
9702
9777
|
widths: this.currentWidths(),
|
|
9703
9778
|
known: cols.map(c => c.id)
|
|
9704
9779
|
};
|
|
@@ -9734,15 +9809,33 @@ class EsTable2Component {
|
|
|
9734
9809
|
}
|
|
9735
9810
|
// --- Pin/unpin dall'header --------------------------------------------------
|
|
9736
9811
|
/** Attiva/disattiva il pin di una colonna e persiste (dall'indicatore in testata) */
|
|
9812
|
+
/**
|
|
9813
|
+
* Prossimo stato nel ciclo di blocco di una colonna: libera → sinistra → destra →
|
|
9814
|
+
* libera. Un comando solo invece di due interruttori, perché i due lati sono
|
|
9815
|
+
* esclusivi e chi usa la tabella ragiona su "dove la voglio", non su due flag.
|
|
9816
|
+
*/
|
|
9817
|
+
pinCycleNext(col) {
|
|
9818
|
+
if (col.pinnedRight)
|
|
9819
|
+
return 'none';
|
|
9820
|
+
return col.pinned ? 'right' : 'left';
|
|
9821
|
+
}
|
|
9822
|
+
/** Etichetta (sorgente inglese, poi localizzata) di cosa farà il prossimo click sul pin */
|
|
9823
|
+
pinTitleFor(col) {
|
|
9824
|
+
const n = this.pinCycleNext(col);
|
|
9825
|
+
return n === 'left' ? 'Lock column to the left' : n === 'right' ? 'Lock column to the right' : 'Unlock column';
|
|
9826
|
+
}
|
|
9737
9827
|
togglePin(col, ev) {
|
|
9738
9828
|
ev?.stopPropagation();
|
|
9739
|
-
|
|
9829
|
+
const next = this.pinCycleNext(col);
|
|
9830
|
+
col.pinned = next === 'left';
|
|
9831
|
+
col.pinnedRight = next === 'right';
|
|
9740
9832
|
// aggiorno le prefs correnti (ordine/visibilità invariati)
|
|
9741
9833
|
const cols = this.columns();
|
|
9742
9834
|
this.columnPrefs = {
|
|
9743
9835
|
order: cols.map(c => c.id),
|
|
9744
9836
|
hidden: cols.filter(c => !c.visible).map(c => c.id),
|
|
9745
9837
|
pinned: cols.filter(c => c.pinned).map(c => c.id),
|
|
9838
|
+
pinnedRight: cols.filter(c => c.pinnedRight).map(c => c.id),
|
|
9746
9839
|
widths: this.currentWidths(),
|
|
9747
9840
|
known: cols.map(c => c.id)
|
|
9748
9841
|
};
|
|
@@ -9851,6 +9944,7 @@ class EsTable2Component {
|
|
|
9851
9944
|
tmplCtx: c.multiProp != null ? { $implicit: c.headerText } : null,
|
|
9852
9945
|
visible: c.visible,
|
|
9853
9946
|
pinned: !!c.pinned,
|
|
9947
|
+
pinnedRight: !!c.pinnedRight,
|
|
9854
9948
|
grouped: this.isGrouped(c),
|
|
9855
9949
|
fixed: !!c.fixed
|
|
9856
9950
|
})));
|
|
@@ -9864,11 +9958,21 @@ class EsTable2Component {
|
|
|
9864
9958
|
list[i] = { ...list[i], visible: !list[i].visible };
|
|
9865
9959
|
this.dialogCols.set(list);
|
|
9866
9960
|
}
|
|
9961
|
+
/** Come `pinTitleFor`, per la riga della dialog (che ha anche i casi non pinnabili) */
|
|
9962
|
+
dialogPinTitle(c) {
|
|
9963
|
+
if (c.fixed)
|
|
9964
|
+
return 'Fixed columns cannot be locked';
|
|
9965
|
+
if (c.grouped)
|
|
9966
|
+
return 'Grouped columns cannot be locked';
|
|
9967
|
+
const n = this.pinCycleNext(c);
|
|
9968
|
+
return n === 'left' ? 'Lock to the left' : n === 'right' ? 'Lock to the right' : 'Unlock';
|
|
9969
|
+
}
|
|
9867
9970
|
dialogTogglePin(i) {
|
|
9868
9971
|
const list = [...this.dialogCols()];
|
|
9869
9972
|
if (list[i].grouped || list[i].fixed)
|
|
9870
9973
|
return; // raggruppate e Fixed non sono pinnabili
|
|
9871
|
-
|
|
9974
|
+
const next = this.pinCycleNext(list[i]);
|
|
9975
|
+
list[i] = { ...list[i], pinned: next === 'left', pinnedRight: next === 'right' };
|
|
9872
9976
|
this.dialogCols.set(list);
|
|
9873
9977
|
}
|
|
9874
9978
|
dialogSetAll(visible) {
|
|
@@ -9987,10 +10091,12 @@ class EsTable2Component {
|
|
|
9987
10091
|
const orderIds = this.enforceGroupContiguity(this.dialogCols().map(c => c.id));
|
|
9988
10092
|
const hiddenSet = new Set(this.dialogCols().filter(c => !c.visible).map(c => c.id));
|
|
9989
10093
|
const pinnedSet = new Set(this.dialogCols().filter(c => c.pinned).map(c => c.id));
|
|
10094
|
+
const pinnedRightSet = new Set(this.dialogCols().filter(c => c.pinnedRight).map(c => c.id));
|
|
9990
10095
|
this.columnPrefs = {
|
|
9991
10096
|
order: orderIds,
|
|
9992
10097
|
hidden: [...hiddenSet],
|
|
9993
10098
|
pinned: [...pinnedSet],
|
|
10099
|
+
pinnedRight: [...pinnedRightSet],
|
|
9994
10100
|
widths: this.currentWidths(),
|
|
9995
10101
|
known: orderIds
|
|
9996
10102
|
};
|
|
@@ -10272,16 +10378,17 @@ class EsTable2Component {
|
|
|
10272
10378
|
order: cols.map(c => c.id),
|
|
10273
10379
|
hidden: cols.filter(c => !c.visible).map(c => c.id),
|
|
10274
10380
|
pinned: cols.filter(c => c.pinned).map(c => c.id),
|
|
10381
|
+
pinnedRight: cols.filter(c => c.pinnedRight).map(c => c.id),
|
|
10275
10382
|
widths
|
|
10276
10383
|
};
|
|
10277
10384
|
this.saveColumnPrefs();
|
|
10278
10385
|
}
|
|
10279
10386
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: EsTable2Component, deps: [{ token: i1.NgControl, optional: true, self: true }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i2.PreferencesService, optional: true }, { token: i0.Injector }, { token: i3.LocalizationService }, { token: EST2_DEFAULTS, optional: true }, { token: EST2_DEBUG, optional: true }, { token: EST2_EXPORT_GLOBAL_ACL, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10280
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: EsTable2Component, isStandalone: false, selector: "es-table2", inputs: { ContextMenu: { classPropertyName: "ContextMenu", publicName: "ContextMenu", isSignal: false, isRequired: false, transformFunction: null }, _Selection: { classPropertyName: "_Selection", publicName: "Selection", isSignal: true, isRequired: false, transformFunction: null }, SingleSelection: { classPropertyName: "SingleSelection", publicName: "SingleSelection", isSignal: true, isRequired: false, transformFunction: null }, SelectionDisabled: { classPropertyName: "SelectionDisabled", publicName: "SelectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, _SelectAll: { classPropertyName: "_SelectAll", publicName: "SelectAll", isSignal: true, isRequired: false, transformFunction: null }, SelectAllAtStart: { classPropertyName: "SelectAllAtStart", publicName: "SelectAllAtStart", isSignal: true, isRequired: false, transformFunction: null }, SelectionKey: { classPropertyName: "SelectionKey", publicName: "SelectionKey", isSignal: true, isRequired: false, transformFunction: null }, _UseSelectionCache: { classPropertyName: "_UseSelectionCache", publicName: "UseSelectionCache", isSignal: true, isRequired: false, transformFunction: null }, _ShiftClick: { classPropertyName: "_ShiftClick", publicName: "ShiftClick", isSignal: true, isRequired: false, transformFunction: null }, _OrderByColumn: { classPropertyName: "_OrderByColumn", publicName: "OrderByColumn", isSignal: true, isRequired: false, transformFunction: null }, MultipleOrderingDirectives: { classPropertyName: "MultipleOrderingDirectives", publicName: "MultipleOrderingDirectives", isSignal: true, isRequired: false, transformFunction: null }, Removal: { classPropertyName: "Removal", publicName: "Removal", isSignal: true, isRequired: false, transformFunction: null }, RemovalCondition: { classPropertyName: "RemovalCondition", publicName: "RemovalCondition", isSignal: true, isRequired: false, transformFunction: null }, RowClassAssigner: { classPropertyName: "RowClassAssigner", publicName: "RowClassAssigner", isSignal: false, isRequired: false, transformFunction: null }, _HidePaging: { classPropertyName: "_HidePaging", publicName: "HidePaging", isSignal: true, isRequired: false, transformFunction: null }, _HidePagingCount: { classPropertyName: "_HidePagingCount", publicName: "HidePagingCount", isSignal: true, isRequired: false, transformFunction: null }, _HidePagingButtons: { classPropertyName: "_HidePagingButtons", publicName: "HidePagingButtons", isSignal: true, isRequired: false, transformFunction: null }, _AllSearch: { classPropertyName: "_AllSearch", publicName: "AllSearch", isSignal: true, isRequired: false, transformFunction: null }, _PagingStyle: { classPropertyName: "_PagingStyle", publicName: "PagingStyle", isSignal: true, isRequired: false, transformFunction: null }, _ArraymodeItemsPerPage: { classPropertyName: "_ArraymodeItemsPerPage", publicName: "ArraymodeItemsPerPage", isSignal: true, isRequired: false, transformFunction: null }, _UseArrayModePaging: { classPropertyName: "_UseArrayModePaging", publicName: "UseArrayModePaging", isSignal: true, isRequired: false, transformFunction: null }, CountLabel: { classPropertyName: "CountLabel", publicName: "CountLabel", isSignal: true, isRequired: false, transformFunction: null }, Height: { classPropertyName: "Height", publicName: "Height", isSignal: true, isRequired: false, transformFunction: null }, MaxHeight: { classPropertyName: "MaxHeight", publicName: "MaxHeight", isSignal: true, isRequired: false, transformFunction: null }, VirtualScroll: { classPropertyName: "VirtualScroll", publicName: "VirtualScroll", isSignal: true, isRequired: false, transformFunction: null }, VirtualRowHeight: { classPropertyName: "VirtualRowHeight", publicName: "VirtualRowHeight", isSignal: true, isRequired: false, transformFunction: null }, EmptySpaceBackgroundColor: { classPropertyName: "EmptySpaceBackgroundColor", publicName: "EmptySpaceBackgroundColor", isSignal: true, isRequired: false, transformFunction: null }, HighCellDensity: { classPropertyName: "HighCellDensity", publicName: "HighCellDensity", isSignal: true, isRequired: false, transformFunction: null }, HeaderHidden: { classPropertyName: "HeaderHidden", publicName: "HeaderHidden", isSignal: true, isRequired: false, transformFunction: null }, BodyHidden: { classPropertyName: "BodyHidden", publicName: "BodyHidden", isSignal: true, isRequired: false, transformFunction: null }, ShowLoadingOnBootstrap: { classPropertyName: "ShowLoadingOnBootstrap", publicName: "ShowLoadingOnBootstrap", isSignal: true, isRequired: false, transformFunction: null }, _DefaultAlignment: { classPropertyName: "_DefaultAlignment", publicName: "DefaultAlignment", isSignal: true, isRequired: false, transformFunction: null }, _TableClass: { classPropertyName: "_TableClass", publicName: "TableClass", isSignal: true, isRequired: false, transformFunction: null }, _ContainerClass: { classPropertyName: "_ContainerClass", publicName: "ContainerClass", isSignal: true, isRequired: false, transformFunction: null }, EsTableHandledSearch: { classPropertyName: "EsTableHandledSearch", publicName: "EsTableHandledSearch", isSignal: true, isRequired: false, transformFunction: null }, SearchThrottle: { classPropertyName: "SearchThrottle", publicName: "SearchThrottle", isSignal: true, isRequired: false, transformFunction: null }, _ColumnsResizable: { classPropertyName: "_ColumnsResizable", publicName: "ColumnsResizable", isSignal: true, isRequired: false, transformFunction: null }, _ColumnsPinnable: { classPropertyName: "_ColumnsPinnable", publicName: "ColumnsPinnable", isSignal: true, isRequired: false, transformFunction: null }, _HiddenColumns: { classPropertyName: "_HiddenColumns", publicName: "HiddenColumns", isSignal: true, isRequired: false, transformFunction: null }, _ColumnsOrdering: { classPropertyName: "_ColumnsOrdering", publicName: "ColumnsOrdering", isSignal: true, isRequired: false, transformFunction: null }, _Export: { classPropertyName: "_Export", publicName: "Export", isSignal: true, isRequired: false, transformFunction: null }, XLSXExport: { classPropertyName: "XLSXExport", publicName: "XLSXExport", isSignal: true, isRequired: false, transformFunction: null }, CSVExport: { classPropertyName: "CSVExport", publicName: "CSVExport", isSignal: true, isRequired: false, transformFunction: null }, ExportFileName: { classPropertyName: "ExportFileName", publicName: "ExportFileName", isSignal: true, isRequired: false, transformFunction: null }, ExportOnlyVisibleColumns: { classPropertyName: "ExportOnlyVisibleColumns", publicName: "ExportOnlyVisibleColumns", isSignal: true, isRequired: false, transformFunction: null }, ExportFunction: { classPropertyName: "ExportFunction", publicName: "ExportFunction", isSignal: false, isRequired: false, transformFunction: null }, CornerMenuOptions: { classPropertyName: "CornerMenuOptions", publicName: "CornerMenuOptions", isSignal: true, isRequired: false, transformFunction: null }, DynamicOperations: { classPropertyName: "DynamicOperations", publicName: "DynamicOperations", isSignal: true, isRequired: false, transformFunction: null }, _DynamicRowColumnsDefinition: { classPropertyName: "_DynamicRowColumnsDefinition", publicName: "DynamicRowColumnsDefinition", isSignal: true, isRequired: false, transformFunction: null }, Hierarchy: { classPropertyName: "Hierarchy", publicName: "Hierarchy", isSignal: true, isRequired: false, transformFunction: null }, _ParentKey: { classPropertyName: "_ParentKey", publicName: "ParentKey", isSignal: true, isRequired: false, transformFunction: null }, _OwnKey: { classPropertyName: "_OwnKey", publicName: "OwnKey", isSignal: true, isRequired: false, transformFunction: null }, _AutoSortHierarchy: { classPropertyName: "_AutoSortHierarchy", publicName: "AutoSortHierarchy", isSignal: true, isRequired: false, transformFunction: null }, StartsExpanded: { classPropertyName: "StartsExpanded", publicName: "StartsExpanded", isSignal: true, isRequired: false, transformFunction: null }, CascadeSelection: { classPropertyName: "CascadeSelection", publicName: "CascadeSelection", isSignal: true, isRequired: false, transformFunction: null }, _SavePreferences: { classPropertyName: "_SavePreferences", publicName: "SavePreferences", isSignal: true, isRequired: false, transformFunction: null }, Name: { classPropertyName: "Name", publicName: "Name", isSignal: true, isRequired: false, transformFunction: null }, _RowGroupingPagingStyle: { classPropertyName: "_RowGroupingPagingStyle", publicName: "RowGroupingPagingStyle", isSignal: true, isRequired: false, transformFunction: null }, _ShowItemGroupsColumns: { classPropertyName: "_ShowItemGroupsColumns", publicName: "ShowItemGroupsColumns", isSignal: true, isRequired: false, transformFunction: null }, Editable: { classPropertyName: "Editable", publicName: "Editable", isSignal: true, isRequired: false, transformFunction: null }, RangeSelection: { classPropertyName: "RangeSelection", publicName: "RangeSelection", isSignal: true, isRequired: false, transformFunction: null }, ItemSourceProperty: { classPropertyName: "ItemSourceProperty", publicName: "ItemSourceProperty", isSignal: true, isRequired: false, transformFunction: null }, HasHeaderGroup: { classPropertyName: "HasHeaderGroup", publicName: "HasHeaderGroup", isSignal: true, isRequired: false, transformFunction: null }, HasSecondaryHeaderGroup: { classPropertyName: "HasSecondaryHeaderGroup", publicName: "HasSecondaryHeaderGroup", isSignal: true, isRequired: false, transformFunction: null }, SearchView: { classPropertyName: "SearchView", publicName: "SearchView", isSignal: true, isRequired: false, transformFunction: null }, _AutoUpdate: { classPropertyName: "_AutoUpdate", publicName: "AutoUpdate", isSignal: true, isRequired: false, transformFunction: null }, EsThTdProvider: { classPropertyName: "EsThTdProvider", publicName: "EsThTdProvider", isSignal: false, isRequired: false, transformFunction: null }, globalCheck: { classPropertyName: "globalCheck", publicName: "globalCheck", isSignal: true, isRequired: false, transformFunction: null }, autoUpdate: { classPropertyName: "autoUpdate", publicName: "autoUpdate", isSignal: true, isRequired: false, transformFunction: null }, seconds: { classPropertyName: "seconds", publicName: "seconds", isSignal: true, isRequired: false, transformFunction: null }, researchInProgress: { classPropertyName: "researchInProgress", publicName: "researchInProgress", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: false, isRequired: false, transformFunction: null }, AddedItems: { classPropertyName: "AddedItems", publicName: "AddedItems", isSignal: true, isRequired: false, transformFunction: null }, RemovedItems: { classPropertyName: "RemovedItems", publicName: "RemovedItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onOrderChanged: "onOrderChanged", onSearchRequest: "onSearchRequest", onSelectionChanged: "onSelectionChanged", onRemoval: "onRemoval", onAbortRemoval: "onAbortRemoval", onModelChange: "onModelChange", onOpenContextMenu: "onOpenContextMenu", onCornerAction: "onCornerAction", onDynamicOperation: "onDynamicOperation", globalCheck: "globalCheckChange", autoUpdate: "autoUpdateChange", seconds: "secondsChange", researchInProgress: "researchInProgressChange", AddedItems: "AddedItemsChange", RemovedItems: "RemovedItemsChange" }, host: { listeners: { "document:mouseup": "onResizeEnd()", "document:copy": "onDocCopy($event)", "document:paste": "onDocPaste($event)", "document:click": "onDocClick()", "window:resize": "onViewportResize()", "document:mousemove": "onResizeMove($event)" }, properties: { "class.est2": "this.hostClass", "class.est2--dense": "this.denseClass" } }, queries: [{ propertyName: "headerRef", first: true, predicate: ["header"], descendants: true }, { propertyName: "bodyRef", first: true, predicate: ["body"], descendants: true }, { propertyName: "thDirectives", predicate: EsThDirective }, { propertyName: "tdDirectives", predicate: EsTdDirective }, { propertyName: "editorDirectives", predicate: EsTdEditorDirective }, { propertyName: "thTdProviders", predicate: ThTdProvider }], viewQueries: [{ propertyName: "tableEmptyMenu", first: true, predicate: ["emptyMenu"], descendants: true, static: true }, { propertyName: "theadRef", first: true, predicate: ["theadRef"], descendants: true }, { propertyName: "scrollRefEl", first: true, predicate: ["scrollRef"], descendants: true }, { propertyName: "dialogBackdropRef", first: true, predicate: ["dialogBackdrop"], descendants: true }], ngImport: i0, template: "@if (view()) {\n <div class=\"est2-wrap {{ ContainerClass() }}\"\n [style.height]=\"Height()\"\n [style.background-color]=\"EmptySpaceBackgroundColor() || null\">\n\n <!-- Auto-aggiornamento: toggle + intervallo (in alto a destra) -->\n @if (AutoUpdate()) {\n <div class=\"est2-autoupdate\">\n <label class=\"est2-autoupdate__label\">\n {{ 'Update every' | localize : lc }}\n <input type=\"text\" maxlength=\"3\" class=\"est2-autoupdate__secs\"\n [ngModel]=\"seconds()\" (ngModelChange)=\"seconds.set($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"autoUpdateChanged()\" />\n {{ 'second/s' | localize : lc }}\n </label>\n <label class=\"est2-switch\" [title]=\"'Toggle auto-update' | localize : lc\">\n <input type=\"checkbox\" [ngModel]=\"autoUpdate()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"autoUpdate.set($event); autoUpdateChanged()\" />\n <span class=\"est2-switch__slider\"></span>\n </label>\n </div>\n }\n\n <!-- Pager superiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'top') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Riepilogo delle modifiche non ancora salvate. Sta nel flusso (non \u00E8 un\n overlay come la barra di selezione) perch\u00E9 deve restare leggibile anche\n quando l'utente \u00E8 su una pagina che non contiene le righe interessate. -->\n @if (pendingChangesOn() && hasPendingChanges) {\n <div class=\"est2-pendingbar\">\n @if (addedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--added\">\n <strong>{{ addedCount }}</strong>\n {{ (addedCount === 1 ? 'row added' : 'rows added') | localize : lc }}\n </span>\n }\n @if (removedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--removed\">\n <strong>{{ removedCount }}</strong>\n {{ (removedCount === 1 ? 'row to delete' : 'rows to delete') | localize : lc }}\n </span>\n }\n <span class=\"est2-pendingbar__spacer\"></span>\n @if (removedCount > 0) {\n <span class=\"est2-link\" (click)=\"restoreAllRemovals()\">{{ 'Undo the deletions' | localize : lc }}</span>\n }\n </div>\n }\n\n <!-- Host POSIZIONATO dello scroller: \u00E8 l'ancora di tutti gli overlay che devono\n coprire la TABELLA (barra di selezione, overlay di caricamento). Senza di esso\n erano ancorati a `.est2-wrap` e con [AutoUpdate] o il pager in alto finivano\n sopra quei controlli invece che sopra l'header. -->\n <div class=\"est2-scroll-host\">\n\n <!-- Barra \"seleziona tutto\" (visibile solo con selezione multipla attiva e righe presenti) -->\n @if (Selection() && !SingleSelection() && hasSelection) {\n <div class=\"est2-selectbar\"\n [class.est2-selectbar--inset]=\"chromeWidth() > 0\"\n [style.height.px]=\"selbarHeight() ? selbarHeight() + 1 : null\"\n [style.right.px]=\"chromeWidth() || null\">\n @if (allSelected) {\n <span>{{ 'All the' | localize : lc }} <strong>{{ selectedCount }}</strong> {{ 'elements are selected' | localize : lc }}</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ (selectedCount === 1 ? 'element selected' : 'elements selected') | localize : lc }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">{{ 'Select all' | localize : lc }} {{ totalCount() }} {{ 'elements' | localize : lc }}</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">{{ 'Reset selection' | localize : lc }}</span>\n </div>\n }\n\n <div class=\"est2-scroll\" #scrollRef [style.max-height.px]=\"MaxHeight()\" (scroll)=\"onScrollerScroll($event)\">\n <table class=\"est2-table {{ TableClass() }}\"\n [class.est2-table--range]=\"rangeActive()\"\n [class.est2-table--dragging]=\"rangeDragging()\"\n (mousedown)=\"onGridMouseDown($event)\"\n (mouseover)=\"onGridMouseOver($event)\">\n\n <!-- ================= HEADER ================= -->\n @if (!HeaderHidden()) {\n <thead #theadRef>\n <!-- Righe di header-group multi-livello (dall'alto verso il basso) -->\n @if (hasHeaderGroups()) {\n @for (grow of headerGroupRows(); track $index) {\n <tr class=\"est2-hgroup-row\">\n @if (Selection()) { <th class=\"est2-col-min est2-selcol\" [class.est2-pinned]=\"hasPinned()\" [style.left.px]=\"hasPinned() ? 0 : null\"></th> }\n @for (op of DynamicOperations(); track op.id) { <th class=\"est2-col-min\"></th> }\n @for (cell of grow; track cell.id; let gi = $index) {\n <th [attr.colspan]=\"cell.span\"\n [attr.data-groupid]=\"cell.isGroup ? cell.id : null\"\n [class.est2-hgroup]=\"cell.isGroup\"\n [class.est2-pinned]=\"gi < pinnedCount()\"\n [style.left.px]=\"gi < pinnedCount() ? pinnedLeftPx(gi) : null\"\n class=\"est2-hgroup-cell\">\n @if (cell.isGroup) {\n @if (cell.template) {\n <ng-container *ngTemplateOutlet=\"cell.template\"></ng-container>\n } @else {\n {{ cell.label }}\n }\n }\n </th>\n }\n @if (Removal()) { <th class=\"est2-col-min\"></th> }\n @if (hasChrome()) { <th class=\"est2-col-min est2-chrome-col\"></th> }\n </tr>\n }\n }\n <tr>\n <!-- Colonna di selezione -->\n @if (Selection()) {\n <th class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"globalCheck()\"\n [indeterminate]=\"selectionIndeterminate\"\n [disabled]=\"SelectionDisabled()\"\n (change)=\"toggleAll()\"\n [attr.aria-label]=\"'Select all rows' | localize : lc\" />\n }\n </th>\n }\n\n <!-- Header da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- intestazioni vuote per le operazioni dinamiche -->\n @for (op of DynamicOperations(); track op.id) {\n <th class=\"est2-col-min\"></th>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let ci = $index) {\n @let hs = colStyles()[ci];\n <th [attr.data-colid]=\"col.id\"\n [class]=\"col.headerClass\"\n [class.est2-th--orderable]=\"OrderByColumn() && col.orderable\"\n [class.est2-col-min]=\"col.header?.thShrink\"\n [class.est2-col-groupstart]=\"hs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [style.left.px]=\"hs.left\"\n [style.width.px]=\"hs.width\"\n [style.min-width.px]=\"hs.width\"\n [style.max-width.px]=\"hs.width\"\n [style.text-align]=\"col.alignment\"\n [style.background-color]=\"col.headerBg || null\"\n (click)=\"toggleSort(col)\">\n <span class=\"est2-th__inner\">\n @if (col.header?.Template) {\n <ng-container *ngTemplateOutlet=\"col.header!.Template!; context: col.multiProp != null ? { $implicit: col.headerText } : null\"></ng-container>\n } @else {\n {{ col.headerText }}\n }\n @if (OrderByColumn() && col.orderable && orderOf(col.id)) {\n <span class=\"est2-sort est2-sort--active\"\n [class.est2-sort--desc]=\"orderOf(col.id) === 'DESC'\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 15l6-6 6 6\"/></svg>\n </span>\n @if (orderIndex(col.id) > 0 && MultipleOrderingDirectives()) {\n <span class=\"est2-sort__badge\">{{ orderIndex(col.id) }}</span>\n }\n }\n <!-- Indicatore/toggle di pin (visibile se pinnata o all'hover) -->\n @if (ColumnsPinnable() && col.multiProp == null && !col.fixed) {\n <button type=\"button\" class=\"est2-pinbtn\"\n [class.est2-pinbtn--on]=\"col.pinned\"\n [title]=\"(col.pinned ? 'Unlock column' : 'Lock column to the left') | localize : lc\"\n (click)=\"togglePin(col, $event)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n </span>\n <!-- Handle di ridimensionamento (colonne pinnate, gated da ColumnsResizable) -->\n @if (col.pinned || ColumnsResizable()) {\n <span class=\"est2-resize-handle\" [title]=\"'Drag to resize' | localize : lc\"\n (mousedown)=\"startColumnResize(col, $event)\"\n (click)=\"$event.stopPropagation()\"></span>\n }\n </th>\n }\n }\n <!-- Header da template semplice -->\n @else if (headerRef) {\n <ng-container *ngTemplateOutlet=\"headerRef\"></ng-container>\n }\n\n <!-- Colonna rimozione -->\n @if (Removal()) { <th class=\"est2-col-min\"></th> }\n <!-- Colonna chrome (export / gestione colonne / menu) -->\n @if (hasChrome()) {\n <th class=\"est2-col-min est2-chrome-th est2-chrome-col\">\n <div class=\"est2-chrome\">\n <!-- I pannelli dei menu NON vivono qui: sono renderizzati a fondo\n componente e posizionati `fixed`, altrimenti l'`overflow:auto`\n di `.est2-scroll` li taglierebbe su tabelle basse. -->\n @if (Export()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Export' | localize : lc\" (click)=\"$event.stopPropagation(); toggleExportMenu($event)\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><path d=\"M7 10l5 5 5-5\"/><path d=\"M12 15V3\"/></svg>\n </button>\n }\n @if (HiddenColumns() || ColumnsOrdering()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Columns' | localize : lc\" (click)=\"$event.stopPropagation(); openColumnsDialog()\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\"/><path d=\"M9 3v18\"/><path d=\"M15 3v18\"/></svg>\n </button>\n }\n @if (CornerMenuOptions().length > 0) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Options' | localize : lc\" (click)=\"$event.stopPropagation(); toggleCornerMenu($event)\">\u22EE</button>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n\n <!-- ================= BODY ================= -->\n @if (!BodyHidden()) {\n <tbody>\n <!-- Valori invarianti per l'intero corpo: senza questi verrebbero\n ricalcolati riga\u00D7colonna volte a ogni ciclo di change-detection. -->\n @let rng = rangeActive();\n @let rect = rangeRect();\n @let errs = hasCellErrors();\n @let vw = virtualWindow();\n @if (vw && vw.padTop > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padTop\"></td></tr>\n }\n @for (item of renderedRows(); track trackRow($index, item); let vi = $index) {\n <!-- indice ASSOLUTO: range, editing e data-r ragionano su boundSource() -->\n @let ri = vStart() + vi;\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--zebra]=\"vw && (ri % 2 === 1)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted || item._removed\"\n [class.est2-row--added]=\"item._localId != null\"\n [class.est2-row--group]=\"item._group\"\n [class.est2-row--clickable]=\"Selection() || item._group\"\n [contextMenu]=\"ContextMenu || emptyMenu\"\n [contextMenuSubject]=\"item\"\n (click)=\"handleRowClick(item, $event)\">\n\n <!-- Cella di selezione -->\n @if (Selection()) {\n <td class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (item._group) {\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"groupSelectionState(item) === 'all'\"\n [indeterminate]=\"groupSelectionState(item) === 'some'\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection(item)\"\n [attr.aria-label]=\"'Select group' | localize : lc\" />\n }\n } @else {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"item._selected\"\n [indeterminate]=\"hierarchyIndeterminate(item)\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(item)\"\n [attr.aria-label]=\"'Select row' | localize : lc\" />\n }\n </td>\n }\n\n <!-- Celle da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- Operazioni dinamiche (icone a sinistra) -->\n @for (op of DynamicOperations(); track op.id) {\n <td class=\"est2-col-min est2-op-cell\">\n @if (!item._group && operationVisible(op, item)) {\n <span class=\"est2-op\" [class]=\"op.iconClass || ''\" [title]=\"op.title\"\n (click)=\"$event.stopPropagation(); dynamicOperation(item, op.id)\">{{ op.text }}</span>\n }\n </td>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let first = $first, ci = $index) {\n @let cs = colStyles()[ci];\n @let insel = rng && inRange(ri, ci);\n @let editing = isEditing(ri, ci);\n <td [class]=\"col.cssClass\"\n [style.text-align]=\"col.alignment\"\n [style.color]=\"cellColor(item, col, 'fore')\"\n [style.background-color]=\"cellColor(item, col, 'back')\"\n [class.est2-nowrap]=\"!col.wrap\"\n [class.est2-col-groupstart]=\"cs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-fixedw]=\"cs.width != null\"\n [style.left.px]=\"cs.left\"\n [style.width.px]=\"cs.width\"\n [style.min-width.px]=\"cs.width\"\n [style.max-width.px]=\"cs.width\"\n [class.est2-td--group-key]=\"item._group && item.column === col.id\"\n [attr.data-r]=\"rng && !item._group ? ri : null\"\n [attr.data-c]=\"rng && !item._group ? ci : null\"\n [class.est2-cell-sel]=\"insel\"\n [class.est2-cell-sel-t]=\"insel && ri === rect!.top\"\n [class.est2-cell-sel-b]=\"insel && ri === rect!.bottom\"\n [class.est2-cell-sel-l]=\"insel && ci === rect!.left\"\n [class.est2-cell-sel-r]=\"insel && ci === rect!.right\"\n [class.est2-cell-editing]=\"editing\"\n [class.est2-cell-invalid]=\"errs && !!cellError(item, col)\"\n [attr.data-error]=\"errs ? (cellError(item, col) || null) : null\"\n (dblclick)=\"onCellDblClick(item, col, ri, ci)\">\n @if (editing) {\n @if (editorFor(col); as edTpl) {\n <ng-container *ngTemplateOutlet=\"edTpl; context: editorContext(item, col)\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"defaultEditor; context: { $implicit: item, col: col }\"></ng-container>\n }\n } @else if (item._group) {\n @if (item.column === col.id) {\n <span class=\"est2-group-key\" [style.padding-left.px]=\"groupIndent(item)\">\n <span class=\"est2-group-chevron\" [class.est2-group-chevron--open]=\"item._expanded\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n {{ groupCellDisplay(item, col) }}\n </span>\n } @else {\n {{ groupCellDisplay(item, col) }}\n }\n } @else {\n <!-- Navigatore albero nella prima colonna -->\n @if (Hierarchy() && first) {\n <span class=\"est2-hier-lead\" [style.padding-left.px]=\"hierarchyIndent(item)\">\n @if (item.parent) {\n <span class=\"est2-group-chevron est2-hier-toggle\" [class.est2-group-chevron--open]=\"item._expanded\"\n (click)=\"$event.stopPropagation(); toggleHierarchyNode(item)\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-hier-toggle\"></span>\n }\n </span>\n }\n @if (itemCellHidden(col)) {\n <!-- colonna di gruppo nascosta sulla riga-oggetto -->\n } @else if (col.multiProp != null) {\n @if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: multiCell(item, col) }\"></ng-container>\n } @else {\n {{ multiCell(item, col)?.value }}\n }\n } @else if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: item }\"></ng-container>\n } @else if (col.routePath) {\n <a class=\"est2-link\" [routerLink]=\"routerLinkFor(item, col)\">{{ cellValue(item, col) }}</a>\n } @else if (col.propAccessor != null) {\n {{ reportCellDisplay(item, col) }}\n } @else if (col.type === 'enum') {\n {{ (cellValue(item, col) | est2_lookup : col.source).description }}\n } @else {\n {{ cellValue(item, col) | est2_format : col.type : col.format : locale }}\n }\n }\n </td>\n }\n }\n <!-- Celle da template semplice -->\n @else if (bodyRef) {\n <ng-container *ngTemplateOutlet=\"bodyRef; context: { $implicit: item }\"></ng-container>\n }\n\n <!-- Rimozione (non sulle righe-gruppo) -->\n @if (Removal()) {\n <td class=\"est2-col-min\">\n @if (!item._group && canRemove(item)) {\n @if (item.removed || item.deleted || item._removed) {\n <button type=\"button\" class=\"est2-rowaction\" [title]=\"'Restore' | localize : lc\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" [title]=\"'Remove' | localize : lc\" (click)=\"removeItem(item)\">\u2715</button>\n }\n }\n </td>\n }\n <!-- Chrome -->\n @if (hasChrome()) { <td class=\"est2-col-min est2-chrome-col\"></td> }\n </tr>\n }\n } @empty {\n <tr>\n <td class=\"est2-empty\" [attr.colspan]=\"totalColspan()\">{{ 'No elements to display' | localize : lc }}</td>\n </tr>\n }\n @if (vw && vw.padBottom > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padBottom\"></td></tr>\n }\n </tbody>\n }\n </table>\n </div>\n\n <!-- Overlay di caricamento: FUORI dallo scroller, cos\u00EC copre il viewport della\n tabella senza scorrere col contenuto e senza essere bucato dalle celle sticky -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>{{ 'Loading\u2026' | localize : lc }}</span>\n </div>\n }\n\n </div>\n\n <!-- Pager inferiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Menu del chrome (export / opzioni). Renderizzati FUORI da `.est2-scroll` e\n posizionati `fixed` sulle coordinate del bottone che li ha aperti: cos\u00EC non\n vengono tagliati dall'`overflow:auto` dello scroller su tabelle basse, e\n restano dentro `.est2` (i token `--est-*` continuano a ereditare). -->\n @if (exportMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @if (CSVExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">{{ 'Export Excel (XLSX)' | localize : lc }}</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n </div>\n }\n @if (cornerMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @for (opt of CornerMenuOptions(); track opt.id) {\n <button type=\"button\" class=\"est2-menu-item\" (click)=\"cornerAction(opt.id); closeChromeMenus()\">{{ opt.description }}</button>\n }\n </div>\n }\n\n <!-- Avviso transitorio (es. incolla con dimensioni incompatibili) -->\n @if (notice()) {\n <div class=\"est2-toast\" role=\"alert\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/></svg>\n <span>{{ notice() }}</span>\n </div>\n }\n\n <!-- Dialog visibilit\u00E0 / ordine colonne -->\n @if (columnsDialogOpen()) {\n <div class=\"est2-dialog-backdrop\" #dialogBackdrop\n [class.est2-dialog-backdrop--embedded]=\"embedded\"\n (click)=\"closeColumnsDialog()\">\n <div class=\"est2-dialog\" (click)=\"$event.stopPropagation()\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"est2-dialog__head\">\n <span>\n @if (HiddenColumns() && ColumnsOrdering()) { {{ 'Columns visibility and order' | localize : lc }} }\n @else if (HiddenColumns()) { {{ 'Columns visibility' | localize : lc }} }\n @else { {{ 'Columns order' | localize : lc }} }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" [attr.aria-label]=\"'Close' | localize : lc\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">{{ 'Show all' | localize : lc }}</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">{{ 'Hide all' | localize : lc }}</button>\n </div>\n }\n\n <ul class=\"est2-collist\">\n @for (c of dialogCols(); track c.id; let i = $index) {\n <li class=\"est2-collist__row\"\n [attr.draggable]=\"ColumnsOrdering() && canReorder(c) ? true : null\"\n [class.est2-collist__row--drag]=\"ColumnsOrdering() && canReorder(c)\"\n [class.est2-collist__row--locked]=\"!canReorder(c)\"\n [class.est2-collist__row--dragging]=\"dragIndex() === i\"\n [class.est2-collist__row--drop-above]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! > i\"\n [class.est2-collist__row--drop-below]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! < i\"\n (dragstart)=\"dialogDragStart(i, $event)\"\n (dragover)=\"dialogDragOver(i, $event)\"\n (drop)=\"dialogDrop(i, $event)\"\n (dragend)=\"dialogDragEnd()\">\n @if (ColumnsOrdering()) {\n @if (canReorder(c)) {\n <span class=\"est2-collist__grip\" [title]=\"'Drag to reorder' | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"currentColor\"><circle cx=\"9\" cy=\"6\" r=\"1.6\"/><circle cx=\"15\" cy=\"6\" r=\"1.6\"/><circle cx=\"9\" cy=\"12\" r=\"1.6\"/><circle cx=\"15\" cy=\"12\" r=\"1.6\"/><circle cx=\"9\" cy=\"18\" r=\"1.6\"/><circle cx=\"15\" cy=\"18\" r=\"1.6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-collist__grip est2-collist__grip--locked\"\n [title]=\"(c.fixed ? 'Fixed column: cannot be moved' : 'Column locked to the left: cannot be moved') | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"4\" y=\"11\" width=\"16\" height=\"10\" rx=\"2\"/><path d=\"M8 11V7a4 4 0 0 1 8 0v4\"/></svg>\n </span>\n }\n }\n @if (HiddenColumns()) {\n <label class=\"est2-collist__vis\" [title]=\"c.fixed ? ('Fixed column: always visible' | localize : lc) : ''\">\n <input type=\"checkbox\" class=\"est2-check\" [checked]=\"c.visible\" [disabled]=\"c.fixed\" (change)=\"dialogToggle(i)\" />\n </label>\n }\n <span class=\"est2-collist__label\">\n @if (c.tmpl) {\n <!-- colonna non renderizzata (Visible:false / nascosta): il testo\n header non esiste nel DOM, quindi rendo il template dell'`*th` -->\n @if (c.prefix) { <span class=\"est2-collist__path\">{{ c.prefix }}</span> }\n <ng-container *ngTemplateOutlet=\"c.tmpl; context: c.tmplCtx\"></ng-container>\n } @else {\n {{ c.label }}\n }\n </span>\n @if (ColumnsPinnable()) {\n <button type=\"button\" class=\"est2-iconbtn est2-collist__pin\" [class.est2-collist__pin--on]=\"c.pinned\"\n [disabled]=\"c.grouped || c.fixed\"\n [title]=\"(c.fixed ? 'Fixed columns cannot be locked' : (c.grouped ? 'Grouped columns cannot be locked' : (c.pinned ? 'Unlock' : 'Lock to the left'))) | localize : lc\"\n (click)=\"dialogTogglePin(i)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n @if (ColumnsOrdering()) {\n <span class=\"est2-collist__ord\">\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Up' | localize : lc\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Down' | localize : lc\" (click)=\"dialogMove(i, 1)\"\n [disabled]=\"i === dialogCols().length - 1 || !canReorder(c) || !canReorder(dialogCols()[i + 1])\">\u2193</button>\n </span>\n }\n </li>\n }\n </ul>\n\n <div class=\"est2-dialog__foot\">\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"resetColumnsDialog()\"\n [title]=\"'Restore the declared columns and delete the saved preferences for this table' | localize : lc\">{{ 'Reset' | localize : lc }}</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">{{ 'Cancel' | localize : lc }}</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">{{ 'Apply' | localize : lc }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n}\n\n<!-- Template pager riutilizzabile sopra/sotto -->\n<ng-template #pager>\n @if (!HidePaging() && !grouped()) {\n <es-table2-pager\n [page]=\"currentPage()\"\n [pages]=\"totalPages()\"\n [total]=\"totalCount()\"\n [locale]=\"locale\"\n [itemsPerPage]=\"viewMode() ? (view()?.itemsperpageoverride ?? 15) : ArraymodeItemsPerPage()\"\n [countLabel]=\"CountLabel()\"\n [showCount]=\"!HidePagingCount()\"\n [showButtons]=\"!HidePagingButtons()\"\n [showPagingOptions]=\"!HidePagingButtons()\"\n [allowAll]=\"AllSearch()\"\n (pageChange)=\"goToPage($event)\"\n (itemsPerPageChange)=\"changeItemsPerPage($event)\">\n </es-table2-pager>\n }\n</ng-template>\n\n<!-- Editor di cella di default (usato quando il consumer non fornisce un `*editor`) -->\n<ng-template #defaultEditor let-item let-col=\"col\">\n @switch (col.type) {\n @case ('enum') {\n <select class=\"est2-editor-input\"\n [value]=\"editDraft\"\n (change)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\">\n @for (o of col.source || []; track o.id) {\n <option [value]=\"o.id\" [selected]=\"o.id == editDraft\">{{ o.description }}</option>\n }\n </select>\n }\n @case ('boolean') {\n <input type=\"checkbox\" class=\"est2-editor-input est2-check\"\n [checked]=\"editDraft === true || editDraft === 'true'\"\n (change)=\"editDraft = $any($event.target).checked; commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\" />\n }\n @default {\n <input class=\"est2-editor-input\"\n [type]=\"editorInputType(col.type)\"\n [value]=\"editDraft\"\n (input)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\" />\n }\n }\n</ng-template>\n\n<!-- Menu di default (nessuna operazione) usato quando il consumer non passa [ContextMenu] -->\n<context-menu #emptyMenu>\n <ng-template contextMenuItem [passive]=\"true\"><em>{{ 'No operations available\u2026' | localize : lc }}</em></ng-template>\n</context-menu>\n", styles: [".est2{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent);--est-radius: 10px;--est-radius-sm: 6px;--est-radius-pill: 999px;--est-gap: 8px;--est-cell-py: 6px;--est-cell-px: 12px;--est-row-h: 33px;--est-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--est-fs: 13px;--est-fs-sm: 12px;--est-fw-head: 700;--est-transition: .14s cubic-bezier(.4, 0, .2, 1);--est-bg-zebra: color-mix(in srgb, var(--est-fg) 3.5%, var(--est-bg));--est-z-menu: 1000;--est-z-modal: 1010;--est-z-toast: 1020;font-family:var(--est-font);font-size:var(--est-fs);color:var(--est-fg);position:relative;display:block}.est2.est2--dark{--est-bg: #14161c;--est-bg-subtle: #1a1d25;--est-bg-raised: #1e222b;--est-bg-hover: #232733;--est-bg-selected: color-mix(in srgb, var(--est-accent) 26%, transparent);--est-fg: #e7eaf0;--est-fg-muted: #9aa3b2;--est-fg-faint: #6b7484;--est-border: #2a2f3a;--est-border-strong: #39404d;--est-accent: #7c74ff;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 22%, transparent);--est-danger: #f87171;--est-warning: #fbbf24;--est-success: #34d399;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 6px 16px -10px rgba(0, 0, 0, .7);--est-shadow-pop: 0 10px 28px -8px rgba(0, 0, 0, .6), 0 2px 6px -2px rgba(0, 0, 0, .5);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 55%, transparent)}.est2.est2--light{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent)}.est2.est2--dense{--est-cell-py: 3px;--est-cell-px: 9px;--est-row-h: 27px;--est-fs: 12.5px}.est2 *,.est2 *:before,.est2 *:after{box-sizing:border-box}.est2 .est2-scroll{position:relative;width:100%;overflow:auto;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg);-webkit-overflow-scrolling:touch}.est2 table.est2-table{width:100%;border-collapse:separate;border-spacing:0;background:var(--est-bg)}.est2 thead th{position:sticky;top:0;z-index:3;background:var(--est-bg-subtle);color:var(--est-fg);font-weight:var(--est-fw-head);font-size:var(--est-fs);text-align:left;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);box-shadow:var(--est-shadow-sticky);-webkit-user-select:none;user-select:none}.est2 thead tr.est2-hgroup-row th{font-size:var(--est-fs-sm);font-weight:700;color:var(--est-fg-muted);text-align:center;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);background:var(--est-bg-subtle);border-bottom:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th.est2-hgroup{color:var(--est-fg);border-left:1px solid var(--est-border);border-right:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th:not(.est2-hgroup){background:var(--est-bg-subtle);border-bottom-color:transparent}.est2 thead tr:last-child th.est2-col-groupstart,.est2 tbody td.est2-col-groupstart{border-left:1px solid var(--est-border)}.est2 tbody td{padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);color:var(--est-fg);vertical-align:middle;background:transparent}.est2 tbody tr:nth-child(2n) td{background:var(--est-bg-zebra)}.est2 tbody tr:nth-child(2n) td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-row--zebra td,.est2 tbody tr.est2-row--zebra td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-vspacer td{padding:0;border:none;background:transparent}.est2 tbody tr.est2-vspacer:hover td{background:transparent}.est2 th.est2-pinned,.est2 td.est2-pinned{position:sticky;background:var(--est-bg);box-shadow:1px 0 0 var(--est-border)}.est2 .est2-resize-handle{position:absolute;top:0;right:-3px;width:10px;height:100%;cursor:col-resize;z-index:2;touch-action:none;-webkit-user-select:none;user-select:none}.est2 .est2-resize-handle:after{content:\"\";position:absolute;top:28%;bottom:28%;right:3px;width:2px;border-radius:2px;background:color-mix(in srgb,var(--est-fg) 30%,transparent);transition:background var(--est-transition),top var(--est-transition),bottom var(--est-transition)}.est2 .est2-resize-handle:hover:after{background:var(--est-accent);top:15%;bottom:15%}.est2 .est2-resize-handle:active:after{background:var(--est-accent);top:6%;bottom:6%}.est2 td.est2-fixedw{overflow:hidden;text-overflow:ellipsis}.est2 td.est2-cell-invalid{background:color-mix(in srgb,var(--est-danger) 10%,var(--est-bg));box-shadow:inset 0 0 0 1px var(--est-danger);color:var(--est-danger)}.est2 .est2-selcol{width:44px;min-width:44px;max-width:44px}.est2 td.est2-pinned{z-index:3}.est2 thead th.est2-pinned,.est2 thead tr.est2-hgroup-row th.est2-pinned{z-index:6;background:var(--est-bg-subtle)}.est2 tbody tr:hover td.est2-pinned{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-pinned{background:var(--est-bg-subtle)}.est2 th.est2-chrome-col,.est2 td.est2-chrome-col{position:sticky;right:0;box-shadow:-1px 0 0 var(--est-border)}.est2 thead th.est2-chrome-col{z-index:16;background:var(--est-bg-subtle)}.est2 td.est2-chrome-col{z-index:4;background:var(--est-bg)}.est2 tbody tr:nth-child(2n) td.est2-chrome-col{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-chrome-col{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-chrome-col{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-chrome-col{background:var(--est-bg-subtle)}.est2 .est2-pinbtn{display:inline-flex;align-items:center;justify-content:center;margin-left:4px;padding:2px;border:none;background:transparent;color:var(--est-fg-faint);border-radius:var(--est-radius-sm);cursor:pointer;opacity:0;transition:opacity var(--est-transition),color var(--est-transition),background var(--est-transition)}.est2 thead th:hover .est2-pinbtn{opacity:.7}.est2 .est2-pinbtn:hover{background:var(--est-bg-hover);color:var(--est-fg);opacity:1}.est2 .est2-pinbtn--on{opacity:1;color:var(--est-accent);transform:rotate(0)}.est2 thead th:hover .est2-pinbtn--on{opacity:1}.est2 .est2-collist__pin.est2-collist__pin--on{color:var(--est-accent);border-color:var(--est-accent)}.est2 tbody tr{height:var(--est-row-h);transition:background var(--est-transition)}.est2 tbody tr:last-child td{border-bottom:none}.est2 tbody tr:hover td{background:var(--est-bg-hover)}.est2 tbody tr.est2-row--selected td{background:var(--est-bg-selected)}.est2 tbody tr.est2-row--clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.est2 tbody tr.est2-row--added td{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 tbody tr.est2-row--added td:first-child{box-shadow:inset 3px 0 0 var(--est-accent)}.est2 tbody tr.est2-row--added td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 .est2-pendingbar{display:flex;align-items:center;gap:10px;padding:7px 12px;margin-bottom:8px;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg-subtle);font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-pendingbar__spacer{flex:1 1 auto}.est2 .est2-pendingbar__chip{display:inline-flex;align-items:center;gap:5px;padding:2px 10px;border-radius:var(--est-radius-pill)}.est2 .est2-pendingbar__chip strong{color:var(--est-fg)}.est2 .est2-pendingbar__chip--added{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 .est2-pendingbar__chip--removed{background:color-mix(in srgb,var(--est-danger) 14%,transparent)}.est2 tbody tr.est2-row--removed td{text-decoration:line-through;color:var(--est-fg-faint)}.est2 .est2-table--range tbody td[data-r]{cursor:cell}.est2 .est2-table--dragging,.est2 .est2-table--dragging tbody td{-webkit-user-select:none;user-select:none}.est2 tbody td.est2-cell-sel{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 tbody td.est2-cell-sel-t{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l{box-shadow:inset 2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-r{box-shadow:inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b{box-shadow:inset 0 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 0 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px -2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-editing{padding:2px 6px}.est2 .est2-editor-input{width:100%;box-sizing:border-box;height:calc(var(--est-row-h) - 8px);padding:2px 6px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1.5px solid var(--est-accent);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-editor-input:focus-visible{box-shadow:var(--est-ring)}.est2 input.est2-editor-input[type=checkbox]{width:16px;height:16px}.est2 .est2-autoupdate{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:4px 2px 8px;font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-autoupdate__label{display:inline-flex;align-items:center;gap:6px}.est2 .est2-autoupdate__secs{width:44px;text-align:center;padding:3px 4px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-autoupdate__secs:focus-visible{box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2 .est2-switch{position:relative;display:inline-flex;width:38px;height:20px;cursor:pointer}.est2 .est2-switch input{position:absolute;opacity:0;width:0;height:0}.est2 .est2-switch__slider{flex:1;border-radius:var(--est-radius-pill);background:var(--est-border-strong);transition:background var(--est-transition)}.est2 .est2-switch__slider:before{content:\"\";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px #00000040;transition:transform var(--est-transition)}.est2 .est2-switch input:checked+.est2-switch__slider{background:var(--est-accent)}.est2 .est2-switch input:checked+.est2-switch__slider:before{transform:translate(18px)}.est2 .est2-switch input:focus-visible+.est2-switch__slider{box-shadow:var(--est-ring)}.est2 .est2-toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%);z-index:var(--est-z-toast);pointer-events:none;display:inline-flex;align-items:center;gap:8px;max-width:min(560px,100vw - 24px);padding:8px 14px;font-size:13px;font-weight:500;color:#fff;background:#b91c1c;border-radius:var(--est-radius);box-shadow:0 6px 20px #00000040;animation:est2-toast-in .16s ease-out}.est2 .est2-toast svg{flex:0 0 auto}@keyframes est2-toast-in{0%{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%)}}@media(prefers-reduced-motion:reduce){.est2 .est2-toast{animation:none}}.est2 tbody tr.est2-row--group{cursor:pointer;-webkit-user-select:none;user-select:none}.est2 tbody tr.est2-row--group td{background:var(--est-bg-subtle);font-weight:600;color:var(--est-fg);border-bottom:1px solid var(--est-border)}.est2 tbody tr.est2-row--group:hover td{background:var(--est-bg-hover)}.est2 .est2-td--group-key{color:var(--est-fg)}.est2 .est2-group-key{display:inline-flex;align-items:center;gap:6px}.est2 .est2-group-chevron{display:inline-flex;color:var(--est-fg-muted);transition:transform var(--est-transition)}.est2 .est2-group-chevron--open{transform:rotate(90deg)}.est2 .est2-hier-lead{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px}.est2 .est2-hier-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex:none}.est2 .est2-group-chevron.est2-hier-toggle{cursor:pointer;border-radius:var(--est-radius-sm);transition:transform var(--est-transition),background var(--est-transition),color var(--est-transition)}.est2 .est2-group-chevron.est2-hier-toggle:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 th.est2-th--orderable{cursor:pointer;transition:color var(--est-transition)}.est2 th.est2-th--orderable:hover{color:var(--est-fg)}.est2 .est2-th__inner{display:inline-flex;align-items:center;gap:6px}.est2 .est2-sort{display:inline-flex;width:14px;height:14px;opacity:.35;transition:opacity var(--est-transition),transform var(--est-transition)}.est2 .est2-sort--active{opacity:1;color:var(--est-accent)}.est2 .est2-sort--desc{transform:rotate(180deg)}.est2 .est2-sort__badge{font-size:9px;font-weight:700;color:var(--est-accent);margin-left:2px}.est2 .est2-check{appearance:none;width:16px;height:16px;border:1.5px solid var(--est-border-strong);border-radius:var(--est-radius-sm);background:var(--est-bg);cursor:pointer;position:relative;transition:border-color var(--est-transition),background var(--est-transition);vertical-align:middle;flex:none}.est2 .est2-check:hover{border-color:var(--est-accent)}.est2 .est2-check:checked{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:checked:after{content:\"\";position:absolute;left:4.5px;top:1.5px;width:4px;height:8px;border:solid var(--est-accent-fg);border-width:0 2px 2px 0;transform:rotate(45deg)}.est2 .est2-check:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2 .est2-check:disabled{opacity:.45;cursor:not-allowed}.est2 .est2-check:disabled:hover{border-color:var(--est-border-strong)}.est2 .est2-check:indeterminate{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--est-accent-fg);transform:none;border:none}.est2 th.est2-col-min,.est2 td.est2-col-min{width:1%;white-space:nowrap}.est2 .est2-wrap,.est2 .est2-scroll-host{position:relative}.est2 .est2-selectbar{position:absolute;top:0;left:0;right:0;z-index:15;display:flex;align-items:center;gap:12px;padding:9px 14px;font-size:var(--est-fs-sm);color:var(--est-fg);background:var(--est-bg-subtle);border:none;border-radius:var(--est-radius) var(--est-radius) 0 0;box-shadow:inset 3px 0 0 var(--est-accent)}.est2 .est2-selectbar--inset{border-top-right-radius:0}.est2 .est2-selectbar strong{color:var(--est-fg);font-weight:700}.est2 .est2-selectbar__spacer{flex:1 1 auto}.est2 .est2-link{color:var(--est-accent);cursor:pointer;font-weight:600}.est2 .est2-link:hover{text-decoration:underline}.est2 .est2-rowaction{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);border:none;background:transparent;color:var(--est-fg-faint);cursor:pointer;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-rowaction:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-rowaction--danger:hover{color:var(--est-danger)}.est2 .est2-op-cell{text-align:center}.est2 .est2-op{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;padding:0 6px;border-radius:var(--est-radius-sm);color:var(--est-accent);cursor:pointer;font-size:var(--est-fs-sm);transition:background var(--est-transition)}.est2 .est2-op:hover{background:var(--est-accent-weak)}.est2 .est2-loading{position:absolute;inset:0;border-radius:var(--est-radius);z-index:20;display:flex;align-items:center;justify-content:center;gap:10px;background:color-mix(in srgb,var(--est-bg) 70%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);color:var(--est-fg-muted);font-size:var(--est-fs-sm)}.est2 .est2-spinner{width:16px;height:16px;border:2px solid var(--est-border-strong);border-top-color:var(--est-accent);border-radius:50%;animation:est2-spin .7s linear infinite}@keyframes est2-spin{to{transform:rotate(360deg)}}.est2 .est2-nowrap{white-space:nowrap}.est2 .est2-cornermenu{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);cursor:pointer;color:var(--est-fg-muted);transition:background var(--est-transition)}.est2 .est2-cornermenu:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-chrome-th{position:relative}.est2 .est2-chrome{display:inline-flex;align-items:center;gap:2px}.est2 .est2-chrome-wrap{position:relative;display:inline-flex}.est2 .est2-chrome-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:none;background:transparent;border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer;font-size:16px;line-height:1;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-chrome-btn:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-menu{position:fixed;z-index:var(--est-z-menu);min-width:180px;padding:4px;background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 8px 24px #0000002e;display:flex;flex-direction:column}.est2 .est2-menu-item{display:block;width:100%;padding:8px 10px;border:none;background:transparent;text-align:left;font:inherit;color:var(--est-fg);border-radius:var(--est-radius-sm);cursor:pointer}.est2 .est2-menu-item:hover{background:var(--est-bg-hover)}.est2 .est2-dialog-backdrop{position:fixed;inset:0;z-index:var(--est-z-modal);display:flex;align-items:center;justify-content:center;padding:16px}.est2 .est2-dialog-backdrop:before{content:\"\";position:absolute;inset:0;background-color:#000;opacity:.5}.est2 .est2-dialog{position:relative}.est2 .est2-dialog-backdrop--embedded{--est-embed-offset: 110px;padding-top:5%;margin-left:calc(-1 * var(--est-embed-offset));width:calc(100% + var(--est-embed-offset));overflow:hidden}.est2 .est2-dialog-backdrop--embedded .est2-dialog{max-height:600px}.est2 .est2-dialog{width:520px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;background:var(--est-bg);color:var(--est-fg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 16px 48px #0000004d;overflow:hidden}.est2 .est2-dialog__head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;font-weight:700;border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__close{border:none;background:transparent;cursor:pointer;color:var(--est-fg-muted);font-size:15px;line-height:1;padding:4px;border-radius:var(--est-radius-sm)}.est2 .est2-dialog__close:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-dialog__tools{padding:8px 14px;font-size:var(--est-fs-sm);border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__tools .est2-link{border:none;background:none;padding:0;font:inherit;font-weight:600}.est2 .est2-dialog__sep{margin:0 6px;color:var(--est-fg-faint)}.est2 .est2-collist{list-style:none;margin:0;padding:6px;overflow-y:auto;flex:1 1 auto;min-height:0}.est2 .est2-collist__row{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--est-radius-sm)}.est2 .est2-collist__row:hover{background:var(--est-bg-hover)}.est2 .est2-collist__row--drag{cursor:grab}.est2 .est2-collist__row--drag:active{cursor:grabbing}.est2 .est2-collist__row--dragging{opacity:.45;background:var(--est-bg-hover)}.est2 .est2-collist__row--drop-above{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 .est2-collist__row--drop-below{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 .est2-collist__grip{display:inline-flex;align-items:center;justify-content:center;color:var(--est-fg-faint);cursor:grab;flex:0 0 auto}.est2 .est2-collist__grip:active{cursor:grabbing}.est2 .est2-collist__row--drag:hover .est2-collist__grip{color:var(--est-fg-muted)}.est2 .est2-collist__grip--locked{color:var(--est-fg-faint);opacity:.55;cursor:default}.est2 .est2-collist__row--locked .est2-collist__label{color:var(--est-fg-muted)}.est2 .est2-collist__vis{display:inline-flex}.est2 .est2-collist__label{flex:1 1 auto}.est2 .est2-collist__path{color:var(--est-fg-faint);margin-right:4px}.est2 .est2-collist__ord{display:inline-flex;gap:4px}.est2 .est2-iconbtn{width:26px;height:26px;border:1px solid var(--est-border);background:var(--est-bg);border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer}.est2 .est2-iconbtn:hover:not(:disabled){background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-iconbtn:disabled{opacity:.4;cursor:default}.est2 .est2-dialog__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid var(--est-border)}.est2 .est2-dialog__spacer{flex:1 1 auto}.est2 .est2-btn{padding:7px 14px;border-radius:var(--est-radius-sm);font:inherit;font-weight:600;cursor:pointer;border:1px solid transparent}.est2 .est2-btn--ghost{background:transparent;color:var(--est-fg);border-color:var(--est-border)}.est2 .est2-btn--ghost:hover{background:var(--est-bg-hover)}.est2 .est2-btn--primary{background:var(--est-accent);color:var(--est-accent-fg)}.est2 .est2-btn--primary:hover{filter:brightness(1.05)}.est2 .est2-empty{padding:48px 16px;text-align:center;color:var(--est-fg-faint);font-size:var(--est-fs-sm)}.ngx-contextmenu{--ctx-bg: #ffffff;--ctx-fg: #1a1d24;--ctx-muted: #626b7a;--ctx-border: #e6e9ef;--ctx-hover: #f2f4f7;--ctx-accent: #4f46e5;--ctx-shadow: 0 10px 28px -8px rgba(16, 24, 40, .22), 0 2px 8px -3px rgba(16, 24, 40, .14);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.ngx-contextmenu .dropdown-menu{display:block;min-width:200px;margin:0;padding:6px;list-style:none;background:var(--ctx-bg);border:1px solid var(--ctx-border);border-radius:12px;box-shadow:var(--ctx-shadow);animation:est2-ctx-in .12s cubic-bezier(.16,1,.3,1)}.ngx-contextmenu li{list-style:none;margin:0}.ngx-contextmenu li>a{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:7px;color:var(--ctx-fg);font-size:13.5px;line-height:1.2;text-decoration:none;cursor:pointer;white-space:nowrap;transition:background .12s ease,color .12s ease}.ngx-contextmenu li>a:hover,.ngx-contextmenu li>a:focus{background:var(--ctx-hover);color:var(--ctx-fg);text-decoration:none;outline:none}.ngx-contextmenu li.divider,.ngx-contextmenu li[role=separator]{height:1px;margin:6px 8px;padding:0;background:var(--ctx-border)}.ngx-contextmenu li.disabled>a,.ngx-contextmenu li[aria-disabled=true]>a{color:var(--ctx-muted);opacity:.55;pointer-events:none}@keyframes est2-ctx-in{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@media(prefers-reduced-motion:reduce){.ngx-contextmenu .dropdown-menu{animation:none}}\n"], dependencies: [{ kind: "directive", type: i8.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i9.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i12.ContextMenuAttachDirective, selector: "[contextMenu]", inputs: ["contextMenuSubject", "contextMenu"] }, { kind: "component", type: i12.ContextMenuComponent, selector: "context-menu", inputs: ["menuClass", "autoFocus", "useBootstrap4", "disabled"], outputs: ["close", "open"] }, { kind: "directive", type: i12.ContextMenuItemDirective, selector: "[contextMenuItem]", inputs: ["subMenu", "divider", "enabled", "passive", "visible"], outputs: ["execute"] }, { kind: "component", type: EsTable2PagerComponent, selector: "es-table2-pager", inputs: ["page", "pages", "total", "itemsPerPage", "countLabel", "locale", "showCount", "showButtons", "showPagingOptions", "allowAll"], outputs: ["pageChange", "itemsPerPageChange"] }, { kind: "pipe", type: i3.LocalizePipe, name: "localize" }, { kind: "pipe", type: Est2FormatPipe, name: "est2_format" }, { kind: "pipe", type: Est2LookupPipe, name: "est2_lookup" }], viewProviders: [{ provide: LocalizationService, useClass: EsTable2Loc }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
10387
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: EsTable2Component, isStandalone: false, selector: "es-table2", inputs: { ContextMenu: { classPropertyName: "ContextMenu", publicName: "ContextMenu", isSignal: false, isRequired: false, transformFunction: null }, _Selection: { classPropertyName: "_Selection", publicName: "Selection", isSignal: true, isRequired: false, transformFunction: null }, SingleSelection: { classPropertyName: "SingleSelection", publicName: "SingleSelection", isSignal: true, isRequired: false, transformFunction: null }, SelectionDisabled: { classPropertyName: "SelectionDisabled", publicName: "SelectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, _SelectAll: { classPropertyName: "_SelectAll", publicName: "SelectAll", isSignal: true, isRequired: false, transformFunction: null }, SelectAllAtStart: { classPropertyName: "SelectAllAtStart", publicName: "SelectAllAtStart", isSignal: true, isRequired: false, transformFunction: null }, SelectionKey: { classPropertyName: "SelectionKey", publicName: "SelectionKey", isSignal: true, isRequired: false, transformFunction: null }, _UseSelectionCache: { classPropertyName: "_UseSelectionCache", publicName: "UseSelectionCache", isSignal: true, isRequired: false, transformFunction: null }, _ShiftClick: { classPropertyName: "_ShiftClick", publicName: "ShiftClick", isSignal: true, isRequired: false, transformFunction: null }, _OrderByColumn: { classPropertyName: "_OrderByColumn", publicName: "OrderByColumn", isSignal: true, isRequired: false, transformFunction: null }, MultipleOrderingDirectives: { classPropertyName: "MultipleOrderingDirectives", publicName: "MultipleOrderingDirectives", isSignal: true, isRequired: false, transformFunction: null }, Removal: { classPropertyName: "Removal", publicName: "Removal", isSignal: true, isRequired: false, transformFunction: null }, RemovalCondition: { classPropertyName: "RemovalCondition", publicName: "RemovalCondition", isSignal: true, isRequired: false, transformFunction: null }, RowClassAssigner: { classPropertyName: "RowClassAssigner", publicName: "RowClassAssigner", isSignal: false, isRequired: false, transformFunction: null }, _HidePaging: { classPropertyName: "_HidePaging", publicName: "HidePaging", isSignal: true, isRequired: false, transformFunction: null }, _HidePagingCount: { classPropertyName: "_HidePagingCount", publicName: "HidePagingCount", isSignal: true, isRequired: false, transformFunction: null }, _HidePagingButtons: { classPropertyName: "_HidePagingButtons", publicName: "HidePagingButtons", isSignal: true, isRequired: false, transformFunction: null }, _AllSearch: { classPropertyName: "_AllSearch", publicName: "AllSearch", isSignal: true, isRequired: false, transformFunction: null }, _PagingStyle: { classPropertyName: "_PagingStyle", publicName: "PagingStyle", isSignal: true, isRequired: false, transformFunction: null }, _ArraymodeItemsPerPage: { classPropertyName: "_ArraymodeItemsPerPage", publicName: "ArraymodeItemsPerPage", isSignal: true, isRequired: false, transformFunction: null }, _UseArrayModePaging: { classPropertyName: "_UseArrayModePaging", publicName: "UseArrayModePaging", isSignal: true, isRequired: false, transformFunction: null }, CountLabel: { classPropertyName: "CountLabel", publicName: "CountLabel", isSignal: true, isRequired: false, transformFunction: null }, Height: { classPropertyName: "Height", publicName: "Height", isSignal: true, isRequired: false, transformFunction: null }, MaxHeight: { classPropertyName: "MaxHeight", publicName: "MaxHeight", isSignal: true, isRequired: false, transformFunction: null }, VirtualScroll: { classPropertyName: "VirtualScroll", publicName: "VirtualScroll", isSignal: true, isRequired: false, transformFunction: null }, VirtualRowHeight: { classPropertyName: "VirtualRowHeight", publicName: "VirtualRowHeight", isSignal: true, isRequired: false, transformFunction: null }, EmptySpaceBackgroundColor: { classPropertyName: "EmptySpaceBackgroundColor", publicName: "EmptySpaceBackgroundColor", isSignal: true, isRequired: false, transformFunction: null }, HighCellDensity: { classPropertyName: "HighCellDensity", publicName: "HighCellDensity", isSignal: true, isRequired: false, transformFunction: null }, HeaderHidden: { classPropertyName: "HeaderHidden", publicName: "HeaderHidden", isSignal: true, isRequired: false, transformFunction: null }, BodyHidden: { classPropertyName: "BodyHidden", publicName: "BodyHidden", isSignal: true, isRequired: false, transformFunction: null }, ShowLoadingOnBootstrap: { classPropertyName: "ShowLoadingOnBootstrap", publicName: "ShowLoadingOnBootstrap", isSignal: true, isRequired: false, transformFunction: null }, _DefaultAlignment: { classPropertyName: "_DefaultAlignment", publicName: "DefaultAlignment", isSignal: true, isRequired: false, transformFunction: null }, _TableClass: { classPropertyName: "_TableClass", publicName: "TableClass", isSignal: true, isRequired: false, transformFunction: null }, _ContainerClass: { classPropertyName: "_ContainerClass", publicName: "ContainerClass", isSignal: true, isRequired: false, transformFunction: null }, EsTableHandledSearch: { classPropertyName: "EsTableHandledSearch", publicName: "EsTableHandledSearch", isSignal: true, isRequired: false, transformFunction: null }, SearchThrottle: { classPropertyName: "SearchThrottle", publicName: "SearchThrottle", isSignal: true, isRequired: false, transformFunction: null }, _ColumnsResizable: { classPropertyName: "_ColumnsResizable", publicName: "ColumnsResizable", isSignal: true, isRequired: false, transformFunction: null }, _ColumnsPinnable: { classPropertyName: "_ColumnsPinnable", publicName: "ColumnsPinnable", isSignal: true, isRequired: false, transformFunction: null }, _HiddenColumns: { classPropertyName: "_HiddenColumns", publicName: "HiddenColumns", isSignal: true, isRequired: false, transformFunction: null }, _ColumnsOrdering: { classPropertyName: "_ColumnsOrdering", publicName: "ColumnsOrdering", isSignal: true, isRequired: false, transformFunction: null }, _Export: { classPropertyName: "_Export", publicName: "Export", isSignal: true, isRequired: false, transformFunction: null }, XLSXExport: { classPropertyName: "XLSXExport", publicName: "XLSXExport", isSignal: true, isRequired: false, transformFunction: null }, CSVExport: { classPropertyName: "CSVExport", publicName: "CSVExport", isSignal: true, isRequired: false, transformFunction: null }, ExportFileName: { classPropertyName: "ExportFileName", publicName: "ExportFileName", isSignal: true, isRequired: false, transformFunction: null }, ExportOnlyVisibleColumns: { classPropertyName: "ExportOnlyVisibleColumns", publicName: "ExportOnlyVisibleColumns", isSignal: true, isRequired: false, transformFunction: null }, ExportFunction: { classPropertyName: "ExportFunction", publicName: "ExportFunction", isSignal: false, isRequired: false, transformFunction: null }, CornerMenuOptions: { classPropertyName: "CornerMenuOptions", publicName: "CornerMenuOptions", isSignal: true, isRequired: false, transformFunction: null }, DynamicOperations: { classPropertyName: "DynamicOperations", publicName: "DynamicOperations", isSignal: true, isRequired: false, transformFunction: null }, _DynamicRowColumnsDefinition: { classPropertyName: "_DynamicRowColumnsDefinition", publicName: "DynamicRowColumnsDefinition", isSignal: true, isRequired: false, transformFunction: null }, Hierarchy: { classPropertyName: "Hierarchy", publicName: "Hierarchy", isSignal: true, isRequired: false, transformFunction: null }, _ParentKey: { classPropertyName: "_ParentKey", publicName: "ParentKey", isSignal: true, isRequired: false, transformFunction: null }, _OwnKey: { classPropertyName: "_OwnKey", publicName: "OwnKey", isSignal: true, isRequired: false, transformFunction: null }, _AutoSortHierarchy: { classPropertyName: "_AutoSortHierarchy", publicName: "AutoSortHierarchy", isSignal: true, isRequired: false, transformFunction: null }, StartsExpanded: { classPropertyName: "StartsExpanded", publicName: "StartsExpanded", isSignal: true, isRequired: false, transformFunction: null }, CascadeSelection: { classPropertyName: "CascadeSelection", publicName: "CascadeSelection", isSignal: true, isRequired: false, transformFunction: null }, _SavePreferences: { classPropertyName: "_SavePreferences", publicName: "SavePreferences", isSignal: true, isRequired: false, transformFunction: null }, Name: { classPropertyName: "Name", publicName: "Name", isSignal: true, isRequired: false, transformFunction: null }, _RowGroupingPagingStyle: { classPropertyName: "_RowGroupingPagingStyle", publicName: "RowGroupingPagingStyle", isSignal: true, isRequired: false, transformFunction: null }, _ShowItemGroupsColumns: { classPropertyName: "_ShowItemGroupsColumns", publicName: "ShowItemGroupsColumns", isSignal: true, isRequired: false, transformFunction: null }, Editable: { classPropertyName: "Editable", publicName: "Editable", isSignal: true, isRequired: false, transformFunction: null }, RangeSelection: { classPropertyName: "RangeSelection", publicName: "RangeSelection", isSignal: true, isRequired: false, transformFunction: null }, ItemSourceProperty: { classPropertyName: "ItemSourceProperty", publicName: "ItemSourceProperty", isSignal: true, isRequired: false, transformFunction: null }, HasHeaderGroup: { classPropertyName: "HasHeaderGroup", publicName: "HasHeaderGroup", isSignal: true, isRequired: false, transformFunction: null }, HasSecondaryHeaderGroup: { classPropertyName: "HasSecondaryHeaderGroup", publicName: "HasSecondaryHeaderGroup", isSignal: true, isRequired: false, transformFunction: null }, SearchView: { classPropertyName: "SearchView", publicName: "SearchView", isSignal: true, isRequired: false, transformFunction: null }, _AutoUpdate: { classPropertyName: "_AutoUpdate", publicName: "AutoUpdate", isSignal: true, isRequired: false, transformFunction: null }, EsThTdProvider: { classPropertyName: "EsThTdProvider", publicName: "EsThTdProvider", isSignal: false, isRequired: false, transformFunction: null }, globalCheck: { classPropertyName: "globalCheck", publicName: "globalCheck", isSignal: true, isRequired: false, transformFunction: null }, autoUpdate: { classPropertyName: "autoUpdate", publicName: "autoUpdate", isSignal: true, isRequired: false, transformFunction: null }, seconds: { classPropertyName: "seconds", publicName: "seconds", isSignal: true, isRequired: false, transformFunction: null }, researchInProgress: { classPropertyName: "researchInProgress", publicName: "researchInProgress", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: false, isRequired: false, transformFunction: null }, AddedItems: { classPropertyName: "AddedItems", publicName: "AddedItems", isSignal: true, isRequired: false, transformFunction: null }, RemovedItems: { classPropertyName: "RemovedItems", publicName: "RemovedItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onOrderChanged: "onOrderChanged", onSearchRequest: "onSearchRequest", onSelectionChanged: "onSelectionChanged", onRemoval: "onRemoval", onAbortRemoval: "onAbortRemoval", onModelChange: "onModelChange", onOpenContextMenu: "onOpenContextMenu", onCornerAction: "onCornerAction", onDynamicOperation: "onDynamicOperation", globalCheck: "globalCheckChange", autoUpdate: "autoUpdateChange", seconds: "secondsChange", researchInProgress: "researchInProgressChange", AddedItems: "AddedItemsChange", RemovedItems: "RemovedItemsChange" }, host: { listeners: { "document:mouseup": "onResizeEnd()", "document:copy": "onDocCopy($event)", "document:paste": "onDocPaste($event)", "document:click": "onDocClick()", "window:resize": "onViewportResize()", "document:mousemove": "onResizeMove($event)" }, properties: { "class.est2": "this.hostClass", "class.est2--dense": "this.denseClass" } }, queries: [{ propertyName: "headerRef", first: true, predicate: ["header"], descendants: true }, { propertyName: "bodyRef", first: true, predicate: ["body"], descendants: true }, { propertyName: "thDirectives", predicate: EsThDirective }, { propertyName: "tdDirectives", predicate: EsTdDirective }, { propertyName: "editorDirectives", predicate: EsTdEditorDirective }, { propertyName: "thTdProviders", predicate: ThTdProvider }], viewQueries: [{ propertyName: "tableEmptyMenu", first: true, predicate: ["emptyMenu"], descendants: true, static: true }, { propertyName: "theadRef", first: true, predicate: ["theadRef"], descendants: true }, { propertyName: "scrollRefEl", first: true, predicate: ["scrollRef"], descendants: true }, { propertyName: "dialogBackdropRef", first: true, predicate: ["dialogBackdrop"], descendants: true }], ngImport: i0, template: "@if (view()) {\n <div class=\"est2-wrap {{ ContainerClass() }}\"\n [style.height]=\"Height()\"\n [style.background-color]=\"EmptySpaceBackgroundColor() || null\">\n\n <!-- Auto-aggiornamento: toggle + intervallo (in alto a destra) -->\n @if (AutoUpdate()) {\n <div class=\"est2-autoupdate\">\n <label class=\"est2-autoupdate__label\">\n {{ 'Update every' | localize : lc }}\n <input type=\"text\" maxlength=\"3\" class=\"est2-autoupdate__secs\"\n [ngModel]=\"seconds()\" (ngModelChange)=\"seconds.set($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"autoUpdateChanged()\" />\n {{ 'second/s' | localize : lc }}\n </label>\n <label class=\"est2-switch\" [title]=\"'Toggle auto-update' | localize : lc\">\n <input type=\"checkbox\" [ngModel]=\"autoUpdate()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"autoUpdate.set($event); autoUpdateChanged()\" />\n <span class=\"est2-switch__slider\"></span>\n </label>\n </div>\n }\n\n <!-- Pager superiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'top') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Riepilogo delle modifiche non ancora salvate. Sta nel flusso (non \u00E8 un\n overlay come la barra di selezione) perch\u00E9 deve restare leggibile anche\n quando l'utente \u00E8 su una pagina che non contiene le righe interessate. -->\n @if (pendingChangesOn() && hasPendingChanges) {\n <div class=\"est2-pendingbar\">\n @if (addedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--added\">\n <strong>{{ addedCount }}</strong>\n {{ (addedCount === 1 ? 'row added' : 'rows added') | localize : lc }}\n </span>\n }\n @if (removedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--removed\">\n <strong>{{ removedCount }}</strong>\n {{ (removedCount === 1 ? 'row to delete' : 'rows to delete') | localize : lc }}\n </span>\n }\n <span class=\"est2-pendingbar__spacer\"></span>\n @if (removedCount > 0) {\n <span class=\"est2-link\" (click)=\"restoreAllRemovals()\">{{ 'Undo the deletions' | localize : lc }}</span>\n }\n </div>\n }\n\n <!-- Host POSIZIONATO dello scroller: \u00E8 l'ancora di tutti gli overlay che devono\n coprire la TABELLA (barra di selezione, overlay di caricamento). Senza di esso\n erano ancorati a `.est2-wrap` e con [AutoUpdate] o il pager in alto finivano\n sopra quei controlli invece che sopra l'header. -->\n <div class=\"est2-scroll-host\">\n\n <!-- Barra \"seleziona tutto\" (visibile solo con selezione multipla attiva e righe presenti) -->\n @if (Selection() && !SingleSelection() && hasSelection) {\n <div class=\"est2-selectbar\"\n [class.est2-selectbar--inset]=\"chromeWidth() > 0\"\n [style.height.px]=\"selbarHeight() ? selbarHeight() + 1 : null\"\n [style.right.px]=\"chromeWidth() || null\">\n @if (allSelected) {\n <span>{{ 'All the' | localize : lc }} <strong>{{ selectedCount }}</strong> {{ 'elements are selected' | localize : lc }}</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ (selectedCount === 1 ? 'element selected' : 'elements selected') | localize : lc }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">{{ 'Select all' | localize : lc }} {{ totalCount() }} {{ 'elements' | localize : lc }}</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">{{ 'Reset selection' | localize : lc }}</span>\n </div>\n }\n\n <div class=\"est2-scroll\" #scrollRef [style.max-height.px]=\"MaxHeight()\" (scroll)=\"onScrollerScroll($event)\">\n <table class=\"est2-table {{ TableClass() }}\"\n [class.est2-table--range]=\"rangeActive()\"\n [class.est2-table--dragging]=\"rangeDragging()\"\n (mousedown)=\"onGridMouseDown($event)\"\n (mouseover)=\"onGridMouseOver($event)\">\n\n <!-- ================= HEADER ================= -->\n @if (!HeaderHidden()) {\n <thead #theadRef>\n <!-- Righe di header-group multi-livello (dall'alto verso il basso) -->\n @if (hasHeaderGroups()) {\n @for (grow of headerGroupRows(); track $index) {\n <tr class=\"est2-hgroup-row\">\n @if (Selection()) { <th class=\"est2-col-min est2-selcol\" [class.est2-pinned]=\"hasPinned()\" [style.left.px]=\"hasPinned() ? 0 : null\"></th> }\n @for (op of DynamicOperations(); track op.id) { <th class=\"est2-col-min\"></th> }\n @for (cell of grow; track cell.id; let gi = $index) {\n <th [attr.colspan]=\"cell.span\"\n [attr.data-groupid]=\"cell.isGroup ? cell.id : null\"\n [class.est2-hgroup]=\"cell.isGroup\"\n [class.est2-pinned]=\"gi < pinnedCount()\"\n [style.left.px]=\"gi < pinnedCount() ? pinnedLeftPx(gi) : null\"\n class=\"est2-hgroup-cell\">\n @if (cell.isGroup) {\n @if (cell.template) {\n <ng-container *ngTemplateOutlet=\"cell.template\"></ng-container>\n } @else {\n {{ cell.label }}\n }\n }\n </th>\n }\n @if (Removal()) { <th class=\"est2-col-min\"></th> }\n @if (hasChrome()) { <th class=\"est2-col-min est2-chrome-col\"></th> }\n </tr>\n }\n }\n <tr>\n <!-- Colonna di selezione -->\n @if (Selection()) {\n <th class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"globalCheck()\"\n [indeterminate]=\"selectionIndeterminate\"\n [disabled]=\"SelectionDisabled()\"\n (change)=\"toggleAll()\"\n [attr.aria-label]=\"'Select all rows' | localize : lc\" />\n }\n </th>\n }\n\n <!-- Header da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- intestazioni vuote per le operazioni dinamiche -->\n @for (op of DynamicOperations(); track op.id) {\n <th class=\"est2-col-min\"></th>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let ci = $index) {\n @let hs = colStyles()[ci];\n <th [attr.data-colid]=\"col.id\"\n [class]=\"col.headerClass\"\n [class.est2-th--orderable]=\"OrderByColumn() && col.orderable\"\n [class.est2-col-min]=\"col.header?.thShrink\"\n [class.est2-col-groupstart]=\"hs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-pinned-right]=\"col.pinnedRight\"\n [style.left.px]=\"hs.left\"\n [style.right.px]=\"hs.right\"\n [style.width.px]=\"hs.width\"\n [style.min-width.px]=\"hs.width\"\n [style.max-width.px]=\"hs.width\"\n [style.text-align]=\"col.alignment\"\n [style.background-color]=\"col.headerBg || null\"\n (click)=\"toggleSort(col)\">\n <span class=\"est2-th__inner\">\n @if (col.header?.Template) {\n <ng-container *ngTemplateOutlet=\"col.header!.Template!; context: col.multiProp != null ? { $implicit: col.headerText } : null\"></ng-container>\n } @else {\n {{ col.headerText }}\n }\n @if (OrderByColumn() && col.orderable && orderOf(col.id)) {\n <span class=\"est2-sort est2-sort--active\"\n [class.est2-sort--desc]=\"orderOf(col.id) === 'DESC'\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 15l6-6 6 6\"/></svg>\n </span>\n @if (orderIndex(col.id) > 0 && MultipleOrderingDirectives()) {\n <span class=\"est2-sort__badge\">{{ orderIndex(col.id) }}</span>\n }\n }\n <!-- Indicatore/toggle di pin (visibile se pinnata o all'hover) -->\n @if (ColumnsPinnable() && col.multiProp == null && !col.fixed) {\n <button type=\"button\" class=\"est2-pinbtn\"\n [class.est2-pinbtn--on]=\"col.pinned || col.pinnedRight\"\n [class.est2-pinbtn--right]=\"col.pinnedRight\"\n [title]=\"pinTitleFor(col) | localize : lc\"\n (click)=\"togglePin(col, $event)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n </span>\n <!-- Handle di ridimensionamento (colonne pinnate, gated da ColumnsResizable) -->\n @if (col.pinned || ColumnsResizable()) {\n <span class=\"est2-resize-handle\" [title]=\"'Drag to resize' | localize : lc\"\n (mousedown)=\"startColumnResize(col, $event)\"\n (click)=\"$event.stopPropagation()\"></span>\n }\n </th>\n }\n }\n <!-- Header da template semplice -->\n @else if (headerRef) {\n <ng-container *ngTemplateOutlet=\"headerRef\"></ng-container>\n }\n\n <!-- Colonna rimozione -->\n @if (Removal()) {\n <th class=\"est2-col-min est2-trail-th\"\n [class.est2-removal-col]=\"hasPinnedRight()\"\n [style.right.px]=\"hasPinnedRight() ? chromeWidth() : null\"></th>\n }\n <!-- Colonna chrome (export / gestione colonne / menu) -->\n @if (hasChrome()) {\n <th class=\"est2-col-min est2-chrome-th est2-chrome-col est2-trail-th\">\n <div class=\"est2-chrome\">\n <!-- I pannelli dei menu NON vivono qui: sono renderizzati a fondo\n componente e posizionati `fixed`, altrimenti l'`overflow:auto`\n di `.est2-scroll` li taglierebbe su tabelle basse. -->\n @if (Export()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Export' | localize : lc\" (click)=\"$event.stopPropagation(); toggleExportMenu($event)\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><path d=\"M7 10l5 5 5-5\"/><path d=\"M12 15V3\"/></svg>\n </button>\n }\n @if (HiddenColumns() || ColumnsOrdering()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Columns' | localize : lc\" (click)=\"$event.stopPropagation(); openColumnsDialog()\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\"/><path d=\"M9 3v18\"/><path d=\"M15 3v18\"/></svg>\n </button>\n }\n @if (CornerMenuOptions().length > 0) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Options' | localize : lc\" (click)=\"$event.stopPropagation(); toggleCornerMenu($event)\">\u22EE</button>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n\n <!-- ================= BODY ================= -->\n @if (!BodyHidden()) {\n <tbody>\n <!-- Valori invarianti per l'intero corpo: senza questi verrebbero\n ricalcolati riga\u00D7colonna volte a ogni ciclo di change-detection. -->\n @let rng = rangeActive();\n @let rect = rangeRect();\n @let errs = hasCellErrors();\n @let vw = virtualWindow();\n @if (vw && vw.padTop > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padTop\"></td></tr>\n }\n @for (item of renderedRows(); track trackRow($index, item); let vi = $index) {\n <!-- indice ASSOLUTO: range, editing e data-r ragionano su boundSource() -->\n @let ri = vStart() + vi;\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--zebra]=\"vw && (ri % 2 === 1)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted || item._removed\"\n [class.est2-row--added]=\"item._localId != null\"\n [class.est2-row--group]=\"item._group\"\n [class.est2-row--clickable]=\"Selection() || item._group\"\n [contextMenu]=\"ContextMenu || emptyMenu\"\n [contextMenuSubject]=\"item\"\n (click)=\"handleRowClick(item, $event)\">\n\n <!-- Cella di selezione -->\n @if (Selection()) {\n <td class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (item._group) {\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"groupSelectionState(item) === 'all'\"\n [indeterminate]=\"groupSelectionState(item) === 'some'\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection(item)\"\n [attr.aria-label]=\"'Select group' | localize : lc\" />\n }\n } @else {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"item._selected\"\n [indeterminate]=\"hierarchyIndeterminate(item)\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(item)\"\n [attr.aria-label]=\"'Select row' | localize : lc\" />\n }\n </td>\n }\n\n <!-- Celle da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- Operazioni dinamiche (icone a sinistra) -->\n @for (op of DynamicOperations(); track op.id) {\n <td class=\"est2-col-min est2-op-cell\">\n @if (!item._group && operationVisible(op, item)) {\n <span class=\"est2-op\" [class]=\"op.iconClass || ''\" [title]=\"op.title\"\n (click)=\"$event.stopPropagation(); dynamicOperation(item, op.id)\">{{ op.text }}</span>\n }\n </td>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let first = $first, ci = $index) {\n @let cs = colStyles()[ci];\n @let insel = rng && inRange(ri, ci);\n @let editing = isEditing(ri, ci);\n <td [class]=\"col.cssClass\"\n [style.text-align]=\"col.alignment\"\n [style.color]=\"cellColor(item, col, 'fore')\"\n [style.background-color]=\"cellColor(item, col, 'back')\"\n [class.est2-nowrap]=\"!col.wrap\"\n [class.est2-col-groupstart]=\"cs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-pinned-right]=\"col.pinnedRight\"\n [class.est2-fixedw]=\"cs.width != null\"\n [style.left.px]=\"cs.left\"\n [style.right.px]=\"cs.right\"\n [style.width.px]=\"cs.width\"\n [style.min-width.px]=\"cs.width\"\n [style.max-width.px]=\"cs.width\"\n [class.est2-td--group-key]=\"item._group && item.column === col.id\"\n [attr.data-r]=\"rng && !item._group ? ri : null\"\n [attr.data-c]=\"rng && !item._group ? ci : null\"\n [class.est2-cell-sel]=\"insel\"\n [class.est2-cell-sel-t]=\"insel && ri === rect!.top\"\n [class.est2-cell-sel-b]=\"insel && ri === rect!.bottom\"\n [class.est2-cell-sel-l]=\"insel && ci === rect!.left\"\n [class.est2-cell-sel-r]=\"insel && ci === rect!.right\"\n [class.est2-cell-editing]=\"editing\"\n [class.est2-cell-invalid]=\"errs && !!cellError(item, col)\"\n [attr.data-error]=\"errs ? (cellError(item, col) || null) : null\"\n (dblclick)=\"onCellDblClick(item, col, ri, ci)\">\n @if (editing) {\n @if (editorFor(col); as edTpl) {\n <ng-container *ngTemplateOutlet=\"edTpl; context: editorContext(item, col)\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"defaultEditor; context: { $implicit: item, col: col }\"></ng-container>\n }\n } @else if (item._group) {\n @if (item.column === col.id) {\n <span class=\"est2-group-key\" [style.padding-left.px]=\"groupIndent(item)\">\n <span class=\"est2-group-chevron\" [class.est2-group-chevron--open]=\"item._expanded\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n {{ groupCellDisplay(item, col) }}\n </span>\n } @else {\n {{ groupCellDisplay(item, col) }}\n }\n } @else {\n <!-- Navigatore albero nella prima colonna -->\n @if (Hierarchy() && first) {\n <span class=\"est2-hier-lead\" [style.padding-left.px]=\"hierarchyIndent(item)\">\n @if (item.parent) {\n <span class=\"est2-group-chevron est2-hier-toggle\" [class.est2-group-chevron--open]=\"item._expanded\"\n (click)=\"$event.stopPropagation(); toggleHierarchyNode(item)\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-hier-toggle\"></span>\n }\n </span>\n }\n @if (itemCellHidden(col)) {\n <!-- colonna di gruppo nascosta sulla riga-oggetto -->\n } @else if (col.multiProp != null) {\n @if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: multiCell(item, col) }\"></ng-container>\n } @else {\n {{ multiCell(item, col)?.value }}\n }\n } @else if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: item }\"></ng-container>\n } @else if (col.routePath) {\n <a class=\"est2-link\" [routerLink]=\"routerLinkFor(item, col)\">{{ cellValue(item, col) }}</a>\n } @else if (col.propAccessor != null) {\n {{ reportCellDisplay(item, col) }}\n } @else if (col.type === 'enum') {\n {{ (cellValue(item, col) | est2_lookup : col.source).description }}\n } @else {\n {{ cellValue(item, col) | est2_format : col.type : col.format : locale }}\n }\n }\n </td>\n }\n }\n <!-- Celle da template semplice -->\n @else if (bodyRef) {\n <ng-container *ngTemplateOutlet=\"bodyRef; context: { $implicit: item }\"></ng-container>\n }\n\n <!-- Rimozione (non sulle righe-gruppo) -->\n @if (Removal()) {\n <td class=\"est2-col-min\"\n [class.est2-removal-col]=\"hasPinnedRight()\"\n [style.right.px]=\"hasPinnedRight() ? chromeWidth() : null\">\n @if (!item._group && canRemove(item)) {\n @if (item.removed || item.deleted || item._removed) {\n <button type=\"button\" class=\"est2-rowaction\" [title]=\"'Restore' | localize : lc\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" [title]=\"'Remove' | localize : lc\" (click)=\"removeItem(item)\">\u2715</button>\n }\n }\n </td>\n }\n <!-- Chrome -->\n @if (hasChrome()) { <td class=\"est2-col-min est2-chrome-col\"></td> }\n </tr>\n }\n } @empty {\n <tr>\n <td class=\"est2-empty\" [attr.colspan]=\"totalColspan()\">{{ 'No elements to display' | localize : lc }}</td>\n </tr>\n }\n @if (vw && vw.padBottom > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padBottom\"></td></tr>\n }\n </tbody>\n }\n </table>\n </div>\n\n <!-- Overlay di caricamento: FUORI dallo scroller, cos\u00EC copre il viewport della\n tabella senza scorrere col contenuto e senza essere bucato dalle celle sticky -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>{{ 'Loading\u2026' | localize : lc }}</span>\n </div>\n }\n\n </div>\n\n <!-- Pager inferiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Menu del chrome (export / opzioni). Renderizzati FUORI da `.est2-scroll` e\n posizionati `fixed` sulle coordinate del bottone che li ha aperti: cos\u00EC non\n vengono tagliati dall'`overflow:auto` dello scroller su tabelle basse, e\n restano dentro `.est2` (i token `--est-*` continuano a ereditare). -->\n @if (exportMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @if (CSVExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">{{ 'Export Excel (XLSX)' | localize : lc }}</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n </div>\n }\n @if (cornerMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @for (opt of CornerMenuOptions(); track opt.id) {\n <button type=\"button\" class=\"est2-menu-item\" (click)=\"cornerAction(opt.id); closeChromeMenus()\">{{ opt.description }}</button>\n }\n </div>\n }\n\n <!-- Avviso transitorio (es. incolla con dimensioni incompatibili) -->\n @if (notice()) {\n <div class=\"est2-toast\" role=\"alert\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/></svg>\n <span>{{ notice() }}</span>\n </div>\n }\n\n <!-- Dialog visibilit\u00E0 / ordine colonne -->\n @if (columnsDialogOpen()) {\n <div class=\"est2-dialog-backdrop\" #dialogBackdrop\n [class.est2-dialog-backdrop--embedded]=\"embedded\"\n (click)=\"closeColumnsDialog()\">\n <div class=\"est2-dialog\" (click)=\"$event.stopPropagation()\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"est2-dialog__head\">\n <span>\n @if (HiddenColumns() && ColumnsOrdering()) { {{ 'Columns visibility and order' | localize : lc }} }\n @else if (HiddenColumns()) { {{ 'Columns visibility' | localize : lc }} }\n @else { {{ 'Columns order' | localize : lc }} }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" [attr.aria-label]=\"'Close' | localize : lc\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">{{ 'Show all' | localize : lc }}</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">{{ 'Hide all' | localize : lc }}</button>\n </div>\n }\n\n <ul class=\"est2-collist\">\n @for (c of dialogCols(); track c.id; let i = $index) {\n <li class=\"est2-collist__row\"\n [attr.draggable]=\"ColumnsOrdering() && canReorder(c) ? true : null\"\n [class.est2-collist__row--drag]=\"ColumnsOrdering() && canReorder(c)\"\n [class.est2-collist__row--locked]=\"!canReorder(c)\"\n [class.est2-collist__row--dragging]=\"dragIndex() === i\"\n [class.est2-collist__row--drop-above]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! > i\"\n [class.est2-collist__row--drop-below]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! < i\"\n (dragstart)=\"dialogDragStart(i, $event)\"\n (dragover)=\"dialogDragOver(i, $event)\"\n (drop)=\"dialogDrop(i, $event)\"\n (dragend)=\"dialogDragEnd()\">\n @if (ColumnsOrdering()) {\n @if (canReorder(c)) {\n <span class=\"est2-collist__grip\" [title]=\"'Drag to reorder' | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"currentColor\"><circle cx=\"9\" cy=\"6\" r=\"1.6\"/><circle cx=\"15\" cy=\"6\" r=\"1.6\"/><circle cx=\"9\" cy=\"12\" r=\"1.6\"/><circle cx=\"15\" cy=\"12\" r=\"1.6\"/><circle cx=\"9\" cy=\"18\" r=\"1.6\"/><circle cx=\"15\" cy=\"18\" r=\"1.6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-collist__grip est2-collist__grip--locked\"\n [title]=\"(c.fixed ? 'Fixed column: cannot be moved' : 'Column locked to the left: cannot be moved') | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"4\" y=\"11\" width=\"16\" height=\"10\" rx=\"2\"/><path d=\"M8 11V7a4 4 0 0 1 8 0v4\"/></svg>\n </span>\n }\n }\n @if (HiddenColumns()) {\n <label class=\"est2-collist__vis\" [title]=\"c.fixed ? ('Fixed column: always visible' | localize : lc) : ''\">\n <input type=\"checkbox\" class=\"est2-check\" [checked]=\"c.visible\" [disabled]=\"c.fixed\" (change)=\"dialogToggle(i)\" />\n </label>\n }\n <span class=\"est2-collist__label\">\n @if (c.tmpl) {\n <!-- colonna non renderizzata (Visible:false / nascosta): il testo\n header non esiste nel DOM, quindi rendo il template dell'`*th` -->\n @if (c.prefix) { <span class=\"est2-collist__path\">{{ c.prefix }}</span> }\n <ng-container *ngTemplateOutlet=\"c.tmpl; context: c.tmplCtx\"></ng-container>\n } @else {\n {{ c.label }}\n }\n </span>\n @if (ColumnsPinnable()) {\n <button type=\"button\" class=\"est2-iconbtn est2-collist__pin\" [class.est2-collist__pin--on]=\"c.pinned || c.pinnedRight\"\n [disabled]=\"c.grouped || c.fixed\"\n [title]=\"dialogPinTitle(c) | localize : lc\"\n (click)=\"dialogTogglePin(i)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n @if (ColumnsOrdering()) {\n <span class=\"est2-collist__ord\">\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Up' | localize : lc\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Down' | localize : lc\" (click)=\"dialogMove(i, 1)\"\n [disabled]=\"i === dialogCols().length - 1 || !canReorder(c) || !canReorder(dialogCols()[i + 1])\">\u2193</button>\n </span>\n }\n </li>\n }\n </ul>\n\n <div class=\"est2-dialog__foot\">\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"resetColumnsDialog()\"\n [title]=\"'Restore the declared columns and delete the saved preferences for this table' | localize : lc\">{{ 'Reset' | localize : lc }}</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">{{ 'Cancel' | localize : lc }}</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">{{ 'Apply' | localize : lc }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n}\n\n<!-- Template pager riutilizzabile sopra/sotto -->\n<ng-template #pager>\n @if (!HidePaging() && !grouped()) {\n <es-table2-pager\n [page]=\"currentPage()\"\n [pages]=\"totalPages()\"\n [total]=\"totalCount()\"\n [locale]=\"locale\"\n [itemsPerPage]=\"viewMode() ? (view()?.itemsperpageoverride ?? 15) : ArraymodeItemsPerPage()\"\n [countLabel]=\"CountLabel()\"\n [showCount]=\"!HidePagingCount()\"\n [showButtons]=\"!HidePagingButtons()\"\n [showPagingOptions]=\"!HidePagingButtons()\"\n [allowAll]=\"AllSearch()\"\n (pageChange)=\"goToPage($event)\"\n (itemsPerPageChange)=\"changeItemsPerPage($event)\">\n </es-table2-pager>\n }\n</ng-template>\n\n<!-- Editor di cella di default (usato quando il consumer non fornisce un `*editor`) -->\n<ng-template #defaultEditor let-item let-col=\"col\">\n @switch (col.type) {\n @case ('enum') {\n <select class=\"est2-editor-input\"\n [value]=\"editDraft\"\n (change)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\">\n @for (o of col.source || []; track o.id) {\n <option [value]=\"o.id\" [selected]=\"o.id == editDraft\">{{ o.description }}</option>\n }\n </select>\n }\n @case ('boolean') {\n <input type=\"checkbox\" class=\"est2-editor-input est2-check\"\n [checked]=\"editDraft === true || editDraft === 'true'\"\n (change)=\"editDraft = $any($event.target).checked; commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\" />\n }\n @default {\n <input class=\"est2-editor-input\"\n [type]=\"editorInputType(col.type)\"\n [value]=\"editDraft\"\n (input)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\" />\n }\n }\n</ng-template>\n\n<!-- Menu di default (nessuna operazione) usato quando il consumer non passa [ContextMenu] -->\n<context-menu #emptyMenu>\n <ng-template contextMenuItem [passive]=\"true\"><em>{{ 'No operations available\u2026' | localize : lc }}</em></ng-template>\n</context-menu>\n", styles: [".est2{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent);--est-radius: 10px;--est-radius-sm: 6px;--est-radius-pill: 999px;--est-gap: 8px;--est-cell-py: 6px;--est-cell-px: 12px;--est-row-h: 33px;--est-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--est-fs: 13px;--est-fs-sm: 12px;--est-fw-head: 700;--est-transition: .14s cubic-bezier(.4, 0, .2, 1);--est-bg-zebra: color-mix(in srgb, var(--est-fg) 3.5%, var(--est-bg));--est-z-menu: 1000;--est-z-modal: 1010;--est-z-toast: 1020;font-family:var(--est-font);font-size:var(--est-fs);color:var(--est-fg);position:relative;display:block}.est2.est2--dark{--est-bg: #14161c;--est-bg-subtle: #1a1d25;--est-bg-raised: #1e222b;--est-bg-hover: #232733;--est-bg-selected: color-mix(in srgb, var(--est-accent) 26%, transparent);--est-fg: #e7eaf0;--est-fg-muted: #9aa3b2;--est-fg-faint: #6b7484;--est-border: #2a2f3a;--est-border-strong: #39404d;--est-accent: #7c74ff;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 22%, transparent);--est-danger: #f87171;--est-warning: #fbbf24;--est-success: #34d399;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 6px 16px -10px rgba(0, 0, 0, .7);--est-shadow-pop: 0 10px 28px -8px rgba(0, 0, 0, .6), 0 2px 6px -2px rgba(0, 0, 0, .5);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 55%, transparent)}.est2.est2--light{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent)}.est2.est2--dense{--est-cell-py: 3px;--est-cell-px: 9px;--est-row-h: 27px;--est-fs: 12.5px}.est2 *,.est2 *:before,.est2 *:after{box-sizing:border-box}.est2 .est2-scroll{position:relative;width:100%;overflow:auto;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg);-webkit-overflow-scrolling:touch}.est2 table.est2-table{width:100%;border-collapse:separate;border-spacing:0;background:var(--est-bg)}.est2 thead th{position:sticky;top:0;z-index:3;background:var(--est-bg-subtle);color:var(--est-fg);font-weight:var(--est-fw-head);font-size:var(--est-fs);text-align:left;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);box-shadow:var(--est-shadow-sticky);-webkit-user-select:none;user-select:none}.est2 thead tr.est2-hgroup-row th{font-size:var(--est-fs-sm);font-weight:700;color:var(--est-fg-muted);text-align:center;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);background:var(--est-bg-subtle);border-bottom:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th.est2-hgroup{color:var(--est-fg);border-left:1px solid var(--est-border);border-right:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th:not(.est2-hgroup){background:var(--est-bg-subtle);border-bottom-color:transparent}.est2 thead tr:last-child th.est2-col-groupstart,.est2 tbody td.est2-col-groupstart{border-left:1px solid var(--est-border)}.est2 tbody td{padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);color:var(--est-fg);vertical-align:middle;background:transparent}.est2 tbody tr:nth-child(2n) td{background:var(--est-bg-zebra)}.est2 tbody tr:nth-child(2n) td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-row--zebra td,.est2 tbody tr.est2-row--zebra td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-vspacer td{padding:0;border:none;background:transparent}.est2 tbody tr.est2-vspacer:hover td{background:transparent}.est2 th.est2-pinned,.est2 td.est2-pinned{position:sticky;background:var(--est-bg);box-shadow:1px 0 0 var(--est-border)}.est2 .est2-resize-handle{position:absolute;top:0;right:-3px;width:10px;height:100%;cursor:col-resize;z-index:2;touch-action:none;-webkit-user-select:none;user-select:none}.est2 .est2-resize-handle:after{content:\"\";position:absolute;top:28%;bottom:28%;right:3px;width:2px;border-radius:2px;background:color-mix(in srgb,var(--est-fg) 30%,transparent);transition:background var(--est-transition),top var(--est-transition),bottom var(--est-transition)}.est2 .est2-resize-handle:hover:after{background:var(--est-accent);top:15%;bottom:15%}.est2 .est2-resize-handle:active:after{background:var(--est-accent);top:6%;bottom:6%}.est2 td.est2-fixedw{overflow:hidden;text-overflow:ellipsis}.est2 td.est2-cell-invalid{background:color-mix(in srgb,var(--est-danger) 10%,var(--est-bg));box-shadow:inset 0 0 0 1px var(--est-danger);color:var(--est-danger)}.est2 .est2-selcol{width:44px;min-width:44px;max-width:44px}.est2 td.est2-pinned{z-index:3}.est2 thead th.est2-pinned,.est2 thead tr.est2-hgroup-row th.est2-pinned{z-index:6;background:var(--est-bg-subtle)}.est2 tbody tr:hover td.est2-pinned{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-pinned{background:var(--est-bg-subtle)}.est2 th.est2-pinned-right,.est2 td.est2-pinned-right{position:sticky;background:var(--est-bg);box-shadow:-1px 0 0 var(--est-border)}.est2 td.est2-pinned-right{z-index:3}.est2 thead th.est2-pinned-right,.est2 thead tr.est2-hgroup-row th.est2-pinned-right{z-index:6;background:var(--est-bg-subtle)}.est2 tbody tr:nth-child(2n) td.est2-pinned-right{background:var(--est-bg-zebra)}.est2 tbody tr.est2-row--zebra td.est2-pinned-right{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-pinned-right{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-pinned-right{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-pinned-right{background:var(--est-bg-subtle)}.est2 tbody tr.est2-row--added td.est2-pinned-right{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 th.est2-removal-col,.est2 td.est2-removal-col{position:sticky;background:var(--est-bg)}.est2 thead th.est2-removal-col{z-index:6;background:var(--est-bg-subtle)}.est2 td.est2-removal-col{z-index:3}.est2 tbody tr:nth-child(2n) td.est2-removal-col{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-removal-col{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-removal-col{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 th.est2-chrome-col,.est2 td.est2-chrome-col{position:sticky;right:0;box-shadow:-1px 0 0 var(--est-border)}.est2 thead th.est2-chrome-col{z-index:16;background:var(--est-bg-subtle)}.est2 td.est2-chrome-col{z-index:4;background:var(--est-bg)}.est2 tbody tr:nth-child(2n) td.est2-chrome-col{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-chrome-col{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-chrome-col{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-chrome-col{background:var(--est-bg-subtle)}.est2 .est2-pinbtn--right svg,.est2 .est2-collist__pin--right svg{transform:scaleX(-1)}.est2 .est2-pinbtn{display:inline-flex;align-items:center;justify-content:center;margin-left:4px;padding:2px;border:none;background:transparent;color:var(--est-fg-faint);border-radius:var(--est-radius-sm);cursor:pointer;opacity:0;transition:opacity var(--est-transition),color var(--est-transition),background var(--est-transition)}.est2 thead th:hover .est2-pinbtn{opacity:.7}.est2 .est2-pinbtn:hover{background:var(--est-bg-hover);color:var(--est-fg);opacity:1}.est2 .est2-pinbtn--on{opacity:1;color:var(--est-accent);transform:rotate(0)}.est2 thead th:hover .est2-pinbtn--on{opacity:1}.est2 .est2-collist__pin.est2-collist__pin--on{color:var(--est-accent);border-color:var(--est-accent)}.est2 tbody tr{height:var(--est-row-h);transition:background var(--est-transition)}.est2 tbody tr:last-child td{border-bottom:none}.est2 tbody tr:hover td{background:var(--est-bg-hover)}.est2 tbody tr.est2-row--selected td{background:var(--est-bg-selected)}.est2 tbody tr.est2-row--clickable{cursor:pointer}.est2 tbody tr.est2-row--added td{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 tbody tr.est2-row--added td:first-child{box-shadow:inset 3px 0 0 var(--est-accent)}.est2 tbody tr.est2-row--added td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 .est2-pendingbar{display:flex;align-items:center;gap:10px;padding:7px 12px;margin-bottom:8px;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg-subtle);font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-pendingbar__spacer{flex:1 1 auto}.est2 .est2-pendingbar__chip{display:inline-flex;align-items:center;gap:5px;padding:2px 10px;border-radius:var(--est-radius-pill)}.est2 .est2-pendingbar__chip strong{color:var(--est-fg)}.est2 .est2-pendingbar__chip--added{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 .est2-pendingbar__chip--removed{background:color-mix(in srgb,var(--est-danger) 14%,transparent)}.est2 tbody tr.est2-row--removed td{text-decoration:line-through;color:var(--est-fg-faint)}.est2 .est2-table--range tbody td[data-r]{cursor:cell}.est2 .est2-table--dragging,.est2 .est2-table--dragging tbody td{-webkit-user-select:none;user-select:none}.est2 tbody td.est2-cell-sel{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 tbody td.est2-cell-sel-t{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l{box-shadow:inset 2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-r{box-shadow:inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b{box-shadow:inset 0 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 0 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px -2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-editing{padding:2px 6px}.est2 .est2-editor-input{width:100%;box-sizing:border-box;height:calc(var(--est-row-h) - 8px);padding:2px 6px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1.5px solid var(--est-accent);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-editor-input:focus-visible{box-shadow:var(--est-ring)}.est2 input.est2-editor-input[type=checkbox]{width:16px;height:16px}.est2 .est2-autoupdate{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:4px 2px 8px;font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-autoupdate__label{display:inline-flex;align-items:center;gap:6px}.est2 .est2-autoupdate__secs{width:44px;text-align:center;padding:3px 4px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-autoupdate__secs:focus-visible{box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2 .est2-switch{position:relative;display:inline-flex;width:38px;height:20px;cursor:pointer}.est2 .est2-switch input{position:absolute;opacity:0;width:0;height:0}.est2 .est2-switch__slider{flex:1;border-radius:var(--est-radius-pill);background:var(--est-border-strong);transition:background var(--est-transition)}.est2 .est2-switch__slider:before{content:\"\";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px #00000040;transition:transform var(--est-transition)}.est2 .est2-switch input:checked+.est2-switch__slider{background:var(--est-accent)}.est2 .est2-switch input:checked+.est2-switch__slider:before{transform:translate(18px)}.est2 .est2-switch input:focus-visible+.est2-switch__slider{box-shadow:var(--est-ring)}.est2 .est2-toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%);z-index:var(--est-z-toast);pointer-events:none;display:inline-flex;align-items:center;gap:8px;max-width:min(560px,100vw - 24px);padding:8px 14px;font-size:13px;font-weight:500;color:#fff;background:#b91c1c;border-radius:var(--est-radius);box-shadow:0 6px 20px #00000040;animation:est2-toast-in .16s ease-out}.est2 .est2-toast svg{flex:0 0 auto}@keyframes est2-toast-in{0%{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%)}}@media(prefers-reduced-motion:reduce){.est2 .est2-toast{animation:none}}.est2 tbody tr.est2-row--group{cursor:pointer;-webkit-user-select:none;user-select:none}.est2 tbody tr.est2-row--group td{background:var(--est-bg-subtle);font-weight:600;color:var(--est-fg);border-bottom:1px solid var(--est-border)}.est2 tbody tr.est2-row--group:hover td{background:var(--est-bg-hover)}.est2 .est2-td--group-key{color:var(--est-fg)}.est2 .est2-group-key{display:inline-flex;align-items:center;gap:6px}.est2 .est2-group-chevron{display:inline-flex;color:var(--est-fg-muted);transition:transform var(--est-transition)}.est2 .est2-group-chevron--open{transform:rotate(90deg)}.est2 .est2-hier-lead{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px}.est2 .est2-hier-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex:none}.est2 .est2-group-chevron.est2-hier-toggle{cursor:pointer;border-radius:var(--est-radius-sm);transition:transform var(--est-transition),background var(--est-transition),color var(--est-transition)}.est2 .est2-group-chevron.est2-hier-toggle:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 th.est2-th--orderable{cursor:pointer;transition:color var(--est-transition)}.est2 th.est2-th--orderable:hover{color:var(--est-fg)}.est2 .est2-th__inner{display:inline-flex;align-items:center;gap:6px}.est2 .est2-sort{display:inline-flex;width:14px;height:14px;opacity:.35;transition:opacity var(--est-transition),transform var(--est-transition)}.est2 .est2-sort--active{opacity:1;color:var(--est-accent)}.est2 .est2-sort--desc{transform:rotate(180deg)}.est2 .est2-sort__badge{font-size:9px;font-weight:700;color:var(--est-accent);margin-left:2px}.est2 .est2-check{appearance:none;width:16px;height:16px;border:1.5px solid var(--est-border-strong);border-radius:var(--est-radius-sm);background:var(--est-bg);cursor:pointer;position:relative;transition:border-color var(--est-transition),background var(--est-transition);vertical-align:middle;flex:none}.est2 .est2-check:hover{border-color:var(--est-accent)}.est2 .est2-check:checked{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:checked:after{content:\"\";position:absolute;left:4.5px;top:1.5px;width:4px;height:8px;border:solid var(--est-accent-fg);border-width:0 2px 2px 0;transform:rotate(45deg)}.est2 .est2-check:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2 .est2-check:disabled{opacity:.45;cursor:not-allowed}.est2 .est2-check:disabled:hover{border-color:var(--est-border-strong)}.est2 .est2-check:indeterminate{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--est-accent-fg);transform:none;border:none}.est2 th.est2-col-min,.est2 td.est2-col-min{width:1%;white-space:nowrap}.est2 .est2-wrap,.est2 .est2-scroll-host{position:relative}.est2 .est2-selectbar{position:absolute;top:0;left:0;right:0;z-index:15;display:flex;align-items:center;gap:12px;padding:9px 14px;font-size:var(--est-fs-sm);color:var(--est-fg);background:var(--est-bg-subtle);border:none;border-radius:var(--est-radius) var(--est-radius) 0 0;box-shadow:inset 3px 0 0 var(--est-accent)}.est2 .est2-selectbar--inset{border-top-right-radius:0}.est2 .est2-selectbar strong{color:var(--est-fg);font-weight:700}.est2 .est2-selectbar__spacer{flex:1 1 auto}.est2 .est2-link{color:var(--est-accent);cursor:pointer;font-weight:600}.est2 .est2-link:hover{text-decoration:underline}.est2 .est2-rowaction{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);border:none;background:transparent;color:var(--est-fg-faint);cursor:pointer;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-rowaction:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-rowaction--danger:hover{color:var(--est-danger)}.est2 .est2-op-cell{text-align:center}.est2 .est2-op{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;padding:0 6px;border-radius:var(--est-radius-sm);color:var(--est-accent);cursor:pointer;font-size:var(--est-fs-sm);transition:background var(--est-transition)}.est2 .est2-op:hover{background:var(--est-accent-weak)}.est2 .est2-loading{position:absolute;inset:0;border-radius:var(--est-radius);z-index:20;display:flex;align-items:center;justify-content:center;gap:10px;background:color-mix(in srgb,var(--est-bg) 70%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);color:var(--est-fg-muted);font-size:var(--est-fs-sm)}.est2 .est2-spinner{width:16px;height:16px;border:2px solid var(--est-border-strong);border-top-color:var(--est-accent);border-radius:50%;animation:est2-spin .7s linear infinite}@keyframes est2-spin{to{transform:rotate(360deg)}}.est2 .est2-nowrap{white-space:nowrap}.est2 .est2-cornermenu{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);cursor:pointer;color:var(--est-fg-muted);transition:background var(--est-transition)}.est2 .est2-cornermenu:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-chrome-th{position:relative}.est2 .est2-chrome{display:inline-flex;align-items:center;gap:2px}.est2 .est2-chrome-wrap{position:relative;display:inline-flex}.est2 .est2-chrome-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:none;background:transparent;border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer;font-size:16px;line-height:1;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-chrome-btn:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-menu{position:fixed;z-index:var(--est-z-menu);min-width:180px;padding:4px;background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 8px 24px #0000002e;display:flex;flex-direction:column}.est2 .est2-menu-item{display:block;width:100%;padding:8px 10px;border:none;background:transparent;text-align:left;font:inherit;color:var(--est-fg);border-radius:var(--est-radius-sm);cursor:pointer}.est2 .est2-menu-item:hover{background:var(--est-bg-hover)}.est2 .est2-dialog-backdrop{position:fixed;inset:0;z-index:var(--est-z-modal);display:flex;align-items:center;justify-content:center;padding:16px}.est2 .est2-dialog-backdrop:before{content:\"\";position:absolute;inset:0;background-color:#000;opacity:.5}.est2 .est2-dialog{position:relative}.est2 .est2-dialog-backdrop--embedded{--est-embed-offset: 110px;padding-top:5%;margin-left:calc(-1 * var(--est-embed-offset));width:calc(100% + var(--est-embed-offset));overflow:hidden}.est2 .est2-dialog-backdrop--embedded .est2-dialog{max-height:600px}.est2 .est2-dialog{width:520px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;background:var(--est-bg);color:var(--est-fg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 16px 48px #0000004d;overflow:hidden}.est2 .est2-dialog__head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;font-weight:700;border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__close{border:none;background:transparent;cursor:pointer;color:var(--est-fg-muted);font-size:15px;line-height:1;padding:4px;border-radius:var(--est-radius-sm)}.est2 .est2-dialog__close:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-dialog__tools{padding:8px 14px;font-size:var(--est-fs-sm);border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__tools .est2-link{border:none;background:none;padding:0;font:inherit;font-weight:600}.est2 .est2-dialog__sep{margin:0 6px;color:var(--est-fg-faint)}.est2 .est2-collist{list-style:none;margin:0;padding:6px;overflow-y:auto;flex:1 1 auto;min-height:0}.est2 .est2-collist__row{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--est-radius-sm)}.est2 .est2-collist__row:hover{background:var(--est-bg-hover)}.est2 .est2-collist__row--drag{cursor:grab}.est2 .est2-collist__row--drag:active{cursor:grabbing}.est2 .est2-collist__row--dragging{opacity:.45;background:var(--est-bg-hover)}.est2 .est2-collist__row--drop-above{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 .est2-collist__row--drop-below{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 .est2-collist__grip{display:inline-flex;align-items:center;justify-content:center;color:var(--est-fg-faint);cursor:grab;flex:0 0 auto}.est2 .est2-collist__grip:active{cursor:grabbing}.est2 .est2-collist__row--drag:hover .est2-collist__grip{color:var(--est-fg-muted)}.est2 .est2-collist__grip--locked{color:var(--est-fg-faint);opacity:.55;cursor:default}.est2 .est2-collist__row--locked .est2-collist__label{color:var(--est-fg-muted)}.est2 .est2-collist__vis{display:inline-flex}.est2 .est2-collist__label{flex:1 1 auto}.est2 .est2-collist__path{color:var(--est-fg-faint);margin-right:4px}.est2 .est2-collist__ord{display:inline-flex;gap:4px}.est2 .est2-iconbtn{width:26px;height:26px;border:1px solid var(--est-border);background:var(--est-bg);border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer}.est2 .est2-iconbtn:hover:not(:disabled){background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-iconbtn:disabled{opacity:.4;cursor:default}.est2 .est2-dialog__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid var(--est-border)}.est2 .est2-dialog__spacer{flex:1 1 auto}.est2 .est2-btn{padding:7px 14px;border-radius:var(--est-radius-sm);font:inherit;font-weight:600;cursor:pointer;border:1px solid transparent}.est2 .est2-btn--ghost{background:transparent;color:var(--est-fg);border-color:var(--est-border)}.est2 .est2-btn--ghost:hover{background:var(--est-bg-hover)}.est2 .est2-btn--primary{background:var(--est-accent);color:var(--est-accent-fg)}.est2 .est2-btn--primary:hover{filter:brightness(1.05)}.est2 .est2-empty{padding:48px 16px;text-align:center;color:var(--est-fg-faint);font-size:var(--est-fs-sm)}.ngx-contextmenu{--ctx-bg: #ffffff;--ctx-fg: #1a1d24;--ctx-muted: #626b7a;--ctx-border: #e6e9ef;--ctx-hover: #f2f4f7;--ctx-accent: #4f46e5;--ctx-shadow: 0 10px 28px -8px rgba(16, 24, 40, .22), 0 2px 8px -3px rgba(16, 24, 40, .14);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.ngx-contextmenu .dropdown-menu{display:block;min-width:200px;margin:0;padding:6px;list-style:none;background:var(--ctx-bg);border:1px solid var(--ctx-border);border-radius:12px;box-shadow:var(--ctx-shadow);animation:est2-ctx-in .12s cubic-bezier(.16,1,.3,1)}.ngx-contextmenu li{list-style:none;margin:0}.ngx-contextmenu li>a{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:7px;color:var(--ctx-fg);font-size:13.5px;line-height:1.2;text-decoration:none;cursor:pointer;white-space:nowrap;transition:background .12s ease,color .12s ease}.ngx-contextmenu li>a:hover,.ngx-contextmenu li>a:focus{background:var(--ctx-hover);color:var(--ctx-fg);text-decoration:none;outline:none}.ngx-contextmenu li.divider,.ngx-contextmenu li[role=separator]{height:1px;margin:6px 8px;padding:0;background:var(--ctx-border)}.ngx-contextmenu li.disabled>a,.ngx-contextmenu li[aria-disabled=true]>a{color:var(--ctx-muted);opacity:.55;pointer-events:none}@keyframes est2-ctx-in{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@media(prefers-reduced-motion:reduce){.ngx-contextmenu .dropdown-menu{animation:none}}\n"], dependencies: [{ kind: "directive", type: i8.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i9.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i12.ContextMenuAttachDirective, selector: "[contextMenu]", inputs: ["contextMenuSubject", "contextMenu"] }, { kind: "component", type: i12.ContextMenuComponent, selector: "context-menu", inputs: ["menuClass", "autoFocus", "useBootstrap4", "disabled"], outputs: ["close", "open"] }, { kind: "directive", type: i12.ContextMenuItemDirective, selector: "[contextMenuItem]", inputs: ["subMenu", "divider", "enabled", "passive", "visible"], outputs: ["execute"] }, { kind: "component", type: EsTable2PagerComponent, selector: "es-table2-pager", inputs: ["page", "pages", "total", "itemsPerPage", "countLabel", "locale", "showCount", "showButtons", "showPagingOptions", "allowAll"], outputs: ["pageChange", "itemsPerPageChange"] }, { kind: "pipe", type: i3.LocalizePipe, name: "localize" }, { kind: "pipe", type: Est2FormatPipe, name: "est2_format" }, { kind: "pipe", type: Est2LookupPipe, name: "est2_lookup" }], viewProviders: [{ provide: LocalizationService, useClass: EsTable2Loc }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
10281
10388
|
}
|
|
10282
10389
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: EsTable2Component, decorators: [{
|
|
10283
10390
|
type: Component,
|
|
10284
|
-
args: [{ selector: 'es-table2', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, viewProviders: [{ provide: LocalizationService, useClass: EsTable2Loc }], template: "@if (view()) {\n <div class=\"est2-wrap {{ ContainerClass() }}\"\n [style.height]=\"Height()\"\n [style.background-color]=\"EmptySpaceBackgroundColor() || null\">\n\n <!-- Auto-aggiornamento: toggle + intervallo (in alto a destra) -->\n @if (AutoUpdate()) {\n <div class=\"est2-autoupdate\">\n <label class=\"est2-autoupdate__label\">\n {{ 'Update every' | localize : lc }}\n <input type=\"text\" maxlength=\"3\" class=\"est2-autoupdate__secs\"\n [ngModel]=\"seconds()\" (ngModelChange)=\"seconds.set($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"autoUpdateChanged()\" />\n {{ 'second/s' | localize : lc }}\n </label>\n <label class=\"est2-switch\" [title]=\"'Toggle auto-update' | localize : lc\">\n <input type=\"checkbox\" [ngModel]=\"autoUpdate()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"autoUpdate.set($event); autoUpdateChanged()\" />\n <span class=\"est2-switch__slider\"></span>\n </label>\n </div>\n }\n\n <!-- Pager superiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'top') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Riepilogo delle modifiche non ancora salvate. Sta nel flusso (non \u00E8 un\n overlay come la barra di selezione) perch\u00E9 deve restare leggibile anche\n quando l'utente \u00E8 su una pagina che non contiene le righe interessate. -->\n @if (pendingChangesOn() && hasPendingChanges) {\n <div class=\"est2-pendingbar\">\n @if (addedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--added\">\n <strong>{{ addedCount }}</strong>\n {{ (addedCount === 1 ? 'row added' : 'rows added') | localize : lc }}\n </span>\n }\n @if (removedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--removed\">\n <strong>{{ removedCount }}</strong>\n {{ (removedCount === 1 ? 'row to delete' : 'rows to delete') | localize : lc }}\n </span>\n }\n <span class=\"est2-pendingbar__spacer\"></span>\n @if (removedCount > 0) {\n <span class=\"est2-link\" (click)=\"restoreAllRemovals()\">{{ 'Undo the deletions' | localize : lc }}</span>\n }\n </div>\n }\n\n <!-- Host POSIZIONATO dello scroller: \u00E8 l'ancora di tutti gli overlay che devono\n coprire la TABELLA (barra di selezione, overlay di caricamento). Senza di esso\n erano ancorati a `.est2-wrap` e con [AutoUpdate] o il pager in alto finivano\n sopra quei controlli invece che sopra l'header. -->\n <div class=\"est2-scroll-host\">\n\n <!-- Barra \"seleziona tutto\" (visibile solo con selezione multipla attiva e righe presenti) -->\n @if (Selection() && !SingleSelection() && hasSelection) {\n <div class=\"est2-selectbar\"\n [class.est2-selectbar--inset]=\"chromeWidth() > 0\"\n [style.height.px]=\"selbarHeight() ? selbarHeight() + 1 : null\"\n [style.right.px]=\"chromeWidth() || null\">\n @if (allSelected) {\n <span>{{ 'All the' | localize : lc }} <strong>{{ selectedCount }}</strong> {{ 'elements are selected' | localize : lc }}</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ (selectedCount === 1 ? 'element selected' : 'elements selected') | localize : lc }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">{{ 'Select all' | localize : lc }} {{ totalCount() }} {{ 'elements' | localize : lc }}</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">{{ 'Reset selection' | localize : lc }}</span>\n </div>\n }\n\n <div class=\"est2-scroll\" #scrollRef [style.max-height.px]=\"MaxHeight()\" (scroll)=\"onScrollerScroll($event)\">\n <table class=\"est2-table {{ TableClass() }}\"\n [class.est2-table--range]=\"rangeActive()\"\n [class.est2-table--dragging]=\"rangeDragging()\"\n (mousedown)=\"onGridMouseDown($event)\"\n (mouseover)=\"onGridMouseOver($event)\">\n\n <!-- ================= HEADER ================= -->\n @if (!HeaderHidden()) {\n <thead #theadRef>\n <!-- Righe di header-group multi-livello (dall'alto verso il basso) -->\n @if (hasHeaderGroups()) {\n @for (grow of headerGroupRows(); track $index) {\n <tr class=\"est2-hgroup-row\">\n @if (Selection()) { <th class=\"est2-col-min est2-selcol\" [class.est2-pinned]=\"hasPinned()\" [style.left.px]=\"hasPinned() ? 0 : null\"></th> }\n @for (op of DynamicOperations(); track op.id) { <th class=\"est2-col-min\"></th> }\n @for (cell of grow; track cell.id; let gi = $index) {\n <th [attr.colspan]=\"cell.span\"\n [attr.data-groupid]=\"cell.isGroup ? cell.id : null\"\n [class.est2-hgroup]=\"cell.isGroup\"\n [class.est2-pinned]=\"gi < pinnedCount()\"\n [style.left.px]=\"gi < pinnedCount() ? pinnedLeftPx(gi) : null\"\n class=\"est2-hgroup-cell\">\n @if (cell.isGroup) {\n @if (cell.template) {\n <ng-container *ngTemplateOutlet=\"cell.template\"></ng-container>\n } @else {\n {{ cell.label }}\n }\n }\n </th>\n }\n @if (Removal()) { <th class=\"est2-col-min\"></th> }\n @if (hasChrome()) { <th class=\"est2-col-min est2-chrome-col\"></th> }\n </tr>\n }\n }\n <tr>\n <!-- Colonna di selezione -->\n @if (Selection()) {\n <th class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"globalCheck()\"\n [indeterminate]=\"selectionIndeterminate\"\n [disabled]=\"SelectionDisabled()\"\n (change)=\"toggleAll()\"\n [attr.aria-label]=\"'Select all rows' | localize : lc\" />\n }\n </th>\n }\n\n <!-- Header da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- intestazioni vuote per le operazioni dinamiche -->\n @for (op of DynamicOperations(); track op.id) {\n <th class=\"est2-col-min\"></th>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let ci = $index) {\n @let hs = colStyles()[ci];\n <th [attr.data-colid]=\"col.id\"\n [class]=\"col.headerClass\"\n [class.est2-th--orderable]=\"OrderByColumn() && col.orderable\"\n [class.est2-col-min]=\"col.header?.thShrink\"\n [class.est2-col-groupstart]=\"hs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [style.left.px]=\"hs.left\"\n [style.width.px]=\"hs.width\"\n [style.min-width.px]=\"hs.width\"\n [style.max-width.px]=\"hs.width\"\n [style.text-align]=\"col.alignment\"\n [style.background-color]=\"col.headerBg || null\"\n (click)=\"toggleSort(col)\">\n <span class=\"est2-th__inner\">\n @if (col.header?.Template) {\n <ng-container *ngTemplateOutlet=\"col.header!.Template!; context: col.multiProp != null ? { $implicit: col.headerText } : null\"></ng-container>\n } @else {\n {{ col.headerText }}\n }\n @if (OrderByColumn() && col.orderable && orderOf(col.id)) {\n <span class=\"est2-sort est2-sort--active\"\n [class.est2-sort--desc]=\"orderOf(col.id) === 'DESC'\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 15l6-6 6 6\"/></svg>\n </span>\n @if (orderIndex(col.id) > 0 && MultipleOrderingDirectives()) {\n <span class=\"est2-sort__badge\">{{ orderIndex(col.id) }}</span>\n }\n }\n <!-- Indicatore/toggle di pin (visibile se pinnata o all'hover) -->\n @if (ColumnsPinnable() && col.multiProp == null && !col.fixed) {\n <button type=\"button\" class=\"est2-pinbtn\"\n [class.est2-pinbtn--on]=\"col.pinned\"\n [title]=\"(col.pinned ? 'Unlock column' : 'Lock column to the left') | localize : lc\"\n (click)=\"togglePin(col, $event)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n </span>\n <!-- Handle di ridimensionamento (colonne pinnate, gated da ColumnsResizable) -->\n @if (col.pinned || ColumnsResizable()) {\n <span class=\"est2-resize-handle\" [title]=\"'Drag to resize' | localize : lc\"\n (mousedown)=\"startColumnResize(col, $event)\"\n (click)=\"$event.stopPropagation()\"></span>\n }\n </th>\n }\n }\n <!-- Header da template semplice -->\n @else if (headerRef) {\n <ng-container *ngTemplateOutlet=\"headerRef\"></ng-container>\n }\n\n <!-- Colonna rimozione -->\n @if (Removal()) { <th class=\"est2-col-min\"></th> }\n <!-- Colonna chrome (export / gestione colonne / menu) -->\n @if (hasChrome()) {\n <th class=\"est2-col-min est2-chrome-th est2-chrome-col\">\n <div class=\"est2-chrome\">\n <!-- I pannelli dei menu NON vivono qui: sono renderizzati a fondo\n componente e posizionati `fixed`, altrimenti l'`overflow:auto`\n di `.est2-scroll` li taglierebbe su tabelle basse. -->\n @if (Export()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Export' | localize : lc\" (click)=\"$event.stopPropagation(); toggleExportMenu($event)\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><path d=\"M7 10l5 5 5-5\"/><path d=\"M12 15V3\"/></svg>\n </button>\n }\n @if (HiddenColumns() || ColumnsOrdering()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Columns' | localize : lc\" (click)=\"$event.stopPropagation(); openColumnsDialog()\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\"/><path d=\"M9 3v18\"/><path d=\"M15 3v18\"/></svg>\n </button>\n }\n @if (CornerMenuOptions().length > 0) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Options' | localize : lc\" (click)=\"$event.stopPropagation(); toggleCornerMenu($event)\">\u22EE</button>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n\n <!-- ================= BODY ================= -->\n @if (!BodyHidden()) {\n <tbody>\n <!-- Valori invarianti per l'intero corpo: senza questi verrebbero\n ricalcolati riga\u00D7colonna volte a ogni ciclo di change-detection. -->\n @let rng = rangeActive();\n @let rect = rangeRect();\n @let errs = hasCellErrors();\n @let vw = virtualWindow();\n @if (vw && vw.padTop > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padTop\"></td></tr>\n }\n @for (item of renderedRows(); track trackRow($index, item); let vi = $index) {\n <!-- indice ASSOLUTO: range, editing e data-r ragionano su boundSource() -->\n @let ri = vStart() + vi;\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--zebra]=\"vw && (ri % 2 === 1)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted || item._removed\"\n [class.est2-row--added]=\"item._localId != null\"\n [class.est2-row--group]=\"item._group\"\n [class.est2-row--clickable]=\"Selection() || item._group\"\n [contextMenu]=\"ContextMenu || emptyMenu\"\n [contextMenuSubject]=\"item\"\n (click)=\"handleRowClick(item, $event)\">\n\n <!-- Cella di selezione -->\n @if (Selection()) {\n <td class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (item._group) {\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"groupSelectionState(item) === 'all'\"\n [indeterminate]=\"groupSelectionState(item) === 'some'\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection(item)\"\n [attr.aria-label]=\"'Select group' | localize : lc\" />\n }\n } @else {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"item._selected\"\n [indeterminate]=\"hierarchyIndeterminate(item)\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(item)\"\n [attr.aria-label]=\"'Select row' | localize : lc\" />\n }\n </td>\n }\n\n <!-- Celle da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- Operazioni dinamiche (icone a sinistra) -->\n @for (op of DynamicOperations(); track op.id) {\n <td class=\"est2-col-min est2-op-cell\">\n @if (!item._group && operationVisible(op, item)) {\n <span class=\"est2-op\" [class]=\"op.iconClass || ''\" [title]=\"op.title\"\n (click)=\"$event.stopPropagation(); dynamicOperation(item, op.id)\">{{ op.text }}</span>\n }\n </td>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let first = $first, ci = $index) {\n @let cs = colStyles()[ci];\n @let insel = rng && inRange(ri, ci);\n @let editing = isEditing(ri, ci);\n <td [class]=\"col.cssClass\"\n [style.text-align]=\"col.alignment\"\n [style.color]=\"cellColor(item, col, 'fore')\"\n [style.background-color]=\"cellColor(item, col, 'back')\"\n [class.est2-nowrap]=\"!col.wrap\"\n [class.est2-col-groupstart]=\"cs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-fixedw]=\"cs.width != null\"\n [style.left.px]=\"cs.left\"\n [style.width.px]=\"cs.width\"\n [style.min-width.px]=\"cs.width\"\n [style.max-width.px]=\"cs.width\"\n [class.est2-td--group-key]=\"item._group && item.column === col.id\"\n [attr.data-r]=\"rng && !item._group ? ri : null\"\n [attr.data-c]=\"rng && !item._group ? ci : null\"\n [class.est2-cell-sel]=\"insel\"\n [class.est2-cell-sel-t]=\"insel && ri === rect!.top\"\n [class.est2-cell-sel-b]=\"insel && ri === rect!.bottom\"\n [class.est2-cell-sel-l]=\"insel && ci === rect!.left\"\n [class.est2-cell-sel-r]=\"insel && ci === rect!.right\"\n [class.est2-cell-editing]=\"editing\"\n [class.est2-cell-invalid]=\"errs && !!cellError(item, col)\"\n [attr.data-error]=\"errs ? (cellError(item, col) || null) : null\"\n (dblclick)=\"onCellDblClick(item, col, ri, ci)\">\n @if (editing) {\n @if (editorFor(col); as edTpl) {\n <ng-container *ngTemplateOutlet=\"edTpl; context: editorContext(item, col)\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"defaultEditor; context: { $implicit: item, col: col }\"></ng-container>\n }\n } @else if (item._group) {\n @if (item.column === col.id) {\n <span class=\"est2-group-key\" [style.padding-left.px]=\"groupIndent(item)\">\n <span class=\"est2-group-chevron\" [class.est2-group-chevron--open]=\"item._expanded\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n {{ groupCellDisplay(item, col) }}\n </span>\n } @else {\n {{ groupCellDisplay(item, col) }}\n }\n } @else {\n <!-- Navigatore albero nella prima colonna -->\n @if (Hierarchy() && first) {\n <span class=\"est2-hier-lead\" [style.padding-left.px]=\"hierarchyIndent(item)\">\n @if (item.parent) {\n <span class=\"est2-group-chevron est2-hier-toggle\" [class.est2-group-chevron--open]=\"item._expanded\"\n (click)=\"$event.stopPropagation(); toggleHierarchyNode(item)\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-hier-toggle\"></span>\n }\n </span>\n }\n @if (itemCellHidden(col)) {\n <!-- colonna di gruppo nascosta sulla riga-oggetto -->\n } @else if (col.multiProp != null) {\n @if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: multiCell(item, col) }\"></ng-container>\n } @else {\n {{ multiCell(item, col)?.value }}\n }\n } @else if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: item }\"></ng-container>\n } @else if (col.routePath) {\n <a class=\"est2-link\" [routerLink]=\"routerLinkFor(item, col)\">{{ cellValue(item, col) }}</a>\n } @else if (col.propAccessor != null) {\n {{ reportCellDisplay(item, col) }}\n } @else if (col.type === 'enum') {\n {{ (cellValue(item, col) | est2_lookup : col.source).description }}\n } @else {\n {{ cellValue(item, col) | est2_format : col.type : col.format : locale }}\n }\n }\n </td>\n }\n }\n <!-- Celle da template semplice -->\n @else if (bodyRef) {\n <ng-container *ngTemplateOutlet=\"bodyRef; context: { $implicit: item }\"></ng-container>\n }\n\n <!-- Rimozione (non sulle righe-gruppo) -->\n @if (Removal()) {\n <td class=\"est2-col-min\">\n @if (!item._group && canRemove(item)) {\n @if (item.removed || item.deleted || item._removed) {\n <button type=\"button\" class=\"est2-rowaction\" [title]=\"'Restore' | localize : lc\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" [title]=\"'Remove' | localize : lc\" (click)=\"removeItem(item)\">\u2715</button>\n }\n }\n </td>\n }\n <!-- Chrome -->\n @if (hasChrome()) { <td class=\"est2-col-min est2-chrome-col\"></td> }\n </tr>\n }\n } @empty {\n <tr>\n <td class=\"est2-empty\" [attr.colspan]=\"totalColspan()\">{{ 'No elements to display' | localize : lc }}</td>\n </tr>\n }\n @if (vw && vw.padBottom > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padBottom\"></td></tr>\n }\n </tbody>\n }\n </table>\n </div>\n\n <!-- Overlay di caricamento: FUORI dallo scroller, cos\u00EC copre il viewport della\n tabella senza scorrere col contenuto e senza essere bucato dalle celle sticky -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>{{ 'Loading\u2026' | localize : lc }}</span>\n </div>\n }\n\n </div>\n\n <!-- Pager inferiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Menu del chrome (export / opzioni). Renderizzati FUORI da `.est2-scroll` e\n posizionati `fixed` sulle coordinate del bottone che li ha aperti: cos\u00EC non\n vengono tagliati dall'`overflow:auto` dello scroller su tabelle basse, e\n restano dentro `.est2` (i token `--est-*` continuano a ereditare). -->\n @if (exportMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @if (CSVExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">{{ 'Export Excel (XLSX)' | localize : lc }}</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n </div>\n }\n @if (cornerMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @for (opt of CornerMenuOptions(); track opt.id) {\n <button type=\"button\" class=\"est2-menu-item\" (click)=\"cornerAction(opt.id); closeChromeMenus()\">{{ opt.description }}</button>\n }\n </div>\n }\n\n <!-- Avviso transitorio (es. incolla con dimensioni incompatibili) -->\n @if (notice()) {\n <div class=\"est2-toast\" role=\"alert\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/></svg>\n <span>{{ notice() }}</span>\n </div>\n }\n\n <!-- Dialog visibilit\u00E0 / ordine colonne -->\n @if (columnsDialogOpen()) {\n <div class=\"est2-dialog-backdrop\" #dialogBackdrop\n [class.est2-dialog-backdrop--embedded]=\"embedded\"\n (click)=\"closeColumnsDialog()\">\n <div class=\"est2-dialog\" (click)=\"$event.stopPropagation()\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"est2-dialog__head\">\n <span>\n @if (HiddenColumns() && ColumnsOrdering()) { {{ 'Columns visibility and order' | localize : lc }} }\n @else if (HiddenColumns()) { {{ 'Columns visibility' | localize : lc }} }\n @else { {{ 'Columns order' | localize : lc }} }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" [attr.aria-label]=\"'Close' | localize : lc\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">{{ 'Show all' | localize : lc }}</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">{{ 'Hide all' | localize : lc }}</button>\n </div>\n }\n\n <ul class=\"est2-collist\">\n @for (c of dialogCols(); track c.id; let i = $index) {\n <li class=\"est2-collist__row\"\n [attr.draggable]=\"ColumnsOrdering() && canReorder(c) ? true : null\"\n [class.est2-collist__row--drag]=\"ColumnsOrdering() && canReorder(c)\"\n [class.est2-collist__row--locked]=\"!canReorder(c)\"\n [class.est2-collist__row--dragging]=\"dragIndex() === i\"\n [class.est2-collist__row--drop-above]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! > i\"\n [class.est2-collist__row--drop-below]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! < i\"\n (dragstart)=\"dialogDragStart(i, $event)\"\n (dragover)=\"dialogDragOver(i, $event)\"\n (drop)=\"dialogDrop(i, $event)\"\n (dragend)=\"dialogDragEnd()\">\n @if (ColumnsOrdering()) {\n @if (canReorder(c)) {\n <span class=\"est2-collist__grip\" [title]=\"'Drag to reorder' | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"currentColor\"><circle cx=\"9\" cy=\"6\" r=\"1.6\"/><circle cx=\"15\" cy=\"6\" r=\"1.6\"/><circle cx=\"9\" cy=\"12\" r=\"1.6\"/><circle cx=\"15\" cy=\"12\" r=\"1.6\"/><circle cx=\"9\" cy=\"18\" r=\"1.6\"/><circle cx=\"15\" cy=\"18\" r=\"1.6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-collist__grip est2-collist__grip--locked\"\n [title]=\"(c.fixed ? 'Fixed column: cannot be moved' : 'Column locked to the left: cannot be moved') | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"4\" y=\"11\" width=\"16\" height=\"10\" rx=\"2\"/><path d=\"M8 11V7a4 4 0 0 1 8 0v4\"/></svg>\n </span>\n }\n }\n @if (HiddenColumns()) {\n <label class=\"est2-collist__vis\" [title]=\"c.fixed ? ('Fixed column: always visible' | localize : lc) : ''\">\n <input type=\"checkbox\" class=\"est2-check\" [checked]=\"c.visible\" [disabled]=\"c.fixed\" (change)=\"dialogToggle(i)\" />\n </label>\n }\n <span class=\"est2-collist__label\">\n @if (c.tmpl) {\n <!-- colonna non renderizzata (Visible:false / nascosta): il testo\n header non esiste nel DOM, quindi rendo il template dell'`*th` -->\n @if (c.prefix) { <span class=\"est2-collist__path\">{{ c.prefix }}</span> }\n <ng-container *ngTemplateOutlet=\"c.tmpl; context: c.tmplCtx\"></ng-container>\n } @else {\n {{ c.label }}\n }\n </span>\n @if (ColumnsPinnable()) {\n <button type=\"button\" class=\"est2-iconbtn est2-collist__pin\" [class.est2-collist__pin--on]=\"c.pinned\"\n [disabled]=\"c.grouped || c.fixed\"\n [title]=\"(c.fixed ? 'Fixed columns cannot be locked' : (c.grouped ? 'Grouped columns cannot be locked' : (c.pinned ? 'Unlock' : 'Lock to the left'))) | localize : lc\"\n (click)=\"dialogTogglePin(i)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n @if (ColumnsOrdering()) {\n <span class=\"est2-collist__ord\">\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Up' | localize : lc\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Down' | localize : lc\" (click)=\"dialogMove(i, 1)\"\n [disabled]=\"i === dialogCols().length - 1 || !canReorder(c) || !canReorder(dialogCols()[i + 1])\">\u2193</button>\n </span>\n }\n </li>\n }\n </ul>\n\n <div class=\"est2-dialog__foot\">\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"resetColumnsDialog()\"\n [title]=\"'Restore the declared columns and delete the saved preferences for this table' | localize : lc\">{{ 'Reset' | localize : lc }}</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">{{ 'Cancel' | localize : lc }}</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">{{ 'Apply' | localize : lc }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n}\n\n<!-- Template pager riutilizzabile sopra/sotto -->\n<ng-template #pager>\n @if (!HidePaging() && !grouped()) {\n <es-table2-pager\n [page]=\"currentPage()\"\n [pages]=\"totalPages()\"\n [total]=\"totalCount()\"\n [locale]=\"locale\"\n [itemsPerPage]=\"viewMode() ? (view()?.itemsperpageoverride ?? 15) : ArraymodeItemsPerPage()\"\n [countLabel]=\"CountLabel()\"\n [showCount]=\"!HidePagingCount()\"\n [showButtons]=\"!HidePagingButtons()\"\n [showPagingOptions]=\"!HidePagingButtons()\"\n [allowAll]=\"AllSearch()\"\n (pageChange)=\"goToPage($event)\"\n (itemsPerPageChange)=\"changeItemsPerPage($event)\">\n </es-table2-pager>\n }\n</ng-template>\n\n<!-- Editor di cella di default (usato quando il consumer non fornisce un `*editor`) -->\n<ng-template #defaultEditor let-item let-col=\"col\">\n @switch (col.type) {\n @case ('enum') {\n <select class=\"est2-editor-input\"\n [value]=\"editDraft\"\n (change)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\">\n @for (o of col.source || []; track o.id) {\n <option [value]=\"o.id\" [selected]=\"o.id == editDraft\">{{ o.description }}</option>\n }\n </select>\n }\n @case ('boolean') {\n <input type=\"checkbox\" class=\"est2-editor-input est2-check\"\n [checked]=\"editDraft === true || editDraft === 'true'\"\n (change)=\"editDraft = $any($event.target).checked; commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\" />\n }\n @default {\n <input class=\"est2-editor-input\"\n [type]=\"editorInputType(col.type)\"\n [value]=\"editDraft\"\n (input)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\" />\n }\n }\n</ng-template>\n\n<!-- Menu di default (nessuna operazione) usato quando il consumer non passa [ContextMenu] -->\n<context-menu #emptyMenu>\n <ng-template contextMenuItem [passive]=\"true\"><em>{{ 'No operations available\u2026' | localize : lc }}</em></ng-template>\n</context-menu>\n", styles: [".est2{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent);--est-radius: 10px;--est-radius-sm: 6px;--est-radius-pill: 999px;--est-gap: 8px;--est-cell-py: 6px;--est-cell-px: 12px;--est-row-h: 33px;--est-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--est-fs: 13px;--est-fs-sm: 12px;--est-fw-head: 700;--est-transition: .14s cubic-bezier(.4, 0, .2, 1);--est-bg-zebra: color-mix(in srgb, var(--est-fg) 3.5%, var(--est-bg));--est-z-menu: 1000;--est-z-modal: 1010;--est-z-toast: 1020;font-family:var(--est-font);font-size:var(--est-fs);color:var(--est-fg);position:relative;display:block}.est2.est2--dark{--est-bg: #14161c;--est-bg-subtle: #1a1d25;--est-bg-raised: #1e222b;--est-bg-hover: #232733;--est-bg-selected: color-mix(in srgb, var(--est-accent) 26%, transparent);--est-fg: #e7eaf0;--est-fg-muted: #9aa3b2;--est-fg-faint: #6b7484;--est-border: #2a2f3a;--est-border-strong: #39404d;--est-accent: #7c74ff;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 22%, transparent);--est-danger: #f87171;--est-warning: #fbbf24;--est-success: #34d399;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 6px 16px -10px rgba(0, 0, 0, .7);--est-shadow-pop: 0 10px 28px -8px rgba(0, 0, 0, .6), 0 2px 6px -2px rgba(0, 0, 0, .5);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 55%, transparent)}.est2.est2--light{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent)}.est2.est2--dense{--est-cell-py: 3px;--est-cell-px: 9px;--est-row-h: 27px;--est-fs: 12.5px}.est2 *,.est2 *:before,.est2 *:after{box-sizing:border-box}.est2 .est2-scroll{position:relative;width:100%;overflow:auto;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg);-webkit-overflow-scrolling:touch}.est2 table.est2-table{width:100%;border-collapse:separate;border-spacing:0;background:var(--est-bg)}.est2 thead th{position:sticky;top:0;z-index:3;background:var(--est-bg-subtle);color:var(--est-fg);font-weight:var(--est-fw-head);font-size:var(--est-fs);text-align:left;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);box-shadow:var(--est-shadow-sticky);-webkit-user-select:none;user-select:none}.est2 thead tr.est2-hgroup-row th{font-size:var(--est-fs-sm);font-weight:700;color:var(--est-fg-muted);text-align:center;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);background:var(--est-bg-subtle);border-bottom:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th.est2-hgroup{color:var(--est-fg);border-left:1px solid var(--est-border);border-right:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th:not(.est2-hgroup){background:var(--est-bg-subtle);border-bottom-color:transparent}.est2 thead tr:last-child th.est2-col-groupstart,.est2 tbody td.est2-col-groupstart{border-left:1px solid var(--est-border)}.est2 tbody td{padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);color:var(--est-fg);vertical-align:middle;background:transparent}.est2 tbody tr:nth-child(2n) td{background:var(--est-bg-zebra)}.est2 tbody tr:nth-child(2n) td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-row--zebra td,.est2 tbody tr.est2-row--zebra td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-vspacer td{padding:0;border:none;background:transparent}.est2 tbody tr.est2-vspacer:hover td{background:transparent}.est2 th.est2-pinned,.est2 td.est2-pinned{position:sticky;background:var(--est-bg);box-shadow:1px 0 0 var(--est-border)}.est2 .est2-resize-handle{position:absolute;top:0;right:-3px;width:10px;height:100%;cursor:col-resize;z-index:2;touch-action:none;-webkit-user-select:none;user-select:none}.est2 .est2-resize-handle:after{content:\"\";position:absolute;top:28%;bottom:28%;right:3px;width:2px;border-radius:2px;background:color-mix(in srgb,var(--est-fg) 30%,transparent);transition:background var(--est-transition),top var(--est-transition),bottom var(--est-transition)}.est2 .est2-resize-handle:hover:after{background:var(--est-accent);top:15%;bottom:15%}.est2 .est2-resize-handle:active:after{background:var(--est-accent);top:6%;bottom:6%}.est2 td.est2-fixedw{overflow:hidden;text-overflow:ellipsis}.est2 td.est2-cell-invalid{background:color-mix(in srgb,var(--est-danger) 10%,var(--est-bg));box-shadow:inset 0 0 0 1px var(--est-danger);color:var(--est-danger)}.est2 .est2-selcol{width:44px;min-width:44px;max-width:44px}.est2 td.est2-pinned{z-index:3}.est2 thead th.est2-pinned,.est2 thead tr.est2-hgroup-row th.est2-pinned{z-index:6;background:var(--est-bg-subtle)}.est2 tbody tr:hover td.est2-pinned{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-pinned{background:var(--est-bg-subtle)}.est2 th.est2-chrome-col,.est2 td.est2-chrome-col{position:sticky;right:0;box-shadow:-1px 0 0 var(--est-border)}.est2 thead th.est2-chrome-col{z-index:16;background:var(--est-bg-subtle)}.est2 td.est2-chrome-col{z-index:4;background:var(--est-bg)}.est2 tbody tr:nth-child(2n) td.est2-chrome-col{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-chrome-col{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-chrome-col{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-chrome-col{background:var(--est-bg-subtle)}.est2 .est2-pinbtn{display:inline-flex;align-items:center;justify-content:center;margin-left:4px;padding:2px;border:none;background:transparent;color:var(--est-fg-faint);border-radius:var(--est-radius-sm);cursor:pointer;opacity:0;transition:opacity var(--est-transition),color var(--est-transition),background var(--est-transition)}.est2 thead th:hover .est2-pinbtn{opacity:.7}.est2 .est2-pinbtn:hover{background:var(--est-bg-hover);color:var(--est-fg);opacity:1}.est2 .est2-pinbtn--on{opacity:1;color:var(--est-accent);transform:rotate(0)}.est2 thead th:hover .est2-pinbtn--on{opacity:1}.est2 .est2-collist__pin.est2-collist__pin--on{color:var(--est-accent);border-color:var(--est-accent)}.est2 tbody tr{height:var(--est-row-h);transition:background var(--est-transition)}.est2 tbody tr:last-child td{border-bottom:none}.est2 tbody tr:hover td{background:var(--est-bg-hover)}.est2 tbody tr.est2-row--selected td{background:var(--est-bg-selected)}.est2 tbody tr.est2-row--clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.est2 tbody tr.est2-row--added td{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 tbody tr.est2-row--added td:first-child{box-shadow:inset 3px 0 0 var(--est-accent)}.est2 tbody tr.est2-row--added td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 .est2-pendingbar{display:flex;align-items:center;gap:10px;padding:7px 12px;margin-bottom:8px;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg-subtle);font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-pendingbar__spacer{flex:1 1 auto}.est2 .est2-pendingbar__chip{display:inline-flex;align-items:center;gap:5px;padding:2px 10px;border-radius:var(--est-radius-pill)}.est2 .est2-pendingbar__chip strong{color:var(--est-fg)}.est2 .est2-pendingbar__chip--added{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 .est2-pendingbar__chip--removed{background:color-mix(in srgb,var(--est-danger) 14%,transparent)}.est2 tbody tr.est2-row--removed td{text-decoration:line-through;color:var(--est-fg-faint)}.est2 .est2-table--range tbody td[data-r]{cursor:cell}.est2 .est2-table--dragging,.est2 .est2-table--dragging tbody td{-webkit-user-select:none;user-select:none}.est2 tbody td.est2-cell-sel{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 tbody td.est2-cell-sel-t{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l{box-shadow:inset 2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-r{box-shadow:inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b{box-shadow:inset 0 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 0 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px -2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-editing{padding:2px 6px}.est2 .est2-editor-input{width:100%;box-sizing:border-box;height:calc(var(--est-row-h) - 8px);padding:2px 6px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1.5px solid var(--est-accent);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-editor-input:focus-visible{box-shadow:var(--est-ring)}.est2 input.est2-editor-input[type=checkbox]{width:16px;height:16px}.est2 .est2-autoupdate{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:4px 2px 8px;font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-autoupdate__label{display:inline-flex;align-items:center;gap:6px}.est2 .est2-autoupdate__secs{width:44px;text-align:center;padding:3px 4px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-autoupdate__secs:focus-visible{box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2 .est2-switch{position:relative;display:inline-flex;width:38px;height:20px;cursor:pointer}.est2 .est2-switch input{position:absolute;opacity:0;width:0;height:0}.est2 .est2-switch__slider{flex:1;border-radius:var(--est-radius-pill);background:var(--est-border-strong);transition:background var(--est-transition)}.est2 .est2-switch__slider:before{content:\"\";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px #00000040;transition:transform var(--est-transition)}.est2 .est2-switch input:checked+.est2-switch__slider{background:var(--est-accent)}.est2 .est2-switch input:checked+.est2-switch__slider:before{transform:translate(18px)}.est2 .est2-switch input:focus-visible+.est2-switch__slider{box-shadow:var(--est-ring)}.est2 .est2-toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%);z-index:var(--est-z-toast);pointer-events:none;display:inline-flex;align-items:center;gap:8px;max-width:min(560px,100vw - 24px);padding:8px 14px;font-size:13px;font-weight:500;color:#fff;background:#b91c1c;border-radius:var(--est-radius);box-shadow:0 6px 20px #00000040;animation:est2-toast-in .16s ease-out}.est2 .est2-toast svg{flex:0 0 auto}@keyframes est2-toast-in{0%{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%)}}@media(prefers-reduced-motion:reduce){.est2 .est2-toast{animation:none}}.est2 tbody tr.est2-row--group{cursor:pointer;-webkit-user-select:none;user-select:none}.est2 tbody tr.est2-row--group td{background:var(--est-bg-subtle);font-weight:600;color:var(--est-fg);border-bottom:1px solid var(--est-border)}.est2 tbody tr.est2-row--group:hover td{background:var(--est-bg-hover)}.est2 .est2-td--group-key{color:var(--est-fg)}.est2 .est2-group-key{display:inline-flex;align-items:center;gap:6px}.est2 .est2-group-chevron{display:inline-flex;color:var(--est-fg-muted);transition:transform var(--est-transition)}.est2 .est2-group-chevron--open{transform:rotate(90deg)}.est2 .est2-hier-lead{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px}.est2 .est2-hier-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex:none}.est2 .est2-group-chevron.est2-hier-toggle{cursor:pointer;border-radius:var(--est-radius-sm);transition:transform var(--est-transition),background var(--est-transition),color var(--est-transition)}.est2 .est2-group-chevron.est2-hier-toggle:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 th.est2-th--orderable{cursor:pointer;transition:color var(--est-transition)}.est2 th.est2-th--orderable:hover{color:var(--est-fg)}.est2 .est2-th__inner{display:inline-flex;align-items:center;gap:6px}.est2 .est2-sort{display:inline-flex;width:14px;height:14px;opacity:.35;transition:opacity var(--est-transition),transform var(--est-transition)}.est2 .est2-sort--active{opacity:1;color:var(--est-accent)}.est2 .est2-sort--desc{transform:rotate(180deg)}.est2 .est2-sort__badge{font-size:9px;font-weight:700;color:var(--est-accent);margin-left:2px}.est2 .est2-check{appearance:none;width:16px;height:16px;border:1.5px solid var(--est-border-strong);border-radius:var(--est-radius-sm);background:var(--est-bg);cursor:pointer;position:relative;transition:border-color var(--est-transition),background var(--est-transition);vertical-align:middle;flex:none}.est2 .est2-check:hover{border-color:var(--est-accent)}.est2 .est2-check:checked{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:checked:after{content:\"\";position:absolute;left:4.5px;top:1.5px;width:4px;height:8px;border:solid var(--est-accent-fg);border-width:0 2px 2px 0;transform:rotate(45deg)}.est2 .est2-check:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2 .est2-check:disabled{opacity:.45;cursor:not-allowed}.est2 .est2-check:disabled:hover{border-color:var(--est-border-strong)}.est2 .est2-check:indeterminate{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--est-accent-fg);transform:none;border:none}.est2 th.est2-col-min,.est2 td.est2-col-min{width:1%;white-space:nowrap}.est2 .est2-wrap,.est2 .est2-scroll-host{position:relative}.est2 .est2-selectbar{position:absolute;top:0;left:0;right:0;z-index:15;display:flex;align-items:center;gap:12px;padding:9px 14px;font-size:var(--est-fs-sm);color:var(--est-fg);background:var(--est-bg-subtle);border:none;border-radius:var(--est-radius) var(--est-radius) 0 0;box-shadow:inset 3px 0 0 var(--est-accent)}.est2 .est2-selectbar--inset{border-top-right-radius:0}.est2 .est2-selectbar strong{color:var(--est-fg);font-weight:700}.est2 .est2-selectbar__spacer{flex:1 1 auto}.est2 .est2-link{color:var(--est-accent);cursor:pointer;font-weight:600}.est2 .est2-link:hover{text-decoration:underline}.est2 .est2-rowaction{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);border:none;background:transparent;color:var(--est-fg-faint);cursor:pointer;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-rowaction:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-rowaction--danger:hover{color:var(--est-danger)}.est2 .est2-op-cell{text-align:center}.est2 .est2-op{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;padding:0 6px;border-radius:var(--est-radius-sm);color:var(--est-accent);cursor:pointer;font-size:var(--est-fs-sm);transition:background var(--est-transition)}.est2 .est2-op:hover{background:var(--est-accent-weak)}.est2 .est2-loading{position:absolute;inset:0;border-radius:var(--est-radius);z-index:20;display:flex;align-items:center;justify-content:center;gap:10px;background:color-mix(in srgb,var(--est-bg) 70%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);color:var(--est-fg-muted);font-size:var(--est-fs-sm)}.est2 .est2-spinner{width:16px;height:16px;border:2px solid var(--est-border-strong);border-top-color:var(--est-accent);border-radius:50%;animation:est2-spin .7s linear infinite}@keyframes est2-spin{to{transform:rotate(360deg)}}.est2 .est2-nowrap{white-space:nowrap}.est2 .est2-cornermenu{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);cursor:pointer;color:var(--est-fg-muted);transition:background var(--est-transition)}.est2 .est2-cornermenu:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-chrome-th{position:relative}.est2 .est2-chrome{display:inline-flex;align-items:center;gap:2px}.est2 .est2-chrome-wrap{position:relative;display:inline-flex}.est2 .est2-chrome-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:none;background:transparent;border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer;font-size:16px;line-height:1;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-chrome-btn:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-menu{position:fixed;z-index:var(--est-z-menu);min-width:180px;padding:4px;background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 8px 24px #0000002e;display:flex;flex-direction:column}.est2 .est2-menu-item{display:block;width:100%;padding:8px 10px;border:none;background:transparent;text-align:left;font:inherit;color:var(--est-fg);border-radius:var(--est-radius-sm);cursor:pointer}.est2 .est2-menu-item:hover{background:var(--est-bg-hover)}.est2 .est2-dialog-backdrop{position:fixed;inset:0;z-index:var(--est-z-modal);display:flex;align-items:center;justify-content:center;padding:16px}.est2 .est2-dialog-backdrop:before{content:\"\";position:absolute;inset:0;background-color:#000;opacity:.5}.est2 .est2-dialog{position:relative}.est2 .est2-dialog-backdrop--embedded{--est-embed-offset: 110px;padding-top:5%;margin-left:calc(-1 * var(--est-embed-offset));width:calc(100% + var(--est-embed-offset));overflow:hidden}.est2 .est2-dialog-backdrop--embedded .est2-dialog{max-height:600px}.est2 .est2-dialog{width:520px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;background:var(--est-bg);color:var(--est-fg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 16px 48px #0000004d;overflow:hidden}.est2 .est2-dialog__head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;font-weight:700;border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__close{border:none;background:transparent;cursor:pointer;color:var(--est-fg-muted);font-size:15px;line-height:1;padding:4px;border-radius:var(--est-radius-sm)}.est2 .est2-dialog__close:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-dialog__tools{padding:8px 14px;font-size:var(--est-fs-sm);border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__tools .est2-link{border:none;background:none;padding:0;font:inherit;font-weight:600}.est2 .est2-dialog__sep{margin:0 6px;color:var(--est-fg-faint)}.est2 .est2-collist{list-style:none;margin:0;padding:6px;overflow-y:auto;flex:1 1 auto;min-height:0}.est2 .est2-collist__row{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--est-radius-sm)}.est2 .est2-collist__row:hover{background:var(--est-bg-hover)}.est2 .est2-collist__row--drag{cursor:grab}.est2 .est2-collist__row--drag:active{cursor:grabbing}.est2 .est2-collist__row--dragging{opacity:.45;background:var(--est-bg-hover)}.est2 .est2-collist__row--drop-above{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 .est2-collist__row--drop-below{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 .est2-collist__grip{display:inline-flex;align-items:center;justify-content:center;color:var(--est-fg-faint);cursor:grab;flex:0 0 auto}.est2 .est2-collist__grip:active{cursor:grabbing}.est2 .est2-collist__row--drag:hover .est2-collist__grip{color:var(--est-fg-muted)}.est2 .est2-collist__grip--locked{color:var(--est-fg-faint);opacity:.55;cursor:default}.est2 .est2-collist__row--locked .est2-collist__label{color:var(--est-fg-muted)}.est2 .est2-collist__vis{display:inline-flex}.est2 .est2-collist__label{flex:1 1 auto}.est2 .est2-collist__path{color:var(--est-fg-faint);margin-right:4px}.est2 .est2-collist__ord{display:inline-flex;gap:4px}.est2 .est2-iconbtn{width:26px;height:26px;border:1px solid var(--est-border);background:var(--est-bg);border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer}.est2 .est2-iconbtn:hover:not(:disabled){background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-iconbtn:disabled{opacity:.4;cursor:default}.est2 .est2-dialog__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid var(--est-border)}.est2 .est2-dialog__spacer{flex:1 1 auto}.est2 .est2-btn{padding:7px 14px;border-radius:var(--est-radius-sm);font:inherit;font-weight:600;cursor:pointer;border:1px solid transparent}.est2 .est2-btn--ghost{background:transparent;color:var(--est-fg);border-color:var(--est-border)}.est2 .est2-btn--ghost:hover{background:var(--est-bg-hover)}.est2 .est2-btn--primary{background:var(--est-accent);color:var(--est-accent-fg)}.est2 .est2-btn--primary:hover{filter:brightness(1.05)}.est2 .est2-empty{padding:48px 16px;text-align:center;color:var(--est-fg-faint);font-size:var(--est-fs-sm)}.ngx-contextmenu{--ctx-bg: #ffffff;--ctx-fg: #1a1d24;--ctx-muted: #626b7a;--ctx-border: #e6e9ef;--ctx-hover: #f2f4f7;--ctx-accent: #4f46e5;--ctx-shadow: 0 10px 28px -8px rgba(16, 24, 40, .22), 0 2px 8px -3px rgba(16, 24, 40, .14);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.ngx-contextmenu .dropdown-menu{display:block;min-width:200px;margin:0;padding:6px;list-style:none;background:var(--ctx-bg);border:1px solid var(--ctx-border);border-radius:12px;box-shadow:var(--ctx-shadow);animation:est2-ctx-in .12s cubic-bezier(.16,1,.3,1)}.ngx-contextmenu li{list-style:none;margin:0}.ngx-contextmenu li>a{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:7px;color:var(--ctx-fg);font-size:13.5px;line-height:1.2;text-decoration:none;cursor:pointer;white-space:nowrap;transition:background .12s ease,color .12s ease}.ngx-contextmenu li>a:hover,.ngx-contextmenu li>a:focus{background:var(--ctx-hover);color:var(--ctx-fg);text-decoration:none;outline:none}.ngx-contextmenu li.divider,.ngx-contextmenu li[role=separator]{height:1px;margin:6px 8px;padding:0;background:var(--ctx-border)}.ngx-contextmenu li.disabled>a,.ngx-contextmenu li[aria-disabled=true]>a{color:var(--ctx-muted);opacity:.55;pointer-events:none}@keyframes est2-ctx-in{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@media(prefers-reduced-motion:reduce){.ngx-contextmenu .dropdown-menu{animation:none}}\n"] }]
|
|
10391
|
+
args: [{ selector: 'es-table2', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, viewProviders: [{ provide: LocalizationService, useClass: EsTable2Loc }], template: "@if (view()) {\n <div class=\"est2-wrap {{ ContainerClass() }}\"\n [style.height]=\"Height()\"\n [style.background-color]=\"EmptySpaceBackgroundColor() || null\">\n\n <!-- Auto-aggiornamento: toggle + intervallo (in alto a destra) -->\n @if (AutoUpdate()) {\n <div class=\"est2-autoupdate\">\n <label class=\"est2-autoupdate__label\">\n {{ 'Update every' | localize : lc }}\n <input type=\"text\" maxlength=\"3\" class=\"est2-autoupdate__secs\"\n [ngModel]=\"seconds()\" (ngModelChange)=\"seconds.set($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"autoUpdateChanged()\" />\n {{ 'second/s' | localize : lc }}\n </label>\n <label class=\"est2-switch\" [title]=\"'Toggle auto-update' | localize : lc\">\n <input type=\"checkbox\" [ngModel]=\"autoUpdate()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"autoUpdate.set($event); autoUpdateChanged()\" />\n <span class=\"est2-switch__slider\"></span>\n </label>\n </div>\n }\n\n <!-- Pager superiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'top') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Riepilogo delle modifiche non ancora salvate. Sta nel flusso (non \u00E8 un\n overlay come la barra di selezione) perch\u00E9 deve restare leggibile anche\n quando l'utente \u00E8 su una pagina che non contiene le righe interessate. -->\n @if (pendingChangesOn() && hasPendingChanges) {\n <div class=\"est2-pendingbar\">\n @if (addedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--added\">\n <strong>{{ addedCount }}</strong>\n {{ (addedCount === 1 ? 'row added' : 'rows added') | localize : lc }}\n </span>\n }\n @if (removedCount > 0) {\n <span class=\"est2-pendingbar__chip est2-pendingbar__chip--removed\">\n <strong>{{ removedCount }}</strong>\n {{ (removedCount === 1 ? 'row to delete' : 'rows to delete') | localize : lc }}\n </span>\n }\n <span class=\"est2-pendingbar__spacer\"></span>\n @if (removedCount > 0) {\n <span class=\"est2-link\" (click)=\"restoreAllRemovals()\">{{ 'Undo the deletions' | localize : lc }}</span>\n }\n </div>\n }\n\n <!-- Host POSIZIONATO dello scroller: \u00E8 l'ancora di tutti gli overlay che devono\n coprire la TABELLA (barra di selezione, overlay di caricamento). Senza di esso\n erano ancorati a `.est2-wrap` e con [AutoUpdate] o il pager in alto finivano\n sopra quei controlli invece che sopra l'header. -->\n <div class=\"est2-scroll-host\">\n\n <!-- Barra \"seleziona tutto\" (visibile solo con selezione multipla attiva e righe presenti) -->\n @if (Selection() && !SingleSelection() && hasSelection) {\n <div class=\"est2-selectbar\"\n [class.est2-selectbar--inset]=\"chromeWidth() > 0\"\n [style.height.px]=\"selbarHeight() ? selbarHeight() + 1 : null\"\n [style.right.px]=\"chromeWidth() || null\">\n @if (allSelected) {\n <span>{{ 'All the' | localize : lc }} <strong>{{ selectedCount }}</strong> {{ 'elements are selected' | localize : lc }}</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ (selectedCount === 1 ? 'element selected' : 'elements selected') | localize : lc }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">{{ 'Select all' | localize : lc }} {{ totalCount() }} {{ 'elements' | localize : lc }}</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">{{ 'Reset selection' | localize : lc }}</span>\n </div>\n }\n\n <div class=\"est2-scroll\" #scrollRef [style.max-height.px]=\"MaxHeight()\" (scroll)=\"onScrollerScroll($event)\">\n <table class=\"est2-table {{ TableClass() }}\"\n [class.est2-table--range]=\"rangeActive()\"\n [class.est2-table--dragging]=\"rangeDragging()\"\n (mousedown)=\"onGridMouseDown($event)\"\n (mouseover)=\"onGridMouseOver($event)\">\n\n <!-- ================= HEADER ================= -->\n @if (!HeaderHidden()) {\n <thead #theadRef>\n <!-- Righe di header-group multi-livello (dall'alto verso il basso) -->\n @if (hasHeaderGroups()) {\n @for (grow of headerGroupRows(); track $index) {\n <tr class=\"est2-hgroup-row\">\n @if (Selection()) { <th class=\"est2-col-min est2-selcol\" [class.est2-pinned]=\"hasPinned()\" [style.left.px]=\"hasPinned() ? 0 : null\"></th> }\n @for (op of DynamicOperations(); track op.id) { <th class=\"est2-col-min\"></th> }\n @for (cell of grow; track cell.id; let gi = $index) {\n <th [attr.colspan]=\"cell.span\"\n [attr.data-groupid]=\"cell.isGroup ? cell.id : null\"\n [class.est2-hgroup]=\"cell.isGroup\"\n [class.est2-pinned]=\"gi < pinnedCount()\"\n [style.left.px]=\"gi < pinnedCount() ? pinnedLeftPx(gi) : null\"\n class=\"est2-hgroup-cell\">\n @if (cell.isGroup) {\n @if (cell.template) {\n <ng-container *ngTemplateOutlet=\"cell.template\"></ng-container>\n } @else {\n {{ cell.label }}\n }\n }\n </th>\n }\n @if (Removal()) { <th class=\"est2-col-min\"></th> }\n @if (hasChrome()) { <th class=\"est2-col-min est2-chrome-col\"></th> }\n </tr>\n }\n }\n <tr>\n <!-- Colonna di selezione -->\n @if (Selection()) {\n <th class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"globalCheck()\"\n [indeterminate]=\"selectionIndeterminate\"\n [disabled]=\"SelectionDisabled()\"\n (change)=\"toggleAll()\"\n [attr.aria-label]=\"'Select all rows' | localize : lc\" />\n }\n </th>\n }\n\n <!-- Header da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- intestazioni vuote per le operazioni dinamiche -->\n @for (op of DynamicOperations(); track op.id) {\n <th class=\"est2-col-min\"></th>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let ci = $index) {\n @let hs = colStyles()[ci];\n <th [attr.data-colid]=\"col.id\"\n [class]=\"col.headerClass\"\n [class.est2-th--orderable]=\"OrderByColumn() && col.orderable\"\n [class.est2-col-min]=\"col.header?.thShrink\"\n [class.est2-col-groupstart]=\"hs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-pinned-right]=\"col.pinnedRight\"\n [style.left.px]=\"hs.left\"\n [style.right.px]=\"hs.right\"\n [style.width.px]=\"hs.width\"\n [style.min-width.px]=\"hs.width\"\n [style.max-width.px]=\"hs.width\"\n [style.text-align]=\"col.alignment\"\n [style.background-color]=\"col.headerBg || null\"\n (click)=\"toggleSort(col)\">\n <span class=\"est2-th__inner\">\n @if (col.header?.Template) {\n <ng-container *ngTemplateOutlet=\"col.header!.Template!; context: col.multiProp != null ? { $implicit: col.headerText } : null\"></ng-container>\n } @else {\n {{ col.headerText }}\n }\n @if (OrderByColumn() && col.orderable && orderOf(col.id)) {\n <span class=\"est2-sort est2-sort--active\"\n [class.est2-sort--desc]=\"orderOf(col.id) === 'DESC'\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 15l6-6 6 6\"/></svg>\n </span>\n @if (orderIndex(col.id) > 0 && MultipleOrderingDirectives()) {\n <span class=\"est2-sort__badge\">{{ orderIndex(col.id) }}</span>\n }\n }\n <!-- Indicatore/toggle di pin (visibile se pinnata o all'hover) -->\n @if (ColumnsPinnable() && col.multiProp == null && !col.fixed) {\n <button type=\"button\" class=\"est2-pinbtn\"\n [class.est2-pinbtn--on]=\"col.pinned || col.pinnedRight\"\n [class.est2-pinbtn--right]=\"col.pinnedRight\"\n [title]=\"pinTitleFor(col) | localize : lc\"\n (click)=\"togglePin(col, $event)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n </span>\n <!-- Handle di ridimensionamento (colonne pinnate, gated da ColumnsResizable) -->\n @if (col.pinned || ColumnsResizable()) {\n <span class=\"est2-resize-handle\" [title]=\"'Drag to resize' | localize : lc\"\n (mousedown)=\"startColumnResize(col, $event)\"\n (click)=\"$event.stopPropagation()\"></span>\n }\n </th>\n }\n }\n <!-- Header da template semplice -->\n @else if (headerRef) {\n <ng-container *ngTemplateOutlet=\"headerRef\"></ng-container>\n }\n\n <!-- Colonna rimozione -->\n @if (Removal()) {\n <th class=\"est2-col-min est2-trail-th\"\n [class.est2-removal-col]=\"hasPinnedRight()\"\n [style.right.px]=\"hasPinnedRight() ? chromeWidth() : null\"></th>\n }\n <!-- Colonna chrome (export / gestione colonne / menu) -->\n @if (hasChrome()) {\n <th class=\"est2-col-min est2-chrome-th est2-chrome-col est2-trail-th\">\n <div class=\"est2-chrome\">\n <!-- I pannelli dei menu NON vivono qui: sono renderizzati a fondo\n componente e posizionati `fixed`, altrimenti l'`overflow:auto`\n di `.est2-scroll` li taglierebbe su tabelle basse. -->\n @if (Export()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Export' | localize : lc\" (click)=\"$event.stopPropagation(); toggleExportMenu($event)\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><path d=\"M7 10l5 5 5-5\"/><path d=\"M12 15V3\"/></svg>\n </button>\n }\n @if (HiddenColumns() || ColumnsOrdering()) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Columns' | localize : lc\" (click)=\"$event.stopPropagation(); openColumnsDialog()\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\"/><path d=\"M9 3v18\"/><path d=\"M15 3v18\"/></svg>\n </button>\n }\n @if (CornerMenuOptions().length > 0) {\n <button type=\"button\" class=\"est2-chrome-btn\" [title]=\"'Options' | localize : lc\" (click)=\"$event.stopPropagation(); toggleCornerMenu($event)\">\u22EE</button>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n\n <!-- ================= BODY ================= -->\n @if (!BodyHidden()) {\n <tbody>\n <!-- Valori invarianti per l'intero corpo: senza questi verrebbero\n ricalcolati riga\u00D7colonna volte a ogni ciclo di change-detection. -->\n @let rng = rangeActive();\n @let rect = rangeRect();\n @let errs = hasCellErrors();\n @let vw = virtualWindow();\n @if (vw && vw.padTop > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padTop\"></td></tr>\n }\n @for (item of renderedRows(); track trackRow($index, item); let vi = $index) {\n <!-- indice ASSOLUTO: range, editing e data-r ragionano su boundSource() -->\n @let ri = vStart() + vi;\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--zebra]=\"vw && (ri % 2 === 1)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted || item._removed\"\n [class.est2-row--added]=\"item._localId != null\"\n [class.est2-row--group]=\"item._group\"\n [class.est2-row--clickable]=\"Selection() || item._group\"\n [contextMenu]=\"ContextMenu || emptyMenu\"\n [contextMenuSubject]=\"item\"\n (click)=\"handleRowClick(item, $event)\">\n\n <!-- Cella di selezione -->\n @if (Selection()) {\n <td class=\"est2-col-min est2-selcol\"\n [class.est2-pinned]=\"hasPinned()\"\n [style.left.px]=\"hasPinned() ? 0 : null\">\n @if (item._group) {\n @if (!SingleSelection()) {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"groupSelectionState(item) === 'all'\"\n [indeterminate]=\"groupSelectionState(item) === 'some'\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection(item)\"\n [attr.aria-label]=\"'Select group' | localize : lc\" />\n }\n } @else {\n <input type=\"checkbox\" class=\"est2-check\"\n [checked]=\"item._selected\"\n [indeterminate]=\"hierarchyIndeterminate(item)\"\n [disabled]=\"SelectionDisabled()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(item)\"\n [attr.aria-label]=\"'Select row' | localize : lc\" />\n }\n </td>\n }\n\n <!-- Celle da colonne (direttive / dinamica / report) -->\n @if (usesColumns()) {\n <!-- Operazioni dinamiche (icone a sinistra) -->\n @for (op of DynamicOperations(); track op.id) {\n <td class=\"est2-col-min est2-op-cell\">\n @if (!item._group && operationVisible(op, item)) {\n <span class=\"est2-op\" [class]=\"op.iconClass || ''\" [title]=\"op.title\"\n (click)=\"$event.stopPropagation(); dynamicOperation(item, op.id)\">{{ op.text }}</span>\n }\n </td>\n }\n @for (col of visibleColumns(); track trackCol($index, col); let first = $first, ci = $index) {\n @let cs = colStyles()[ci];\n @let insel = rng && inRange(ri, ci);\n @let editing = isEditing(ri, ci);\n <td [class]=\"col.cssClass\"\n [style.text-align]=\"col.alignment\"\n [style.color]=\"cellColor(item, col, 'fore')\"\n [style.background-color]=\"cellColor(item, col, 'back')\"\n [class.est2-nowrap]=\"!col.wrap\"\n [class.est2-col-groupstart]=\"cs.groupStart\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-pinned-right]=\"col.pinnedRight\"\n [class.est2-fixedw]=\"cs.width != null\"\n [style.left.px]=\"cs.left\"\n [style.right.px]=\"cs.right\"\n [style.width.px]=\"cs.width\"\n [style.min-width.px]=\"cs.width\"\n [style.max-width.px]=\"cs.width\"\n [class.est2-td--group-key]=\"item._group && item.column === col.id\"\n [attr.data-r]=\"rng && !item._group ? ri : null\"\n [attr.data-c]=\"rng && !item._group ? ci : null\"\n [class.est2-cell-sel]=\"insel\"\n [class.est2-cell-sel-t]=\"insel && ri === rect!.top\"\n [class.est2-cell-sel-b]=\"insel && ri === rect!.bottom\"\n [class.est2-cell-sel-l]=\"insel && ci === rect!.left\"\n [class.est2-cell-sel-r]=\"insel && ci === rect!.right\"\n [class.est2-cell-editing]=\"editing\"\n [class.est2-cell-invalid]=\"errs && !!cellError(item, col)\"\n [attr.data-error]=\"errs ? (cellError(item, col) || null) : null\"\n (dblclick)=\"onCellDblClick(item, col, ri, ci)\">\n @if (editing) {\n @if (editorFor(col); as edTpl) {\n <ng-container *ngTemplateOutlet=\"edTpl; context: editorContext(item, col)\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"defaultEditor; context: { $implicit: item, col: col }\"></ng-container>\n }\n } @else if (item._group) {\n @if (item.column === col.id) {\n <span class=\"est2-group-key\" [style.padding-left.px]=\"groupIndent(item)\">\n <span class=\"est2-group-chevron\" [class.est2-group-chevron--open]=\"item._expanded\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n {{ groupCellDisplay(item, col) }}\n </span>\n } @else {\n {{ groupCellDisplay(item, col) }}\n }\n } @else {\n <!-- Navigatore albero nella prima colonna -->\n @if (Hierarchy() && first) {\n <span class=\"est2-hier-lead\" [style.padding-left.px]=\"hierarchyIndent(item)\">\n @if (item.parent) {\n <span class=\"est2-group-chevron est2-hier-toggle\" [class.est2-group-chevron--open]=\"item._expanded\"\n (click)=\"$event.stopPropagation(); toggleHierarchyNode(item)\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 6l6 6-6 6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-hier-toggle\"></span>\n }\n </span>\n }\n @if (itemCellHidden(col)) {\n <!-- colonna di gruppo nascosta sulla riga-oggetto -->\n } @else if (col.multiProp != null) {\n @if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: multiCell(item, col) }\"></ng-container>\n } @else {\n {{ multiCell(item, col)?.value }}\n }\n } @else if (col.cell?.Template) {\n <ng-container *ngTemplateOutlet=\"col.cell!.Template; context: { $implicit: item }\"></ng-container>\n } @else if (col.routePath) {\n <a class=\"est2-link\" [routerLink]=\"routerLinkFor(item, col)\">{{ cellValue(item, col) }}</a>\n } @else if (col.propAccessor != null) {\n {{ reportCellDisplay(item, col) }}\n } @else if (col.type === 'enum') {\n {{ (cellValue(item, col) | est2_lookup : col.source).description }}\n } @else {\n {{ cellValue(item, col) | est2_format : col.type : col.format : locale }}\n }\n }\n </td>\n }\n }\n <!-- Celle da template semplice -->\n @else if (bodyRef) {\n <ng-container *ngTemplateOutlet=\"bodyRef; context: { $implicit: item }\"></ng-container>\n }\n\n <!-- Rimozione (non sulle righe-gruppo) -->\n @if (Removal()) {\n <td class=\"est2-col-min\"\n [class.est2-removal-col]=\"hasPinnedRight()\"\n [style.right.px]=\"hasPinnedRight() ? chromeWidth() : null\">\n @if (!item._group && canRemove(item)) {\n @if (item.removed || item.deleted || item._removed) {\n <button type=\"button\" class=\"est2-rowaction\" [title]=\"'Restore' | localize : lc\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" [title]=\"'Remove' | localize : lc\" (click)=\"removeItem(item)\">\u2715</button>\n }\n }\n </td>\n }\n <!-- Chrome -->\n @if (hasChrome()) { <td class=\"est2-col-min est2-chrome-col\"></td> }\n </tr>\n }\n } @empty {\n <tr>\n <td class=\"est2-empty\" [attr.colspan]=\"totalColspan()\">{{ 'No elements to display' | localize : lc }}</td>\n </tr>\n }\n @if (vw && vw.padBottom > 0) {\n <tr class=\"est2-vspacer\" aria-hidden=\"true\"><td [attr.colspan]=\"totalColspan()\" [style.height.px]=\"vw.padBottom\"></td></tr>\n }\n </tbody>\n }\n </table>\n </div>\n\n <!-- Overlay di caricamento: FUORI dallo scroller, cos\u00EC copre il viewport della\n tabella senza scorrere col contenuto e senza essere bucato dalle celle sticky -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>{{ 'Loading\u2026' | localize : lc }}</span>\n </div>\n }\n\n </div>\n\n <!-- Pager inferiore -->\n @if (PagingStyle() === 'both' || PagingStyle() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"pager\"></ng-container>\n }\n\n <!-- Menu del chrome (export / opzioni). Renderizzati FUORI da `.est2-scroll` e\n posizionati `fixed` sulle coordinate del bottone che li ha aperti: cos\u00EC non\n vengono tagliati dall'`overflow:auto` dello scroller su tabelle basse, e\n restano dentro `.est2` (i token `--est-*` continuano a ereditare). -->\n @if (exportMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @if (CSVExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">{{ 'Export Excel (XLSX)' | localize : lc }}</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">{{ 'Export CSV' | localize : lc }}</button> }\n </div>\n }\n @if (cornerMenuOpen() && menuAnchor(); as anchor) {\n <div class=\"est2-menu\" (click)=\"$event.stopPropagation()\"\n [style.top.px]=\"anchor.top\" [style.right.px]=\"anchor.right\">\n @for (opt of CornerMenuOptions(); track opt.id) {\n <button type=\"button\" class=\"est2-menu-item\" (click)=\"cornerAction(opt.id); closeChromeMenus()\">{{ opt.description }}</button>\n }\n </div>\n }\n\n <!-- Avviso transitorio (es. incolla con dimensioni incompatibili) -->\n @if (notice()) {\n <div class=\"est2-toast\" role=\"alert\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/></svg>\n <span>{{ notice() }}</span>\n </div>\n }\n\n <!-- Dialog visibilit\u00E0 / ordine colonne -->\n @if (columnsDialogOpen()) {\n <div class=\"est2-dialog-backdrop\" #dialogBackdrop\n [class.est2-dialog-backdrop--embedded]=\"embedded\"\n (click)=\"closeColumnsDialog()\">\n <div class=\"est2-dialog\" (click)=\"$event.stopPropagation()\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"est2-dialog__head\">\n <span>\n @if (HiddenColumns() && ColumnsOrdering()) { {{ 'Columns visibility and order' | localize : lc }} }\n @else if (HiddenColumns()) { {{ 'Columns visibility' | localize : lc }} }\n @else { {{ 'Columns order' | localize : lc }} }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" [attr.aria-label]=\"'Close' | localize : lc\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">{{ 'Show all' | localize : lc }}</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">{{ 'Hide all' | localize : lc }}</button>\n </div>\n }\n\n <ul class=\"est2-collist\">\n @for (c of dialogCols(); track c.id; let i = $index) {\n <li class=\"est2-collist__row\"\n [attr.draggable]=\"ColumnsOrdering() && canReorder(c) ? true : null\"\n [class.est2-collist__row--drag]=\"ColumnsOrdering() && canReorder(c)\"\n [class.est2-collist__row--locked]=\"!canReorder(c)\"\n [class.est2-collist__row--dragging]=\"dragIndex() === i\"\n [class.est2-collist__row--drop-above]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! > i\"\n [class.est2-collist__row--drop-below]=\"dragOverIndex() === i && dragIndex() !== null && dragIndex()! < i\"\n (dragstart)=\"dialogDragStart(i, $event)\"\n (dragover)=\"dialogDragOver(i, $event)\"\n (drop)=\"dialogDrop(i, $event)\"\n (dragend)=\"dialogDragEnd()\">\n @if (ColumnsOrdering()) {\n @if (canReorder(c)) {\n <span class=\"est2-collist__grip\" [title]=\"'Drag to reorder' | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"currentColor\"><circle cx=\"9\" cy=\"6\" r=\"1.6\"/><circle cx=\"15\" cy=\"6\" r=\"1.6\"/><circle cx=\"9\" cy=\"12\" r=\"1.6\"/><circle cx=\"15\" cy=\"12\" r=\"1.6\"/><circle cx=\"9\" cy=\"18\" r=\"1.6\"/><circle cx=\"15\" cy=\"18\" r=\"1.6\"/></svg>\n </span>\n } @else {\n <span class=\"est2-collist__grip est2-collist__grip--locked\"\n [title]=\"(c.fixed ? 'Fixed column: cannot be moved' : 'Column locked to the left: cannot be moved') | localize : lc\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"4\" y=\"11\" width=\"16\" height=\"10\" rx=\"2\"/><path d=\"M8 11V7a4 4 0 0 1 8 0v4\"/></svg>\n </span>\n }\n }\n @if (HiddenColumns()) {\n <label class=\"est2-collist__vis\" [title]=\"c.fixed ? ('Fixed column: always visible' | localize : lc) : ''\">\n <input type=\"checkbox\" class=\"est2-check\" [checked]=\"c.visible\" [disabled]=\"c.fixed\" (change)=\"dialogToggle(i)\" />\n </label>\n }\n <span class=\"est2-collist__label\">\n @if (c.tmpl) {\n <!-- colonna non renderizzata (Visible:false / nascosta): il testo\n header non esiste nel DOM, quindi rendo il template dell'`*th` -->\n @if (c.prefix) { <span class=\"est2-collist__path\">{{ c.prefix }}</span> }\n <ng-container *ngTemplateOutlet=\"c.tmpl; context: c.tmplCtx\"></ng-container>\n } @else {\n {{ c.label }}\n }\n </span>\n @if (ColumnsPinnable()) {\n <button type=\"button\" class=\"est2-iconbtn est2-collist__pin\" [class.est2-collist__pin--on]=\"c.pinned || c.pinnedRight\"\n [disabled]=\"c.grouped || c.fixed\"\n [title]=\"dialogPinTitle(c) | localize : lc\"\n (click)=\"dialogTogglePin(i)\">\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>\n </button>\n }\n @if (ColumnsOrdering()) {\n <span class=\"est2-collist__ord\">\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Up' | localize : lc\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" [title]=\"'Down' | localize : lc\" (click)=\"dialogMove(i, 1)\"\n [disabled]=\"i === dialogCols().length - 1 || !canReorder(c) || !canReorder(dialogCols()[i + 1])\">\u2193</button>\n </span>\n }\n </li>\n }\n </ul>\n\n <div class=\"est2-dialog__foot\">\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"resetColumnsDialog()\"\n [title]=\"'Restore the declared columns and delete the saved preferences for this table' | localize : lc\">{{ 'Reset' | localize : lc }}</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">{{ 'Cancel' | localize : lc }}</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">{{ 'Apply' | localize : lc }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n}\n\n<!-- Template pager riutilizzabile sopra/sotto -->\n<ng-template #pager>\n @if (!HidePaging() && !grouped()) {\n <es-table2-pager\n [page]=\"currentPage()\"\n [pages]=\"totalPages()\"\n [total]=\"totalCount()\"\n [locale]=\"locale\"\n [itemsPerPage]=\"viewMode() ? (view()?.itemsperpageoverride ?? 15) : ArraymodeItemsPerPage()\"\n [countLabel]=\"CountLabel()\"\n [showCount]=\"!HidePagingCount()\"\n [showButtons]=\"!HidePagingButtons()\"\n [showPagingOptions]=\"!HidePagingButtons()\"\n [allowAll]=\"AllSearch()\"\n (pageChange)=\"goToPage($event)\"\n (itemsPerPageChange)=\"changeItemsPerPage($event)\">\n </es-table2-pager>\n }\n</ng-template>\n\n<!-- Editor di cella di default (usato quando il consumer non fornisce un `*editor`) -->\n<ng-template #defaultEditor let-item let-col=\"col\">\n @switch (col.type) {\n @case ('enum') {\n <select class=\"est2-editor-input\"\n [value]=\"editDraft\"\n (change)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\">\n @for (o of col.source || []; track o.id) {\n <option [value]=\"o.id\" [selected]=\"o.id == editDraft\">{{ o.description }}</option>\n }\n </select>\n }\n @case ('boolean') {\n <input type=\"checkbox\" class=\"est2-editor-input est2-check\"\n [checked]=\"editDraft === true || editDraft === 'true'\"\n (change)=\"editDraft = $any($event.target).checked; commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\" />\n }\n @default {\n <input class=\"est2-editor-input\"\n [type]=\"editorInputType(col.type)\"\n [value]=\"editDraft\"\n (input)=\"editDraft = $any($event.target).value\"\n (keydown.enter)=\"commitEdit(item, col)\"\n (keydown.escape)=\"cancelEdit()\"\n (blur)=\"commitEdit(item, col)\" />\n }\n }\n</ng-template>\n\n<!-- Menu di default (nessuna operazione) usato quando il consumer non passa [ContextMenu] -->\n<context-menu #emptyMenu>\n <ng-template contextMenuItem [passive]=\"true\"><em>{{ 'No operations available\u2026' | localize : lc }}</em></ng-template>\n</context-menu>\n", styles: [".est2{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent);--est-radius: 10px;--est-radius-sm: 6px;--est-radius-pill: 999px;--est-gap: 8px;--est-cell-py: 6px;--est-cell-px: 12px;--est-row-h: 33px;--est-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--est-fs: 13px;--est-fs-sm: 12px;--est-fw-head: 700;--est-transition: .14s cubic-bezier(.4, 0, .2, 1);--est-bg-zebra: color-mix(in srgb, var(--est-fg) 3.5%, var(--est-bg));--est-z-menu: 1000;--est-z-modal: 1010;--est-z-toast: 1020;font-family:var(--est-font);font-size:var(--est-fs);color:var(--est-fg);position:relative;display:block}.est2.est2--dark{--est-bg: #14161c;--est-bg-subtle: #1a1d25;--est-bg-raised: #1e222b;--est-bg-hover: #232733;--est-bg-selected: color-mix(in srgb, var(--est-accent) 26%, transparent);--est-fg: #e7eaf0;--est-fg-muted: #9aa3b2;--est-fg-faint: #6b7484;--est-border: #2a2f3a;--est-border-strong: #39404d;--est-accent: #7c74ff;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 22%, transparent);--est-danger: #f87171;--est-warning: #fbbf24;--est-success: #34d399;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 6px 16px -10px rgba(0, 0, 0, .7);--est-shadow-pop: 0 10px 28px -8px rgba(0, 0, 0, .6), 0 2px 6px -2px rgba(0, 0, 0, .5);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 55%, transparent)}.est2.est2--light{--est-bg: #ffffff;--est-bg-subtle: #f7f8fa;--est-bg-raised: #ffffff;--est-bg-hover: #f2f4f7;--est-bg-selected: color-mix(in srgb, var(--est-accent) 12%, transparent);--est-fg: #1a1d24;--est-fg-muted: #626b7a;--est-fg-faint: #9aa3b2;--est-border: #e6e9ef;--est-border-strong: #d3d8e0;--est-accent: #4f46e5;--est-accent-fg: #ffffff;--est-accent-weak: color-mix(in srgb, var(--est-accent) 14%, transparent);--est-danger: #dc2626;--est-warning: #d97706;--est-success: #059669;--est-shadow-sticky: 0 1px 0 var(--est-border), 0 4px 12px -8px rgba(16, 24, 40, .24);--est-shadow-pop: 0 8px 24px -6px rgba(16, 24, 40, .18), 0 2px 6px -2px rgba(16, 24, 40, .12);--est-ring: 0 0 0 3px color-mix(in srgb, var(--est-accent) 40%, transparent)}.est2.est2--dense{--est-cell-py: 3px;--est-cell-px: 9px;--est-row-h: 27px;--est-fs: 12.5px}.est2 *,.est2 *:before,.est2 *:after{box-sizing:border-box}.est2 .est2-scroll{position:relative;width:100%;overflow:auto;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg);-webkit-overflow-scrolling:touch}.est2 table.est2-table{width:100%;border-collapse:separate;border-spacing:0;background:var(--est-bg)}.est2 thead th{position:sticky;top:0;z-index:3;background:var(--est-bg-subtle);color:var(--est-fg);font-weight:var(--est-fw-head);font-size:var(--est-fs);text-align:left;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);box-shadow:var(--est-shadow-sticky);-webkit-user-select:none;user-select:none}.est2 thead tr.est2-hgroup-row th{font-size:var(--est-fs-sm);font-weight:700;color:var(--est-fg-muted);text-align:center;white-space:nowrap;padding:var(--est-cell-py) var(--est-cell-px);background:var(--est-bg-subtle);border-bottom:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th.est2-hgroup{color:var(--est-fg);border-left:1px solid var(--est-border);border-right:1px solid var(--est-border)}.est2 thead tr.est2-hgroup-row th:not(.est2-hgroup){background:var(--est-bg-subtle);border-bottom-color:transparent}.est2 thead tr:last-child th.est2-col-groupstart,.est2 tbody td.est2-col-groupstart{border-left:1px solid var(--est-border)}.est2 tbody td{padding:var(--est-cell-py) var(--est-cell-px);border-bottom:1px solid var(--est-border);color:var(--est-fg);vertical-align:middle;background:transparent}.est2 tbody tr:nth-child(2n) td{background:var(--est-bg-zebra)}.est2 tbody tr:nth-child(2n) td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-row--zebra td,.est2 tbody tr.est2-row--zebra td.est2-pinned{background:var(--est-bg-zebra)}.est2 tbody tr.est2-vspacer td{padding:0;border:none;background:transparent}.est2 tbody tr.est2-vspacer:hover td{background:transparent}.est2 th.est2-pinned,.est2 td.est2-pinned{position:sticky;background:var(--est-bg);box-shadow:1px 0 0 var(--est-border)}.est2 .est2-resize-handle{position:absolute;top:0;right:-3px;width:10px;height:100%;cursor:col-resize;z-index:2;touch-action:none;-webkit-user-select:none;user-select:none}.est2 .est2-resize-handle:after{content:\"\";position:absolute;top:28%;bottom:28%;right:3px;width:2px;border-radius:2px;background:color-mix(in srgb,var(--est-fg) 30%,transparent);transition:background var(--est-transition),top var(--est-transition),bottom var(--est-transition)}.est2 .est2-resize-handle:hover:after{background:var(--est-accent);top:15%;bottom:15%}.est2 .est2-resize-handle:active:after{background:var(--est-accent);top:6%;bottom:6%}.est2 td.est2-fixedw{overflow:hidden;text-overflow:ellipsis}.est2 td.est2-cell-invalid{background:color-mix(in srgb,var(--est-danger) 10%,var(--est-bg));box-shadow:inset 0 0 0 1px var(--est-danger);color:var(--est-danger)}.est2 .est2-selcol{width:44px;min-width:44px;max-width:44px}.est2 td.est2-pinned{z-index:3}.est2 thead th.est2-pinned,.est2 thead tr.est2-hgroup-row th.est2-pinned{z-index:6;background:var(--est-bg-subtle)}.est2 tbody tr:hover td.est2-pinned{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-pinned{background:var(--est-bg-subtle)}.est2 th.est2-pinned-right,.est2 td.est2-pinned-right{position:sticky;background:var(--est-bg);box-shadow:-1px 0 0 var(--est-border)}.est2 td.est2-pinned-right{z-index:3}.est2 thead th.est2-pinned-right,.est2 thead tr.est2-hgroup-row th.est2-pinned-right{z-index:6;background:var(--est-bg-subtle)}.est2 tbody tr:nth-child(2n) td.est2-pinned-right{background:var(--est-bg-zebra)}.est2 tbody tr.est2-row--zebra td.est2-pinned-right{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-pinned-right{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-pinned-right{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-pinned-right{background:var(--est-bg-subtle)}.est2 tbody tr.est2-row--added td.est2-pinned-right{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 th.est2-removal-col,.est2 td.est2-removal-col{position:sticky;background:var(--est-bg)}.est2 thead th.est2-removal-col{z-index:6;background:var(--est-bg-subtle)}.est2 td.est2-removal-col{z-index:3}.est2 tbody tr:nth-child(2n) td.est2-removal-col{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-removal-col{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-removal-col{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 th.est2-chrome-col,.est2 td.est2-chrome-col{position:sticky;right:0;box-shadow:-1px 0 0 var(--est-border)}.est2 thead th.est2-chrome-col{z-index:16;background:var(--est-bg-subtle)}.est2 td.est2-chrome-col{z-index:4;background:var(--est-bg)}.est2 tbody tr:nth-child(2n) td.est2-chrome-col{background:var(--est-bg-zebra)}.est2 tbody tr:hover td.est2-chrome-col{background:color-mix(in srgb,var(--est-fg) 5%,var(--est-bg))}.est2 tbody tr.est2-row--selected td.est2-chrome-col{background:color-mix(in srgb,var(--est-accent) 12%,var(--est-bg))}.est2 tbody tr.est2-row--group td.est2-chrome-col{background:var(--est-bg-subtle)}.est2 .est2-pinbtn--right svg,.est2 .est2-collist__pin--right svg{transform:scaleX(-1)}.est2 .est2-pinbtn{display:inline-flex;align-items:center;justify-content:center;margin-left:4px;padding:2px;border:none;background:transparent;color:var(--est-fg-faint);border-radius:var(--est-radius-sm);cursor:pointer;opacity:0;transition:opacity var(--est-transition),color var(--est-transition),background var(--est-transition)}.est2 thead th:hover .est2-pinbtn{opacity:.7}.est2 .est2-pinbtn:hover{background:var(--est-bg-hover);color:var(--est-fg);opacity:1}.est2 .est2-pinbtn--on{opacity:1;color:var(--est-accent);transform:rotate(0)}.est2 thead th:hover .est2-pinbtn--on{opacity:1}.est2 .est2-collist__pin.est2-collist__pin--on{color:var(--est-accent);border-color:var(--est-accent)}.est2 tbody tr{height:var(--est-row-h);transition:background var(--est-transition)}.est2 tbody tr:last-child td{border-bottom:none}.est2 tbody tr:hover td{background:var(--est-bg-hover)}.est2 tbody tr.est2-row--selected td{background:var(--est-bg-selected)}.est2 tbody tr.est2-row--clickable{cursor:pointer}.est2 tbody tr.est2-row--added td{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 tbody tr.est2-row--added td:first-child{box-shadow:inset 3px 0 0 var(--est-accent)}.est2 tbody tr.est2-row--added td.est2-pinned{background:color-mix(in srgb,var(--est-accent) 7%,var(--est-bg))}.est2 .est2-pendingbar{display:flex;align-items:center;gap:10px;padding:7px 12px;margin-bottom:8px;border:1px solid var(--est-border);border-radius:var(--est-radius);background:var(--est-bg-subtle);font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-pendingbar__spacer{flex:1 1 auto}.est2 .est2-pendingbar__chip{display:inline-flex;align-items:center;gap:5px;padding:2px 10px;border-radius:var(--est-radius-pill)}.est2 .est2-pendingbar__chip strong{color:var(--est-fg)}.est2 .est2-pendingbar__chip--added{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 .est2-pendingbar__chip--removed{background:color-mix(in srgb,var(--est-danger) 14%,transparent)}.est2 tbody tr.est2-row--removed td{text-decoration:line-through;color:var(--est-fg-faint)}.est2 .est2-table--range tbody td[data-r]{cursor:cell}.est2 .est2-table--dragging,.est2 .est2-table--dragging tbody td{-webkit-user-select:none;user-select:none}.est2 tbody td.est2-cell-sel{background:color-mix(in srgb,var(--est-accent) 14%,transparent)}.est2 tbody td.est2-cell-sel-t{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l{box-shadow:inset 2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-r{box-shadow:inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b{box-shadow:inset 0 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 0 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-r{box-shadow:inset -2px 2px 0 0 var(--est-accent),inset 0 -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px -2px 0 0 var(--est-accent),inset -2px 0 0 0 var(--est-accent)}.est2 tbody td.est2-cell-sel-t.est2-cell-sel-b.est2-cell-sel-l.est2-cell-sel-r{box-shadow:inset 2px 2px 0 0 var(--est-accent),inset -2px -2px 0 0 var(--est-accent)}.est2 tbody td.est2-cell-editing{padding:2px 6px}.est2 .est2-editor-input{width:100%;box-sizing:border-box;height:calc(var(--est-row-h) - 8px);padding:2px 6px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1.5px solid var(--est-accent);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-editor-input:focus-visible{box-shadow:var(--est-ring)}.est2 input.est2-editor-input[type=checkbox]{width:16px;height:16px}.est2 .est2-autoupdate{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:4px 2px 8px;font-size:var(--est-fs-sm);color:var(--est-fg-muted)}.est2 .est2-autoupdate__label{display:inline-flex;align-items:center;gap:6px}.est2 .est2-autoupdate__secs{width:44px;text-align:center;padding:3px 4px;font:inherit;color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);outline:none}.est2 .est2-autoupdate__secs:focus-visible{box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2 .est2-switch{position:relative;display:inline-flex;width:38px;height:20px;cursor:pointer}.est2 .est2-switch input{position:absolute;opacity:0;width:0;height:0}.est2 .est2-switch__slider{flex:1;border-radius:var(--est-radius-pill);background:var(--est-border-strong);transition:background var(--est-transition)}.est2 .est2-switch__slider:before{content:\"\";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px #00000040;transition:transform var(--est-transition)}.est2 .est2-switch input:checked+.est2-switch__slider{background:var(--est-accent)}.est2 .est2-switch input:checked+.est2-switch__slider:before{transform:translate(18px)}.est2 .est2-switch input:focus-visible+.est2-switch__slider{box-shadow:var(--est-ring)}.est2 .est2-toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%);z-index:var(--est-z-toast);pointer-events:none;display:inline-flex;align-items:center;gap:8px;max-width:min(560px,100vw - 24px);padding:8px 14px;font-size:13px;font-weight:500;color:#fff;background:#b91c1c;border-radius:var(--est-radius);box-shadow:0 6px 20px #00000040;animation:est2-toast-in .16s ease-out}.est2 .est2-toast svg{flex:0 0 auto}@keyframes est2-toast-in{0%{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%)}}@media(prefers-reduced-motion:reduce){.est2 .est2-toast{animation:none}}.est2 tbody tr.est2-row--group{cursor:pointer;-webkit-user-select:none;user-select:none}.est2 tbody tr.est2-row--group td{background:var(--est-bg-subtle);font-weight:600;color:var(--est-fg);border-bottom:1px solid var(--est-border)}.est2 tbody tr.est2-row--group:hover td{background:var(--est-bg-hover)}.est2 .est2-td--group-key{color:var(--est-fg)}.est2 .est2-group-key{display:inline-flex;align-items:center;gap:6px}.est2 .est2-group-chevron{display:inline-flex;color:var(--est-fg-muted);transition:transform var(--est-transition)}.est2 .est2-group-chevron--open{transform:rotate(90deg)}.est2 .est2-hier-lead{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px}.est2 .est2-hier-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex:none}.est2 .est2-group-chevron.est2-hier-toggle{cursor:pointer;border-radius:var(--est-radius-sm);transition:transform var(--est-transition),background var(--est-transition),color var(--est-transition)}.est2 .est2-group-chevron.est2-hier-toggle:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 th.est2-th--orderable{cursor:pointer;transition:color var(--est-transition)}.est2 th.est2-th--orderable:hover{color:var(--est-fg)}.est2 .est2-th__inner{display:inline-flex;align-items:center;gap:6px}.est2 .est2-sort{display:inline-flex;width:14px;height:14px;opacity:.35;transition:opacity var(--est-transition),transform var(--est-transition)}.est2 .est2-sort--active{opacity:1;color:var(--est-accent)}.est2 .est2-sort--desc{transform:rotate(180deg)}.est2 .est2-sort__badge{font-size:9px;font-weight:700;color:var(--est-accent);margin-left:2px}.est2 .est2-check{appearance:none;width:16px;height:16px;border:1.5px solid var(--est-border-strong);border-radius:var(--est-radius-sm);background:var(--est-bg);cursor:pointer;position:relative;transition:border-color var(--est-transition),background var(--est-transition);vertical-align:middle;flex:none}.est2 .est2-check:hover{border-color:var(--est-accent)}.est2 .est2-check:checked{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:checked:after{content:\"\";position:absolute;left:4.5px;top:1.5px;width:4px;height:8px;border:solid var(--est-accent-fg);border-width:0 2px 2px 0;transform:rotate(45deg)}.est2 .est2-check:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2 .est2-check:disabled{opacity:.45;cursor:not-allowed}.est2 .est2-check:disabled:hover{border-color:var(--est-border-strong)}.est2 .est2-check:indeterminate{background:var(--est-accent);border-color:var(--est-accent)}.est2 .est2-check:indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--est-accent-fg);transform:none;border:none}.est2 th.est2-col-min,.est2 td.est2-col-min{width:1%;white-space:nowrap}.est2 .est2-wrap,.est2 .est2-scroll-host{position:relative}.est2 .est2-selectbar{position:absolute;top:0;left:0;right:0;z-index:15;display:flex;align-items:center;gap:12px;padding:9px 14px;font-size:var(--est-fs-sm);color:var(--est-fg);background:var(--est-bg-subtle);border:none;border-radius:var(--est-radius) var(--est-radius) 0 0;box-shadow:inset 3px 0 0 var(--est-accent)}.est2 .est2-selectbar--inset{border-top-right-radius:0}.est2 .est2-selectbar strong{color:var(--est-fg);font-weight:700}.est2 .est2-selectbar__spacer{flex:1 1 auto}.est2 .est2-link{color:var(--est-accent);cursor:pointer;font-weight:600}.est2 .est2-link:hover{text-decoration:underline}.est2 .est2-rowaction{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);border:none;background:transparent;color:var(--est-fg-faint);cursor:pointer;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-rowaction:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-rowaction--danger:hover{color:var(--est-danger)}.est2 .est2-op-cell{text-align:center}.est2 .est2-op{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;padding:0 6px;border-radius:var(--est-radius-sm);color:var(--est-accent);cursor:pointer;font-size:var(--est-fs-sm);transition:background var(--est-transition)}.est2 .est2-op:hover{background:var(--est-accent-weak)}.est2 .est2-loading{position:absolute;inset:0;border-radius:var(--est-radius);z-index:20;display:flex;align-items:center;justify-content:center;gap:10px;background:color-mix(in srgb,var(--est-bg) 70%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);color:var(--est-fg-muted);font-size:var(--est-fs-sm)}.est2 .est2-spinner{width:16px;height:16px;border:2px solid var(--est-border-strong);border-top-color:var(--est-accent);border-radius:50%;animation:est2-spin .7s linear infinite}@keyframes est2-spin{to{transform:rotate(360deg)}}.est2 .est2-nowrap{white-space:nowrap}.est2 .est2-cornermenu{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:var(--est-radius-sm);cursor:pointer;color:var(--est-fg-muted);transition:background var(--est-transition)}.est2 .est2-cornermenu:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-chrome-th{position:relative}.est2 .est2-chrome{display:inline-flex;align-items:center;gap:2px}.est2 .est2-chrome-wrap{position:relative;display:inline-flex}.est2 .est2-chrome-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:none;background:transparent;border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer;font-size:16px;line-height:1;transition:background var(--est-transition),color var(--est-transition)}.est2 .est2-chrome-btn:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-menu{position:fixed;z-index:var(--est-z-menu);min-width:180px;padding:4px;background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 8px 24px #0000002e;display:flex;flex-direction:column}.est2 .est2-menu-item{display:block;width:100%;padding:8px 10px;border:none;background:transparent;text-align:left;font:inherit;color:var(--est-fg);border-radius:var(--est-radius-sm);cursor:pointer}.est2 .est2-menu-item:hover{background:var(--est-bg-hover)}.est2 .est2-dialog-backdrop{position:fixed;inset:0;z-index:var(--est-z-modal);display:flex;align-items:center;justify-content:center;padding:16px}.est2 .est2-dialog-backdrop:before{content:\"\";position:absolute;inset:0;background-color:#000;opacity:.5}.est2 .est2-dialog{position:relative}.est2 .est2-dialog-backdrop--embedded{--est-embed-offset: 110px;padding-top:5%;margin-left:calc(-1 * var(--est-embed-offset));width:calc(100% + var(--est-embed-offset));overflow:hidden}.est2 .est2-dialog-backdrop--embedded .est2-dialog{max-height:600px}.est2 .est2-dialog{width:520px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;background:var(--est-bg);color:var(--est-fg);border:1px solid var(--est-border);border-radius:var(--est-radius);box-shadow:0 16px 48px #0000004d;overflow:hidden}.est2 .est2-dialog__head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;font-weight:700;border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__close{border:none;background:transparent;cursor:pointer;color:var(--est-fg-muted);font-size:15px;line-height:1;padding:4px;border-radius:var(--est-radius-sm)}.est2 .est2-dialog__close:hover{background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-dialog__tools{padding:8px 14px;font-size:var(--est-fs-sm);border-bottom:1px solid var(--est-border)}.est2 .est2-dialog__tools .est2-link{border:none;background:none;padding:0;font:inherit;font-weight:600}.est2 .est2-dialog__sep{margin:0 6px;color:var(--est-fg-faint)}.est2 .est2-collist{list-style:none;margin:0;padding:6px;overflow-y:auto;flex:1 1 auto;min-height:0}.est2 .est2-collist__row{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--est-radius-sm)}.est2 .est2-collist__row:hover{background:var(--est-bg-hover)}.est2 .est2-collist__row--drag{cursor:grab}.est2 .est2-collist__row--drag:active{cursor:grabbing}.est2 .est2-collist__row--dragging{opacity:.45;background:var(--est-bg-hover)}.est2 .est2-collist__row--drop-above{box-shadow:inset 0 2px 0 0 var(--est-accent)}.est2 .est2-collist__row--drop-below{box-shadow:inset 0 -2px 0 0 var(--est-accent)}.est2 .est2-collist__grip{display:inline-flex;align-items:center;justify-content:center;color:var(--est-fg-faint);cursor:grab;flex:0 0 auto}.est2 .est2-collist__grip:active{cursor:grabbing}.est2 .est2-collist__row--drag:hover .est2-collist__grip{color:var(--est-fg-muted)}.est2 .est2-collist__grip--locked{color:var(--est-fg-faint);opacity:.55;cursor:default}.est2 .est2-collist__row--locked .est2-collist__label{color:var(--est-fg-muted)}.est2 .est2-collist__vis{display:inline-flex}.est2 .est2-collist__label{flex:1 1 auto}.est2 .est2-collist__path{color:var(--est-fg-faint);margin-right:4px}.est2 .est2-collist__ord{display:inline-flex;gap:4px}.est2 .est2-iconbtn{width:26px;height:26px;border:1px solid var(--est-border);background:var(--est-bg);border-radius:var(--est-radius-sm);color:var(--est-fg-muted);cursor:pointer}.est2 .est2-iconbtn:hover:not(:disabled){background:var(--est-bg-hover);color:var(--est-fg)}.est2 .est2-iconbtn:disabled{opacity:.4;cursor:default}.est2 .est2-dialog__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid var(--est-border)}.est2 .est2-dialog__spacer{flex:1 1 auto}.est2 .est2-btn{padding:7px 14px;border-radius:var(--est-radius-sm);font:inherit;font-weight:600;cursor:pointer;border:1px solid transparent}.est2 .est2-btn--ghost{background:transparent;color:var(--est-fg);border-color:var(--est-border)}.est2 .est2-btn--ghost:hover{background:var(--est-bg-hover)}.est2 .est2-btn--primary{background:var(--est-accent);color:var(--est-accent-fg)}.est2 .est2-btn--primary:hover{filter:brightness(1.05)}.est2 .est2-empty{padding:48px 16px;text-align:center;color:var(--est-fg-faint);font-size:var(--est-fs-sm)}.ngx-contextmenu{--ctx-bg: #ffffff;--ctx-fg: #1a1d24;--ctx-muted: #626b7a;--ctx-border: #e6e9ef;--ctx-hover: #f2f4f7;--ctx-accent: #4f46e5;--ctx-shadow: 0 10px 28px -8px rgba(16, 24, 40, .22), 0 2px 8px -3px rgba(16, 24, 40, .14);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.ngx-contextmenu .dropdown-menu{display:block;min-width:200px;margin:0;padding:6px;list-style:none;background:var(--ctx-bg);border:1px solid var(--ctx-border);border-radius:12px;box-shadow:var(--ctx-shadow);animation:est2-ctx-in .12s cubic-bezier(.16,1,.3,1)}.ngx-contextmenu li{list-style:none;margin:0}.ngx-contextmenu li>a{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:7px;color:var(--ctx-fg);font-size:13.5px;line-height:1.2;text-decoration:none;cursor:pointer;white-space:nowrap;transition:background .12s ease,color .12s ease}.ngx-contextmenu li>a:hover,.ngx-contextmenu li>a:focus{background:var(--ctx-hover);color:var(--ctx-fg);text-decoration:none;outline:none}.ngx-contextmenu li.divider,.ngx-contextmenu li[role=separator]{height:1px;margin:6px 8px;padding:0;background:var(--ctx-border)}.ngx-contextmenu li.disabled>a,.ngx-contextmenu li[aria-disabled=true]>a{color:var(--ctx-muted);opacity:.55;pointer-events:none}@keyframes est2-ctx-in{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@media(prefers-reduced-motion:reduce){.ngx-contextmenu .dropdown-menu{animation:none}}\n"] }]
|
|
10285
10392
|
}], ctorParameters: () => [{ type: i1.NgControl, decorators: [{
|
|
10286
10393
|
type: Self
|
|
10287
10394
|
}, {
|