@esfaenza/es-table 20.3.32 → 20.3.33
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 +197 -47
- package/fesm2022/esfaenza-es-table.mjs.map +1 -1
- package/index.d.ts +40 -1
- package/package.json +1 -1
|
@@ -5794,6 +5794,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
5794
5794
|
class EsTableModuleConfig {
|
|
5795
5795
|
}
|
|
5796
5796
|
|
|
5797
|
+
// =============================================================================
|
|
5798
|
+
// Cache dei formattatori Intl
|
|
5799
|
+
// -----------------------------------------------------------------------------
|
|
5800
|
+
// `Number.prototype.toLocaleString(locale, {opts})` e l'equivalente su Date
|
|
5801
|
+
// COSTRUISCONO un formattatore a ogni chiamata: con un oggetto di opzioni nuovo
|
|
5802
|
+
// ogni volta la cache interna del motore non scatta, e la costruzione costa
|
|
5803
|
+
// decine di microsecondi. Su una griglia è il costo dominante del primo render
|
|
5804
|
+
// e di ogni cambio pagina (ogni cella nuova formatta il proprio valore).
|
|
5805
|
+
//
|
|
5806
|
+
// Qui i formattatori sono creati una sola volta per combinazione
|
|
5807
|
+
// (locale + opzioni) e riusati. Le chiavi sono poche e limitate dai tipi
|
|
5808
|
+
// supportati, quindi le mappe restano piccole per tutta la vita dell'app.
|
|
5809
|
+
// =============================================================================
|
|
5810
|
+
const numberFormatters = new Map();
|
|
5811
|
+
const dateFormatters = new Map();
|
|
5812
|
+
/** Formattatore numerico per `locale` con un numero fisso di decimali (o automatico) */
|
|
5813
|
+
function numberFormatter(locale, digits) {
|
|
5814
|
+
const key = (locale || '') + '|' + (digits ?? '-');
|
|
5815
|
+
let f = numberFormatters.get(key);
|
|
5816
|
+
if (!f) {
|
|
5817
|
+
f = new Intl.NumberFormat(locale || undefined, digits != null ? { minimumFractionDigits: digits, maximumFractionDigits: digits } : undefined);
|
|
5818
|
+
numberFormatters.set(key, f);
|
|
5819
|
+
}
|
|
5820
|
+
return f;
|
|
5821
|
+
}
|
|
5822
|
+
/** Formattatore di date per `locale` + opzioni, identificate da `variant` */
|
|
5823
|
+
function dateFormatter(locale, variant, opts) {
|
|
5824
|
+
const key = (locale || '') + '|' + variant;
|
|
5825
|
+
let f = dateFormatters.get(key);
|
|
5826
|
+
if (!f) {
|
|
5827
|
+
f = new Intl.DateTimeFormat(locale || undefined, opts);
|
|
5828
|
+
dateFormatters.set(key, f);
|
|
5829
|
+
}
|
|
5830
|
+
return f;
|
|
5831
|
+
}
|
|
5832
|
+
|
|
5797
5833
|
/**
|
|
5798
5834
|
* Formattazione di presentazione per il renderer di default (celle senza template).
|
|
5799
5835
|
* Self-contained (Intl / Date nativi) per non trascinare dipendenze esterne.
|
|
@@ -5831,15 +5867,15 @@ class Est2FormatPipe {
|
|
|
5831
5867
|
let digits = defDigits;
|
|
5832
5868
|
if (format && /^F\d+$/.test(format))
|
|
5833
5869
|
digits = parseInt(format.slice(1), 10);
|
|
5834
|
-
return
|
|
5870
|
+
return numberFormatter(locale, digits).format(n);
|
|
5835
5871
|
}
|
|
5836
5872
|
date(value, locale, withTime) {
|
|
5837
5873
|
const d = value instanceof Date ? value : new Date(value);
|
|
5838
5874
|
if (isNaN(d.getTime()))
|
|
5839
5875
|
return String(value);
|
|
5840
|
-
return
|
|
5841
|
-
? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }
|
|
5842
|
-
: { year: 'numeric', month: '2-digit', day: '2-digit' });
|
|
5876
|
+
return withTime
|
|
5877
|
+
? dateFormatter(locale, 'datetime', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }).format(d)
|
|
5878
|
+
: dateFormatter(locale, 'date', { year: 'numeric', month: '2-digit', day: '2-digit' }).format(d);
|
|
5843
5879
|
}
|
|
5844
5880
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: Est2FormatPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
5845
5881
|
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.28", ngImport: i0, type: Est2FormatPipe, isStandalone: false, name: "est2_format" }); }
|
|
@@ -5950,7 +5986,7 @@ function formatAggValue(value, format, locale) {
|
|
|
5950
5986
|
: format === 'long'
|
|
5951
5987
|
? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }
|
|
5952
5988
|
: { weekday: 'short', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' };
|
|
5953
|
-
return
|
|
5989
|
+
return dateFormatter(locale, format, opts).format(d);
|
|
5954
5990
|
}
|
|
5955
5991
|
return value;
|
|
5956
5992
|
}
|
|
@@ -7078,6 +7114,8 @@ class EsTable2Component {
|
|
|
7078
7114
|
this.ShowItemGroupsColumns = linkedSignal(() => this.io(this._ShowItemGroupsColumns(), this.defaults?.ShowItemGroupsColumns, false), ...(ngDevMode ? [{ debugName: "ShowItemGroupsColumns" }] : []));
|
|
7079
7115
|
/** Mappa ownKey(string) -> figli diretti, per la modalità gerarchica */
|
|
7080
7116
|
this.hierChildren = new Map();
|
|
7117
|
+
/** Indice `chiave-propria → riga`: evita la scansione lineare per risalire ai padri */
|
|
7118
|
+
this.hierByKey = new Map();
|
|
7081
7119
|
/** Paginazione arrayMode */
|
|
7082
7120
|
this.arrayPage = signal(1, ...(ngDevMode ? [{ debugName: "arrayPage" }] : []));
|
|
7083
7121
|
/** Ordinamenti attivi (id -> 'ASC'|'DESC') */
|
|
@@ -7103,6 +7141,22 @@ class EsTable2Component {
|
|
|
7103
7141
|
// ===========================================================================
|
|
7104
7142
|
/** Colonne visibili, con le colonne pinnate spostate in testa (stabile) */
|
|
7105
7143
|
this.visibleColumns = computed(() => this.sortStructural(this.columns().filter(c => c.visible)), ...(ngDevMode ? [{ debugName: "visibleColumns" }] : []));
|
|
7144
|
+
/**
|
|
7145
|
+
* Stile PER-COLONNA precalcolato (larghezza, offset sticky, inizio gruppo).
|
|
7146
|
+
* Il ciclo delle celle è annidato in quello delle righe, quindi senza questo ogni
|
|
7147
|
+
* valore si ricalcolerebbe righe×colonne volte a ogni render: qui si calcola una
|
|
7148
|
+
* volta per colonna e la cella fa un solo accesso per indice.
|
|
7149
|
+
*/
|
|
7150
|
+
this.colStyles = computed(() => {
|
|
7151
|
+
const cols = this.visibleColumns();
|
|
7152
|
+
const offsets = this.pinnedOffsets();
|
|
7153
|
+
const bounds = this.columnGroupBoundaries();
|
|
7154
|
+
return cols.map((c, ci) => ({
|
|
7155
|
+
width: this.colWidthPx(c),
|
|
7156
|
+
left: c.pinned ? (offsets[ci] ?? 0) : null,
|
|
7157
|
+
groupStart: bounds.has(ci)
|
|
7158
|
+
}));
|
|
7159
|
+
}, ...(ngDevMode ? [{ debugName: "colStyles" }] : []));
|
|
7106
7160
|
/** Numero di colonne pinnate visibili */
|
|
7107
7161
|
this.pinnedCount = computed(() => this.visibleColumns().filter(c => c.pinned).length, ...(ngDevMode ? [{ debugName: "pinnedCount" }] : []));
|
|
7108
7162
|
/** true se c'è almeno una colonna pinnata */
|
|
@@ -7216,6 +7270,40 @@ class EsTable2Component {
|
|
|
7216
7270
|
/** Avviso transitorio mostrato in un piccolo toast (es. incolla incompatibile) */
|
|
7217
7271
|
this.notice = signal(null, ...(ngDevMode ? [{ debugName: "notice" }] : []));
|
|
7218
7272
|
this.noticeTimer = null;
|
|
7273
|
+
/** Item (foglia) appartenenti a un gruppo, a qualsiasi profondità */
|
|
7274
|
+
/**
|
|
7275
|
+
* Indice `chiave-gruppo → item[]`, ricalcolato una sola volta per ogni nuova
|
|
7276
|
+
* `boundSource()`. Le chiavi di gruppo sono composite (`IT¦Roma¦Shop`), quindi ogni
|
|
7277
|
+
* PREFISSO della chiave di un item è un suo gruppo antenato: l'indice si costruisce
|
|
7278
|
+
* in O(righe × profondità) con una sola passata.
|
|
7279
|
+
*
|
|
7280
|
+
* Serve perché la checkbox di gruppo chiama `groupSelectionState` DUE volte per
|
|
7281
|
+
* riga-gruppo a ogni ciclo di change-detection: con la vecchia `filter` su tutte le
|
|
7282
|
+
* righe erano 2·gruppi·righe confronti per render (quadratico nel numero di righe).
|
|
7283
|
+
*/
|
|
7284
|
+
this.groupItemsIndex = computed(() => {
|
|
7285
|
+
const map = new Map();
|
|
7286
|
+
for (const r of this.boundSource()) {
|
|
7287
|
+
if (!r._item)
|
|
7288
|
+
continue;
|
|
7289
|
+
const key = r['_thisKey'];
|
|
7290
|
+
if (!key)
|
|
7291
|
+
continue;
|
|
7292
|
+
for (let from = 0;;) {
|
|
7293
|
+
const i = key.indexOf('¦', from);
|
|
7294
|
+
const k = i < 0 ? key : key.slice(0, i);
|
|
7295
|
+
const arr = map.get(k);
|
|
7296
|
+
if (arr)
|
|
7297
|
+
arr.push(r);
|
|
7298
|
+
else
|
|
7299
|
+
map.set(k, [r]);
|
|
7300
|
+
if (i < 0)
|
|
7301
|
+
break;
|
|
7302
|
+
from = i + 1;
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
return map;
|
|
7306
|
+
}, ...(ngDevMode ? [{ debugName: "groupItemsIndex" }] : []));
|
|
7219
7307
|
// ===========================================================================
|
|
7220
7308
|
// Fase 6 — Colonne (visibilità/ordine), preferenze, export
|
|
7221
7309
|
// ===========================================================================
|
|
@@ -7241,6 +7329,8 @@ class EsTable2Component {
|
|
|
7241
7329
|
* troncava (su una tabella di poche righe il menu usciva dal box e spariva).
|
|
7242
7330
|
*/
|
|
7243
7331
|
this.menuAnchor = signal(null, ...(ngDevMode ? [{ debugName: "menuAnchor" }] : []));
|
|
7332
|
+
/** Ultima cella già valutata per il tooltip (evita di rimisurare a ogni mousemove) */
|
|
7333
|
+
this.hoveredCell = null;
|
|
7244
7334
|
// --- Ridimensionamento colonne ---------------------------------------------
|
|
7245
7335
|
// L'handle è mostrato quando `col.pinned || ColumnsResizable()`: le colonne PINNATE
|
|
7246
7336
|
// sono SEMPRE ridimensionabili (sono a larghezza fissa per contratto); `[Columns
|
|
@@ -7523,8 +7613,20 @@ class EsTable2Component {
|
|
|
7523
7613
|
if (!op.conditionField)
|
|
7524
7614
|
return true;
|
|
7525
7615
|
const src = item?.properties ?? item;
|
|
7526
|
-
|
|
7527
|
-
|
|
7616
|
+
if (src == null)
|
|
7617
|
+
return false;
|
|
7618
|
+
// Percorso rapido: accesso diretto. Il fallback case-insensitive costava un
|
|
7619
|
+
// `Object.keys` + `toLowerCase` per riga a OGNI ciclo di change-detection.
|
|
7620
|
+
let v = src[op.conditionField];
|
|
7621
|
+
if (v === undefined) {
|
|
7622
|
+
const lower = op.conditionField.toLowerCase();
|
|
7623
|
+
for (const k in src)
|
|
7624
|
+
if (k.toLowerCase() === lower) {
|
|
7625
|
+
v = src[k];
|
|
7626
|
+
break;
|
|
7627
|
+
}
|
|
7628
|
+
}
|
|
7629
|
+
return v !== undefined && String(v) === String(op.conditionValue);
|
|
7528
7630
|
}
|
|
7529
7631
|
dynamicOperation(item, id) {
|
|
7530
7632
|
this.onDynamicOperation.emit({ id, item });
|
|
@@ -7751,8 +7853,14 @@ class EsTable2Component {
|
|
|
7751
7853
|
it._selected = false;
|
|
7752
7854
|
return;
|
|
7753
7855
|
}
|
|
7856
|
+
// Set costruito UNA volta sola. Con `includes` dentro il ciclo ogni refresh
|
|
7857
|
+
// (ordinamento, cambio pagina, nuovo bind, tick di autoupdate) costava
|
|
7858
|
+
// O(righe × selezionati): su una tabella array con 5.000 righe tutte
|
|
7859
|
+
// selezionate erano 25 milioni di confronti per refresh.
|
|
7860
|
+
const all = !!sel.all;
|
|
7861
|
+
const lookup = new Set(all ? sel.exclusions : sel.items);
|
|
7754
7862
|
for (const it of items)
|
|
7755
|
-
it._selected =
|
|
7863
|
+
it._selected = all ? !lookup.has(it) : lookup.has(it);
|
|
7756
7864
|
}
|
|
7757
7865
|
/** Ricalcola il conteggio: in modalità **all** è (totale - esclusioni) */
|
|
7758
7866
|
refreshSelectionCount(sel) {
|
|
@@ -7934,10 +8042,15 @@ class EsTable2Component {
|
|
|
7934
8042
|
r._selected = false;
|
|
7935
8043
|
sel.items = [];
|
|
7936
8044
|
}
|
|
8045
|
+
// Set per il dedup: `sel.items.includes` dentro il ciclo rendeva il range O(righe²)
|
|
8046
|
+
const have = new Set(sel.items);
|
|
7937
8047
|
for (let i = lo; i <= hi; i++) {
|
|
7938
|
-
rows[i]
|
|
7939
|
-
|
|
7940
|
-
|
|
8048
|
+
const r = rows[i];
|
|
8049
|
+
r._selected = true;
|
|
8050
|
+
if (!have.has(r)) {
|
|
8051
|
+
have.add(r);
|
|
8052
|
+
sel.items.push(r);
|
|
8053
|
+
}
|
|
7941
8054
|
}
|
|
7942
8055
|
// l'ancora resta invariata, così Shift+click successivi ri-estendono da lì
|
|
7943
8056
|
sel.all = false;
|
|
@@ -7985,6 +8098,7 @@ class EsTable2Component {
|
|
|
7985
8098
|
}
|
|
7986
8099
|
/** mouseover durante il drag: estende il range (solo se la cella cambia davvero) */
|
|
7987
8100
|
onGridMouseOver(event) {
|
|
8101
|
+
this.onCellHover(event); // tooltip in delega (vedi onCellHover)
|
|
7988
8102
|
if (!this.rangeDragging() || !this.rangeActive())
|
|
7989
8103
|
return;
|
|
7990
8104
|
if ((event.buttons & 1) === 0) {
|
|
@@ -8257,9 +8371,8 @@ class EsTable2Component {
|
|
|
8257
8371
|
}
|
|
8258
8372
|
this.cdr.markForCheck();
|
|
8259
8373
|
}
|
|
8260
|
-
/** Item (foglia) appartenenti a un gruppo, a qualsiasi profondità */
|
|
8261
8374
|
groupItems(group) {
|
|
8262
|
-
return this.
|
|
8375
|
+
return this.groupItemsIndex().get(group?._thisKey) ?? [];
|
|
8263
8376
|
}
|
|
8264
8377
|
/** Stato di selezione aggregato di un gruppo, per la sua checkbox */
|
|
8265
8378
|
groupSelectionState(group) {
|
|
@@ -8276,22 +8389,27 @@ class EsTable2Component {
|
|
|
8276
8389
|
const items = this.groupItems(group);
|
|
8277
8390
|
const select = this.groupSelectionState(group) !== 'all';
|
|
8278
8391
|
const sel = this.currentSelection();
|
|
8279
|
-
for (const it of items)
|
|
8392
|
+
for (const it of items)
|
|
8280
8393
|
it._selected = select;
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8394
|
+
// Set al posto di includes/filter DENTRO il ciclo: con k item nel gruppo era O(k²)
|
|
8395
|
+
const target = sel.all ? sel.exclusions : sel.items; // in modalità "tutto" si accumulano esclusioni
|
|
8396
|
+
if (select === sel.all) {
|
|
8397
|
+
// rimuovo gli item dalla lista di destinazione
|
|
8398
|
+
const drop = new Set(items);
|
|
8399
|
+
const kept = target.filter(i => !drop.has(i));
|
|
8400
|
+
if (sel.all)
|
|
8401
|
+
sel.exclusions = kept;
|
|
8402
|
+
else
|
|
8403
|
+
sel.items = kept;
|
|
8404
|
+
}
|
|
8405
|
+
else {
|
|
8406
|
+
// li aggiungo (dedup via Set, senza scandire l'array a ogni push)
|
|
8407
|
+
const have = new Set(target);
|
|
8408
|
+
for (const it of items)
|
|
8409
|
+
if (!have.has(it)) {
|
|
8410
|
+
have.add(it);
|
|
8411
|
+
target.push(it);
|
|
8291
8412
|
}
|
|
8292
|
-
else
|
|
8293
|
-
sel.items = sel.items.filter(i => i !== it);
|
|
8294
|
-
}
|
|
8295
8413
|
}
|
|
8296
8414
|
this.refreshSelectionCount(sel);
|
|
8297
8415
|
this.syncGlobalCheck();
|
|
@@ -8345,8 +8463,11 @@ class EsTable2Component {
|
|
|
8345
8463
|
const ordered = assignHierarchyLevels(items, this.OwnKey(), this.ParentKey(), this.StartsExpanded(), this.AutoSortHierarchy());
|
|
8346
8464
|
// mappa padre -> figli per lookup O(1) (cascade selection, indeterminate)
|
|
8347
8465
|
this.hierChildren.clear();
|
|
8466
|
+
this.hierByKey.clear();
|
|
8348
8467
|
const pk = this.ParentKey();
|
|
8468
|
+
const ok = this.OwnKey();
|
|
8349
8469
|
for (const r of ordered) {
|
|
8470
|
+
this.hierByKey.set(String(r[ok]), r);
|
|
8350
8471
|
const pv = r[pk];
|
|
8351
8472
|
if (pv === null || pv === undefined || pv === '')
|
|
8352
8473
|
continue;
|
|
@@ -8363,31 +8484,29 @@ class EsTable2Component {
|
|
|
8363
8484
|
directChildren(item) {
|
|
8364
8485
|
return this.hierChildren.get(String(item[this.OwnKey()])) ?? [];
|
|
8365
8486
|
}
|
|
8366
|
-
|
|
8367
|
-
|
|
8487
|
+
/** Selezione in cascata: propaga lo stato al sottoalbero e ricalcola gli antenati */
|
|
8488
|
+
cascadeSelection(item, selected) {
|
|
8489
|
+
item._selected = selected;
|
|
8490
|
+
// discesa iterativa senza materializzare l'array del sottoalbero
|
|
8368
8491
|
const stack = [...this.directChildren(item)];
|
|
8369
8492
|
while (stack.length) {
|
|
8370
8493
|
const n = stack.pop();
|
|
8371
|
-
|
|
8494
|
+
n._selected = selected;
|
|
8372
8495
|
const kids = this.directChildren(n);
|
|
8373
|
-
|
|
8374
|
-
stack.push(
|
|
8496
|
+
for (let i = 0; i < kids.length; i++)
|
|
8497
|
+
stack.push(kids[i]);
|
|
8375
8498
|
}
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
cascadeSelection(item, selected) {
|
|
8380
|
-
item._selected = selected;
|
|
8381
|
-
for (const d of this.hierDescendants(item))
|
|
8382
|
-
d._selected = selected;
|
|
8383
|
-
// risali: un padre è selezionato sse tutti i figli lo sono
|
|
8499
|
+
// risali: un padre è selezionato sse tutti i figli lo sono. La lookup del padre
|
|
8500
|
+
// passa da `hierByKey` (O(1)): prima era una `find` su TUTTE le righe per ogni
|
|
8501
|
+
// livello di profondità, con una String() per riga visitata.
|
|
8384
8502
|
const pk = this.ParentKey();
|
|
8385
8503
|
let current = item;
|
|
8386
|
-
|
|
8504
|
+
let guard = 0;
|
|
8505
|
+
while (guard++ < 4096) {
|
|
8387
8506
|
const pv = current[pk];
|
|
8388
8507
|
if (pv === null || pv === undefined || pv === '')
|
|
8389
8508
|
break;
|
|
8390
|
-
const parent = this.
|
|
8509
|
+
const parent = this.hierByKey.get(String(pv));
|
|
8391
8510
|
if (!parent)
|
|
8392
8511
|
break;
|
|
8393
8512
|
const kids = this.directChildren(parent);
|
|
@@ -8406,7 +8525,25 @@ class EsTable2Component {
|
|
|
8406
8525
|
hierarchyIndeterminate(item) {
|
|
8407
8526
|
if (!this.CascadeSelection() || !item?.parent || item._selected)
|
|
8408
8527
|
return false;
|
|
8409
|
-
return this.
|
|
8528
|
+
return this.hierAnyDescendantSelected(item);
|
|
8529
|
+
}
|
|
8530
|
+
/**
|
|
8531
|
+
* Visita in profondità con USCITA ANTICIPATA al primo discendente selezionato.
|
|
8532
|
+
* È legata alla checkbox di OGNI riga, quindi girava a ogni ciclo di
|
|
8533
|
+
* change-detection: la versione precedente materializzava invece l'intero
|
|
8534
|
+
* sottoalbero in un array nuovo per ogni riga e per ogni render.
|
|
8535
|
+
*/
|
|
8536
|
+
hierAnyDescendantSelected(item) {
|
|
8537
|
+
const stack = [...this.directChildren(item)];
|
|
8538
|
+
while (stack.length) {
|
|
8539
|
+
const n = stack.pop();
|
|
8540
|
+
if (n._selected)
|
|
8541
|
+
return true;
|
|
8542
|
+
const kids = this.directChildren(n);
|
|
8543
|
+
for (let i = 0; i < kids.length; i++)
|
|
8544
|
+
stack.push(kids[i]);
|
|
8545
|
+
}
|
|
8546
|
+
return false;
|
|
8410
8547
|
}
|
|
8411
8548
|
/** Espande/collassa un nodo dell'albero */
|
|
8412
8549
|
toggleHierarchyNode(item) {
|
|
@@ -9079,10 +9216,23 @@ class EsTable2Component {
|
|
|
9079
9216
|
* anziché `cellDisplayText`, che conosce solo `property`/`id` e non il contenuto di
|
|
9080
9217
|
* un template custom (era il bug del tooltip che mostrava sempre l'id colonna).
|
|
9081
9218
|
*/
|
|
9219
|
+
/**
|
|
9220
|
+
* Tooltip "intelligente" sulle celle a larghezza fissa: mostra il testo REALE
|
|
9221
|
+
* solo quando è troncato. In DELEGA sulla `<table>` (non un `(mouseenter)` per
|
|
9222
|
+
* cella): un listener per cella significava installarne uno per ogni `<td>` a
|
|
9223
|
+
* ogni creazione di riga — su una pagina da 50×43 sono 2.150 listener per
|
|
9224
|
+
* cambio pagina.
|
|
9225
|
+
*/
|
|
9082
9226
|
onCellHover(ev) {
|
|
9083
|
-
|
|
9084
|
-
|
|
9227
|
+
// In delega `currentTarget` è la `<table>`: parto SEMPRE da `target` (l'elemento
|
|
9228
|
+
// effettivamente sotto il mouse, spesso un figlio della cella) e risalgo al `<td>`.
|
|
9229
|
+
const from = (ev.target ?? ev.currentTarget);
|
|
9230
|
+
const el = typeof from?.closest === 'function' ? from.closest('td') : from;
|
|
9231
|
+
if (!el || typeof el.setAttribute !== 'function' || typeof el.removeAttribute !== 'function')
|
|
9085
9232
|
return;
|
|
9233
|
+
if (this.hoveredCell === el)
|
|
9234
|
+
return; // stessa cella: niente rimisurazione
|
|
9235
|
+
this.hoveredCell = el;
|
|
9086
9236
|
if (el.scrollWidth > el.clientWidth + 1) {
|
|
9087
9237
|
const txt = (el.textContent || '').trim();
|
|
9088
9238
|
if (txt)
|
|
@@ -9138,11 +9288,11 @@ class EsTable2Component {
|
|
|
9138
9288
|
this.saveColumnPrefs();
|
|
9139
9289
|
}
|
|
9140
9290
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: EsTable2Component, deps: [{ token: i1.NgControl, optional: true, self: true }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i2$1.PreferencesService, optional: true }, { token: i0.Injector }, { token: EST2_DEFAULTS, optional: true }, { token: EST2_DEBUG, optional: true }, { token: EST2_EXPORT_GLOBAL_ACL, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9141
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }, _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 }, 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 } }, 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" }, 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 }], 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 Aggiorna ogni\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 secondi\n </label>\n <label class=\"est2-switch\" title=\"Attiva/disattiva auto-aggiornamento\">\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 <!-- 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>Tutti i <strong>{{ selectedCount }}</strong> elementi sono selezionati</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ selectedCount === 1 ? 'elemento selezionato' : 'elementi selezionati' }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">Seleziona tutti i {{ totalCount() }} elementi</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">Azzera selezione</span>\n </div>\n }\n\n <div class=\"est2-scroll\" [style.max-height.px]=\"MaxHeight()\">\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 aria-label=\"Seleziona tutto\" />\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 <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]=\"columnGroupBoundaries().has(ci)\"\n [class.est2-pinned]=\"col.pinned\"\n [style.left.px]=\"col.pinned ? pinnedLeftPx(ci) : null\"\n [style.width.px]=\"colWidthPx(col)\"\n [style.min-width.px]=\"colWidthPx(col)\"\n [style.max-width.px]=\"colWidthPx(col)\"\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 ? 'Sblocca colonna' : 'Blocca colonna a sinistra'\"\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=\"Trascina per ridimensionare\"\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=\"Esporta\" (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=\"Colonne\" (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=\"Opzioni\" (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 @for (item of boundSource(); track trackRow($index, item); let ri = $index) {\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted\"\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 aria-label=\"Seleziona gruppo\" />\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 aria-label=\"Seleziona riga\" />\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 <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]=\"columnGroupBoundaries().has(ci)\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-fixedw]=\"colWidthPx(col) != null\"\n [style.left.px]=\"col.pinned ? pinnedLeftPx(ci) : null\"\n [style.width.px]=\"colWidthPx(col)\"\n [style.min-width.px]=\"colWidthPx(col)\"\n [style.max-width.px]=\"colWidthPx(col)\"\n (mouseenter)=\"onCellHover($event)\"\n [class.est2-td--group-key]=\"item._group && item.column === col.id\"\n [attr.data-r]=\"rangeActive() && !item._group ? ri : null\"\n [attr.data-c]=\"rangeActive() && !item._group ? ci : null\"\n [class.est2-cell-sel]=\"rangeActive() && inRange(ri, ci)\"\n [class.est2-cell-sel-t]=\"rangeActive() && inRange(ri, ci) && ri === rangeRect()!.top\"\n [class.est2-cell-sel-b]=\"rangeActive() && inRange(ri, ci) && ri === rangeRect()!.bottom\"\n [class.est2-cell-sel-l]=\"rangeActive() && inRange(ri, ci) && ci === rangeRect()!.left\"\n [class.est2-cell-sel-r]=\"rangeActive() && inRange(ri, ci) && ci === rangeRect()!.right\"\n [class.est2-cell-editing]=\"isEditing(ri, ci)\"\n (dblclick)=\"onCellDblClick(item, col, ri, ci)\">\n @if (isEditing(ri, ci)) {\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) {\n <button type=\"button\" class=\"est2-rowaction\" title=\"Ripristina\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" title=\"Rimuovi\" (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()\">Nessun elemento da visualizzare</td>\n </tr>\n }\n </tbody>\n }\n </table>\n\n <!-- Overlay di caricamento -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>Caricamento\u2026</span>\n </div>\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')\">Esporta CSV</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">Esporta Excel (XLSX)</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">Esporta CSV</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\" (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()) { Visibilit\u00E0 e ordine colonne }\n @else if (HiddenColumns()) { Visibilit\u00E0 colonne }\n @else { Ordine colonne }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" aria-label=\"Chiudi\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">Mostra tutte</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">Nascondi tutte</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=\"Trascina per riordinare\" 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 ? 'Colonna fissa: non spostabile' : 'Colonna bloccata a sinistra: non spostabile'\" 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 ? 'Colonna fissa: sempre visibile' : ''\">\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 ? 'Le colonne fisse non sono bloccabili' : (c.grouped ? 'Le colonne di un gruppo non sono bloccabili' : (c.pinned ? 'Sblocca' : 'Blocca a sinistra'))\"\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=\"Su\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" title=\"Gi\u00F9\" (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()\">Ripristina</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">Annulla</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">Applica</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>Nessuna operazione disponibile\u2026</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}@media(prefers-color-scheme:dark){.est2{--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--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 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 .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--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{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;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);z-index:5;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;background:color-mix(in srgb,#000 32%,transparent)}.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}@media(prefers-color-scheme:dark){.ngx-contextmenu{--ctx-bg: #1e222b;--ctx-fg: #e7eaf0;--ctx-muted: #9aa3b2;--ctx-border: #2a2f3a;--ctx-hover: #232733;--ctx-accent: #7c74ff;--ctx-shadow: 0 12px 30px -8px rgba(0, 0, 0, .6), 0 2px 8px -3px rgba(0, 0, 0, .5)}}@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: Est2FormatPipe, name: "est2_format" }, { kind: "pipe", type: Est2LookupPipe, name: "est2_lookup" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
9291
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }, _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 }, 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 } }, 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" }, 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 }], 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 Aggiorna ogni\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 secondi\n </label>\n <label class=\"est2-switch\" title=\"Attiva/disattiva auto-aggiornamento\">\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 <!-- 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>Tutti i <strong>{{ selectedCount }}</strong> elementi sono selezionati</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ selectedCount === 1 ? 'elemento selezionato' : 'elementi selezionati' }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">Seleziona tutti i {{ totalCount() }} elementi</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">Azzera selezione</span>\n </div>\n }\n\n <div class=\"est2-scroll\" [style.max-height.px]=\"MaxHeight()\">\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 aria-label=\"Seleziona tutto\" />\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 ? 'Sblocca colonna' : 'Blocca colonna a sinistra'\"\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=\"Trascina per ridimensionare\"\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=\"Esporta\" (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=\"Colonne\" (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=\"Opzioni\" (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 @for (item of boundSource(); track trackRow($index, item); let ri = $index) {\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted\"\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 aria-label=\"Seleziona gruppo\" />\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 aria-label=\"Seleziona riga\" />\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 (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) {\n <button type=\"button\" class=\"est2-rowaction\" title=\"Ripristina\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" title=\"Rimuovi\" (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()\">Nessun elemento da visualizzare</td>\n </tr>\n }\n </tbody>\n }\n </table>\n\n <!-- Overlay di caricamento -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>Caricamento\u2026</span>\n </div>\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')\">Esporta CSV</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">Esporta Excel (XLSX)</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">Esporta CSV</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\" (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()) { Visibilit\u00E0 e ordine colonne }\n @else if (HiddenColumns()) { Visibilit\u00E0 colonne }\n @else { Ordine colonne }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" aria-label=\"Chiudi\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">Mostra tutte</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">Nascondi tutte</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=\"Trascina per riordinare\" 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 ? 'Colonna fissa: non spostabile' : 'Colonna bloccata a sinistra: non spostabile'\" 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 ? 'Colonna fissa: sempre visibile' : ''\">\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 ? 'Le colonne fisse non sono bloccabili' : (c.grouped ? 'Le colonne di un gruppo non sono bloccabili' : (c.pinned ? 'Sblocca' : 'Blocca a sinistra'))\"\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=\"Su\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" title=\"Gi\u00F9\" (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()\">Ripristina</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">Annulla</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">Applica</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>Nessuna operazione disponibile\u2026</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}@media(prefers-color-scheme:dark){.est2{--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--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 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 .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--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{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;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);z-index:5;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;background:color-mix(in srgb,#000 32%,transparent)}.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}@media(prefers-color-scheme:dark){.ngx-contextmenu{--ctx-bg: #1e222b;--ctx-fg: #e7eaf0;--ctx-muted: #9aa3b2;--ctx-border: #2a2f3a;--ctx-hover: #232733;--ctx-accent: #7c74ff;--ctx-shadow: 0 12px 30px -8px rgba(0, 0, 0, .6), 0 2px 8px -3px rgba(0, 0, 0, .5)}}@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: Est2FormatPipe, name: "est2_format" }, { kind: "pipe", type: Est2LookupPipe, name: "est2_lookup" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
9142
9292
|
}
|
|
9143
9293
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: EsTable2Component, decorators: [{
|
|
9144
9294
|
type: Component,
|
|
9145
|
-
args: [{ selector: 'es-table2', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, 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 Aggiorna ogni\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 secondi\n </label>\n <label class=\"est2-switch\" title=\"Attiva/disattiva auto-aggiornamento\">\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 <!-- 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>Tutti i <strong>{{ selectedCount }}</strong> elementi sono selezionati</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ selectedCount === 1 ? 'elemento selezionato' : 'elementi selezionati' }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">Seleziona tutti i {{ totalCount() }} elementi</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">Azzera selezione</span>\n </div>\n }\n\n <div class=\"est2-scroll\" [style.max-height.px]=\"MaxHeight()\">\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 aria-label=\"Seleziona tutto\" />\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 <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]=\"columnGroupBoundaries().has(ci)\"\n [class.est2-pinned]=\"col.pinned\"\n [style.left.px]=\"col.pinned ? pinnedLeftPx(ci) : null\"\n [style.width.px]=\"colWidthPx(col)\"\n [style.min-width.px]=\"colWidthPx(col)\"\n [style.max-width.px]=\"colWidthPx(col)\"\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 ? 'Sblocca colonna' : 'Blocca colonna a sinistra'\"\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=\"Trascina per ridimensionare\"\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=\"Esporta\" (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=\"Colonne\" (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=\"Opzioni\" (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 @for (item of boundSource(); track trackRow($index, item); let ri = $index) {\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted\"\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 aria-label=\"Seleziona gruppo\" />\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 aria-label=\"Seleziona riga\" />\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 <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]=\"columnGroupBoundaries().has(ci)\"\n [class.est2-pinned]=\"col.pinned\"\n [class.est2-fixedw]=\"colWidthPx(col) != null\"\n [style.left.px]=\"col.pinned ? pinnedLeftPx(ci) : null\"\n [style.width.px]=\"colWidthPx(col)\"\n [style.min-width.px]=\"colWidthPx(col)\"\n [style.max-width.px]=\"colWidthPx(col)\"\n (mouseenter)=\"onCellHover($event)\"\n [class.est2-td--group-key]=\"item._group && item.column === col.id\"\n [attr.data-r]=\"rangeActive() && !item._group ? ri : null\"\n [attr.data-c]=\"rangeActive() && !item._group ? ci : null\"\n [class.est2-cell-sel]=\"rangeActive() && inRange(ri, ci)\"\n [class.est2-cell-sel-t]=\"rangeActive() && inRange(ri, ci) && ri === rangeRect()!.top\"\n [class.est2-cell-sel-b]=\"rangeActive() && inRange(ri, ci) && ri === rangeRect()!.bottom\"\n [class.est2-cell-sel-l]=\"rangeActive() && inRange(ri, ci) && ci === rangeRect()!.left\"\n [class.est2-cell-sel-r]=\"rangeActive() && inRange(ri, ci) && ci === rangeRect()!.right\"\n [class.est2-cell-editing]=\"isEditing(ri, ci)\"\n (dblclick)=\"onCellDblClick(item, col, ri, ci)\">\n @if (isEditing(ri, ci)) {\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) {\n <button type=\"button\" class=\"est2-rowaction\" title=\"Ripristina\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" title=\"Rimuovi\" (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()\">Nessun elemento da visualizzare</td>\n </tr>\n }\n </tbody>\n }\n </table>\n\n <!-- Overlay di caricamento -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>Caricamento\u2026</span>\n </div>\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')\">Esporta CSV</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">Esporta Excel (XLSX)</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">Esporta CSV</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\" (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()) { Visibilit\u00E0 e ordine colonne }\n @else if (HiddenColumns()) { Visibilit\u00E0 colonne }\n @else { Ordine colonne }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" aria-label=\"Chiudi\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">Mostra tutte</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">Nascondi tutte</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=\"Trascina per riordinare\" 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 ? 'Colonna fissa: non spostabile' : 'Colonna bloccata a sinistra: non spostabile'\" 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 ? 'Colonna fissa: sempre visibile' : ''\">\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 ? 'Le colonne fisse non sono bloccabili' : (c.grouped ? 'Le colonne di un gruppo non sono bloccabili' : (c.pinned ? 'Sblocca' : 'Blocca a sinistra'))\"\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=\"Su\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" title=\"Gi\u00F9\" (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()\">Ripristina</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">Annulla</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">Applica</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>Nessuna operazione disponibile\u2026</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}@media(prefers-color-scheme:dark){.est2{--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--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 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 .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--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{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;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);z-index:5;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;background:color-mix(in srgb,#000 32%,transparent)}.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}@media(prefers-color-scheme:dark){.ngx-contextmenu{--ctx-bg: #1e222b;--ctx-fg: #e7eaf0;--ctx-muted: #9aa3b2;--ctx-border: #2a2f3a;--ctx-hover: #232733;--ctx-accent: #7c74ff;--ctx-shadow: 0 12px 30px -8px rgba(0, 0, 0, .6), 0 2px 8px -3px rgba(0, 0, 0, .5)}}@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"] }]
|
|
9295
|
+
args: [{ selector: 'es-table2', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, 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 Aggiorna ogni\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 secondi\n </label>\n <label class=\"est2-switch\" title=\"Attiva/disattiva auto-aggiornamento\">\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 <!-- 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>Tutti i <strong>{{ selectedCount }}</strong> elementi sono selezionati</span>\n } @else {\n <span><strong>{{ selectedCount }}</strong> {{ selectedCount === 1 ? 'elemento selezionato' : 'elementi selezionati' }}</span>\n @if (canSelectEverything) {\n <span class=\"est2-link\" (click)=\"selectEverything()\">Seleziona tutti i {{ totalCount() }} elementi</span>\n }\n }\n <span class=\"est2-selectbar__spacer\"></span>\n <span class=\"est2-link\" (click)=\"clearSelection()\">Azzera selezione</span>\n </div>\n }\n\n <div class=\"est2-scroll\" [style.max-height.px]=\"MaxHeight()\">\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 aria-label=\"Seleziona tutto\" />\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 ? 'Sblocca colonna' : 'Blocca colonna a sinistra'\"\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=\"Trascina per ridimensionare\"\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=\"Esporta\" (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=\"Colonne\" (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=\"Opzioni\" (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 @for (item of boundSource(); track trackRow($index, item); let ri = $index) {\n @if ((!grouped() && !Hierarchy()) || item._visible) {\n <tr [class]=\"rowClass(item)\"\n [class.est2-row--selected]=\"item._selected\"\n [class.est2-row--removed]=\"item.removed || item.deleted\"\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 aria-label=\"Seleziona gruppo\" />\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 aria-label=\"Seleziona riga\" />\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 (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) {\n <button type=\"button\" class=\"est2-rowaction\" title=\"Ripristina\" (click)=\"abortRemoval(item)\">\u21BA</button>\n } @else {\n <button type=\"button\" class=\"est2-rowaction est2-rowaction--danger\" title=\"Rimuovi\" (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()\">Nessun elemento da visualizzare</td>\n </tr>\n }\n </tbody>\n }\n </table>\n\n <!-- Overlay di caricamento -->\n @if (researchInProgress() || (firstBind() && ShowLoadingOnBootstrap())) {\n <div class=\"est2-loading\">\n <span class=\"est2-spinner\"></span>\n <span>Caricamento\u2026</span>\n </div>\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')\">Esporta CSV</button> }\n @if (XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('XLSX')\">Esporta Excel (XLSX)</button> }\n @if (!CSVExport() && !XLSXExport()) { <button type=\"button\" class=\"est2-menu-item\" (click)=\"export('CSV')\">Esporta CSV</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\" (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()) { Visibilit\u00E0 e ordine colonne }\n @else if (HiddenColumns()) { Visibilit\u00E0 colonne }\n @else { Ordine colonne }\n </span>\n <button type=\"button\" class=\"est2-dialog__close\" (click)=\"closeColumnsDialog()\" aria-label=\"Chiudi\">\u2715</button>\n </div>\n\n @if (HiddenColumns()) {\n <div class=\"est2-dialog__tools\">\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(true)\">Mostra tutte</button>\n <span class=\"est2-dialog__sep\">\u00B7</span>\n <button type=\"button\" class=\"est2-link\" (click)=\"dialogSetAll(false)\">Nascondi tutte</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=\"Trascina per riordinare\" 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 ? 'Colonna fissa: non spostabile' : 'Colonna bloccata a sinistra: non spostabile'\" 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 ? 'Colonna fissa: sempre visibile' : ''\">\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 ? 'Le colonne fisse non sono bloccabili' : (c.grouped ? 'Le colonne di un gruppo non sono bloccabili' : (c.pinned ? 'Sblocca' : 'Blocca a sinistra'))\"\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=\"Su\" (click)=\"dialogMove(i, -1)\"\n [disabled]=\"i === 0 || !canReorder(c) || !canReorder(dialogCols()[i - 1])\">\u2191</button>\n <button type=\"button\" class=\"est2-iconbtn\" title=\"Gi\u00F9\" (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()\">Ripristina</button>\n <span class=\"est2-dialog__spacer\"></span>\n <button type=\"button\" class=\"est2-btn est2-btn--ghost\" (click)=\"closeColumnsDialog()\">Annulla</button>\n <button type=\"button\" class=\"est2-btn est2-btn--primary\" (click)=\"applyColumnsDialog()\">Applica</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>Nessuna operazione disponibile\u2026</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}@media(prefers-color-scheme:dark){.est2{--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--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 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 .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--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{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;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);z-index:5;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;background:color-mix(in srgb,#000 32%,transparent)}.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}@media(prefers-color-scheme:dark){.ngx-contextmenu{--ctx-bg: #1e222b;--ctx-fg: #e7eaf0;--ctx-muted: #9aa3b2;--ctx-border: #2a2f3a;--ctx-hover: #232733;--ctx-accent: #7c74ff;--ctx-shadow: 0 12px 30px -8px rgba(0, 0, 0, .6), 0 2px 8px -3px rgba(0, 0, 0, .5)}}@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"] }]
|
|
9146
9296
|
}], ctorParameters: () => [{ type: i1.NgControl, decorators: [{
|
|
9147
9297
|
type: Self
|
|
9148
9298
|
}, {
|