wuic-framework-lib 1.0.20 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{wuic-framework-lib-chart-list.component-B44k_wZe.mjs → wuic-framework-lib-chart-list.component-B1V8Q-Qq.mjs} +2 -2
- package/fesm2022/{wuic-framework-lib-code-editor.component-D1dy4Dl9.mjs → wuic-framework-lib-code-editor.component-4N95qt3D.mjs} +26 -5
- package/fesm2022/{wuic-framework-lib-designer.component-CnOQbvEH.mjs → wuic-framework-lib-designer.component-pW1YK-oY.mjs} +4 -4
- package/fesm2022/{wuic-framework-lib-first-run-wizard.component-C9lswoxG.mjs → wuic-framework-lib-first-run-wizard.component-CaGePYLi.mjs} +14 -4
- package/fesm2022/{wuic-framework-lib-framework-docs.component-B6qNTs5H.mjs → wuic-framework-lib-framework-docs.component-m0IlS0Sl.mjs} +406 -10
- package/fesm2022/{wuic-framework-lib-pivot-builder.component-DMk26H0M.mjs → wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs} +19 -16
- package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-y3shST6g.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-Dm9GTZ07.mjs} +3 -3
- package/fesm2022/{wuic-framework-lib-report-designer.component-BnngR2eL.mjs → wuic-framework-lib-report-designer.component-FiAwNkPM.mjs} +2 -2
- package/fesm2022/{wuic-framework-lib-report-viewer.component-LGNRIQh2.mjs → wuic-framework-lib-report-viewer.component-DY0dGMrt.mjs} +2 -2
- package/fesm2022/{wuic-framework-lib-scheduler-list.component-DbcFMwx5.mjs → wuic-framework-lib-scheduler-list.component-3E5m-9Qd.mjs} +31 -5
- package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-DXyRX0YK.mjs → wuic-framework-lib-spreadsheet-list-sf.component-D9P9WbDl.mjs} +483 -24
- package/fesm2022/{wuic-framework-lib-workflow-designer.component-Bm719AWV.mjs → wuic-framework-lib-workflow-designer.component-4RC2EkGA.mjs} +25 -5
- package/fesm2022/{wuic-framework-lib-wuic-framework-lib-CGCZvtKK.mjs → wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs} +5082 -3802
- package/fesm2022/wuic-framework-lib.mjs +1 -1
- package/package.json +1 -1
- package/types/wuic-framework-lib.d.ts +1271 -646
|
@@ -7,7 +7,7 @@ import * as i8 from '@ngx-translate/core';
|
|
|
7
7
|
import { TranslateModule } from '@ngx-translate/core';
|
|
8
8
|
import * as i7 from '@syncfusion/ej2-angular-spreadsheet';
|
|
9
9
|
import { SpreadsheetModule } from '@syncfusion/ej2-angular-spreadsheet';
|
|
10
|
-
import { W as WtoolboxService, M as MetadataProviderService, U as UserInfoService, b as DataSourceComponent, I as ImportExportButtonComponent } from './wuic-framework-lib-wuic-framework-lib-
|
|
10
|
+
import { W as WtoolboxService, M as MetadataProviderService, U as UserInfoService, b as DataSourceComponent, I as ImportExportButtonComponent } from './wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs';
|
|
11
11
|
import { registerLicense } from '@syncfusion/ej2-base';
|
|
12
12
|
import * as i1 from '@angular/platform-browser';
|
|
13
13
|
import * as i1$1 from '@angular/router';
|
|
@@ -360,6 +360,78 @@ class SpreadsheetListSfComponent {
|
|
|
360
360
|
datasource;
|
|
361
361
|
hardcodedDatasource;
|
|
362
362
|
hideToolbar = false;
|
|
363
|
+
// ============================================================
|
|
364
|
+
// Lock-awareness API (opt-in, additive — non rompe API legacy)
|
|
365
|
+
// ------------------------------------------------------------
|
|
366
|
+
// GENERIC + METADATA-DRIVEN (no app-specific naming hardcoded).
|
|
367
|
+
//
|
|
368
|
+
// Configurazione PER ROUTE in `_metadati__tabelle.md_props_bag`:
|
|
369
|
+
// {
|
|
370
|
+
// "archetypes": {
|
|
371
|
+
// "spreadsheet": {
|
|
372
|
+
// "lock": {
|
|
373
|
+
// "endpointBase": "/api/spreadsheet",
|
|
374
|
+
// "primaryScopeField": "program_id", // field usato come URL path segment
|
|
375
|
+
// "requiredScopeFields": ["program_id"], // field obbligatori per acquire-lock
|
|
376
|
+
// "autoEnableOnScope": true, // se tutti i required presenti → auto-acquire
|
|
377
|
+
// "heartbeatIntervalSec": 60 // override (default 60)
|
|
378
|
+
// },
|
|
379
|
+
// "formulas": { ... }
|
|
380
|
+
// }
|
|
381
|
+
// }
|
|
382
|
+
// }
|
|
383
|
+
//
|
|
384
|
+
// Lo `lockScope` e' un dictionary generico chiave→valore (es. `{program_id:1, year:2026}`),
|
|
385
|
+
// tipicamente popolato dalla lazy-wrapper leggendo i query param della URL.
|
|
386
|
+
// Il component:
|
|
387
|
+
// - In ngAfterViewInit (o post-dataBound se config in mdpropsbag) chiama
|
|
388
|
+
// POST {endpointBase}/lock-range/{lockScope[primaryScopeField]} con body = scope.
|
|
389
|
+
// - Avvia heartbeat timer (default 60s) verso {endpointBase}/heartbeat.
|
|
390
|
+
// - Espone metodi pubblici saveCellsBatch() + releaseLock() + acquireLock().
|
|
391
|
+
// - In ngOnDestroy chiama POST {endpointBase}/release-lock con lockToken.
|
|
392
|
+
//
|
|
393
|
+
// Vedi `SpreadsheetController.cs` di una app concreta (es. CostCnh) per il
|
|
394
|
+
// contratto endpoint. La forma della request: path /:primaryScope, body = scope.
|
|
395
|
+
// ============================================================
|
|
396
|
+
/**
|
|
397
|
+
* Forza attivazione lock-awareness ANCHE se `mdpropsbag.archetypes.spreadsheet.lock.autoEnableOnScope`
|
|
398
|
+
* e' false o assente. Default: false (la decisione viene presa dalla mdpropsbag).
|
|
399
|
+
* Util per scenari programmatici dove un parent override-a la config.
|
|
400
|
+
*/
|
|
401
|
+
enableLockAwareness = false;
|
|
402
|
+
/**
|
|
403
|
+
* Scope di lock generico (record key→value). Tipicamente popolato dalla
|
|
404
|
+
* lazy-wrapper a partire dai query param della URL (es. `?program_id=1&year=2026`),
|
|
405
|
+
* oppure passato esplicitamente da un parent. La mdpropsbag dichiara quale
|
|
406
|
+
* field e' il "primary" (usato come path segment) e quali sono required.
|
|
407
|
+
*/
|
|
408
|
+
lockScope = {};
|
|
409
|
+
/**
|
|
410
|
+
* Override `heartbeatIntervalSec` se settato (>0). Altrimenti viene letto da
|
|
411
|
+
* `mdpropsbag.archetypes.spreadsheet.lock.heartbeatIntervalSec` (default 60).
|
|
412
|
+
*/
|
|
413
|
+
heartbeatIntervalSec;
|
|
414
|
+
// ============================================================
|
|
415
|
+
// Formula / Ribbon UX (additive, default-off per backward compat)
|
|
416
|
+
// ------------------------------------------------------------
|
|
417
|
+
// Syncfusion EJ2 Spreadsheet supporta 350+ funzioni (SUM, AVG, IF, VLOOKUP,
|
|
418
|
+
// INDEX/MATCH, dates, math, financial, logical, lookup) e references inter-cell
|
|
419
|
+
// (=A1+B1, =SUM(C5:C10)). Per attivarle:
|
|
420
|
+
// - [enableCalculation]="true" → engine formula attivo (default era già true Syncfusion)
|
|
421
|
+
// - [showFormulaBar]="true" → barra formula visibile (modifica formula da UI)
|
|
422
|
+
// - [showRibbon]="true" → ribbon completo (funzioni, format, charts)
|
|
423
|
+
//
|
|
424
|
+
// Quando user inserisce '=A1+B1' in una cella:
|
|
425
|
+
// - Syncfusion CALCOLA il valore numerico per display
|
|
426
|
+
// - cellSave event riceve { value: computed, formula: '=A1+B1' }
|
|
427
|
+
// - Component invia entrambi al backend via saveCellsBatch (campo formulaText opzionale)
|
|
428
|
+
// - Backend persiste value in cp.facts.<field> + formula_text in cp.facts_formula (1:1)
|
|
429
|
+
// - Su reload, snapshot re-injection formula in cell → user vede formula in formula bar
|
|
430
|
+
// ============================================================
|
|
431
|
+
/** Mostra formula bar Syncfusion (default false per backward compat). NB: Syncfusion engine formule sempre attivo. */
|
|
432
|
+
showFormulaBar = false;
|
|
433
|
+
/** Mostra ribbon Syncfusion completo (default false). */
|
|
434
|
+
showRibbon = false;
|
|
363
435
|
/**
|
|
364
436
|
* Provider letto da `<wuic-import-export-button>` al click su Export XLS.
|
|
365
437
|
* Arrow function per preservare il binding di `this` quando passata come Input.
|
|
@@ -378,6 +450,14 @@ class SpreadsheetListSfComponent {
|
|
|
378
450
|
onSpreadsheetRowInserted = new EventEmitter();
|
|
379
451
|
onSpreadsheetRowsDeleted = new EventEmitter();
|
|
380
452
|
onSpreadsheetBatchSaved = new EventEmitter();
|
|
453
|
+
/** Emesso dopo successful acquire/refresh lock (vedi SpreadsheetController.lock-range). */
|
|
454
|
+
onLockAcquired = new EventEmitter();
|
|
455
|
+
/** Emesso se acquire-lock ritorna outcome='conflict' (altro utente sta editando). */
|
|
456
|
+
onLockConflict = new EventEmitter();
|
|
457
|
+
/** Emesso al successful release (ngOnDestroy o release esplicito). */
|
|
458
|
+
onLockReleased = new EventEmitter();
|
|
459
|
+
/** Emesso quando heartbeat fallisce (lock scaduto o released da altro). */
|
|
460
|
+
onLockExpired = new EventEmitter();
|
|
381
461
|
// ============================================================
|
|
382
462
|
// Public state (API PUBBLICA COMPATIBILE COL VECCHIO)
|
|
383
463
|
// ============================================================
|
|
@@ -485,6 +565,14 @@ class SpreadsheetListSfComponent {
|
|
|
485
565
|
* enableVirtualization: true è il default Syncfusion, lasciato per performance.
|
|
486
566
|
*/
|
|
487
567
|
sfScrollSettings = { isFinite: true, enableVirtualization: true };
|
|
568
|
+
// ─── Lock-awareness state (Sprint 9.4 framework integration) ─────────────
|
|
569
|
+
/** Token UUID lock corrente. Settato da acquireLock(), letto da save/heartbeat/release. */
|
|
570
|
+
lockToken;
|
|
571
|
+
/** Lock id (numerico) per audit join con cp.spreadsheet_change_log. */
|
|
572
|
+
lockId;
|
|
573
|
+
/** Lock expiry UTC. UI puo' mostrare countdown. */
|
|
574
|
+
lockExpiresUtc;
|
|
575
|
+
heartbeatTimer;
|
|
488
576
|
// ============================================================
|
|
489
577
|
// Constructor
|
|
490
578
|
// ============================================================
|
|
@@ -533,6 +621,13 @@ class SpreadsheetListSfComponent {
|
|
|
533
621
|
}
|
|
534
622
|
});
|
|
535
623
|
}
|
|
624
|
+
// Sprint 9.4 — auto-acquire lock se opt-in @Input.
|
|
625
|
+
// Quando l'opt-in viene da `mdpropsbag.archetypes.spreadsheet.lock.autoEnableOnScope`,
|
|
626
|
+
// l'acquire viene rinviato a `tryAutoAcquireLockFromMetadata()` chiamato post-dataBound
|
|
627
|
+
// (quando metaInfo e' loaded dal datasource).
|
|
628
|
+
if (this.enableLockAwareness) {
|
|
629
|
+
void this.acquireLock();
|
|
630
|
+
}
|
|
536
631
|
}
|
|
537
632
|
ngOnDestroy() {
|
|
538
633
|
this.datasourceReadySubscription?.unsubscribe();
|
|
@@ -540,6 +635,15 @@ class SpreadsheetListSfComponent {
|
|
|
540
635
|
this.themeObserver?.disconnect();
|
|
541
636
|
this.stopColHeaderObserver();
|
|
542
637
|
this.clearPendingLayoutPassTimeouts();
|
|
638
|
+
// Sprint 9.4 — cleanup lock
|
|
639
|
+
if (this.heartbeatTimer) {
|
|
640
|
+
clearInterval(this.heartbeatTimer);
|
|
641
|
+
this.heartbeatTimer = undefined;
|
|
642
|
+
}
|
|
643
|
+
if (this.lockToken) {
|
|
644
|
+
// best-effort release (fire-and-forget — non bloccare destroy)
|
|
645
|
+
void this.releaseLock().catch(() => { });
|
|
646
|
+
}
|
|
543
647
|
try {
|
|
544
648
|
this.sfSpreadsheet?.destroy?.();
|
|
545
649
|
}
|
|
@@ -1487,6 +1591,11 @@ class SpreadsheetListSfComponent {
|
|
|
1487
1591
|
for (const d of delays) {
|
|
1488
1592
|
setTimeout(() => this.syncColumnHeadersWithMetadata(), d);
|
|
1489
1593
|
}
|
|
1594
|
+
// Apply metadata-driven formulas (from mdpropsbag.archetypes.spreadsheet.formulas)
|
|
1595
|
+
this.applyMetadataFormulas();
|
|
1596
|
+
// Auto-acquire lock se mdpropsbag.archetypes.spreadsheet.lock.autoEnableOnScope=true
|
|
1597
|
+
// e tutti i requiredScopeFields sono presenti in lockScope. Idempotente.
|
|
1598
|
+
this.tryAutoAcquireLockFromMetadata();
|
|
1490
1599
|
}
|
|
1491
1600
|
/**
|
|
1492
1601
|
* Sostituisce il testo delle celle nell'header column-letter nativo di
|
|
@@ -1605,10 +1714,13 @@ class SpreadsheetListSfComponent {
|
|
|
1605
1714
|
const distinctList = await this.computeDistinctValuesForPopup(col, fieldName);
|
|
1606
1715
|
// Selezione corrente: se esiste un filtro sulla colonna, quei valori
|
|
1607
1716
|
// sono pre-checked; altrimenti tutti checked.
|
|
1717
|
+
// selectedSet contiene `d.value` (raw FK ID per lookupByID, raw value altrimenti)
|
|
1718
|
+
// — NON `d.display` — cosi' il filter server-side puo' usarli direttamente
|
|
1719
|
+
// come predicati `WHERE col = <id>` (cross-DBMS safe).
|
|
1608
1720
|
const currentFilter = this.columnFilters.get(fieldName) || null;
|
|
1609
1721
|
const selectedSet = currentFilter
|
|
1610
1722
|
? new Set(currentFilter)
|
|
1611
|
-
: new Set(distinctList.map(d => d.
|
|
1723
|
+
: new Set(distinctList.map(d => d.value));
|
|
1612
1724
|
// ==== Costruisci DOM popup ====
|
|
1613
1725
|
const popup = document.createElement('div');
|
|
1614
1726
|
popup.className = 'wuic-sf-filter-popup';
|
|
@@ -1670,16 +1782,20 @@ class SpreadsheetListSfComponent {
|
|
|
1670
1782
|
return;
|
|
1671
1783
|
const label = document.createElement('label');
|
|
1672
1784
|
label.className = 'wuic-sf-filter-popup-item';
|
|
1673
|
-
|
|
1785
|
+
// data-value contiene il VALUE (FK ID per lookup) per leggibilita' DOM/testing.
|
|
1786
|
+
label.setAttribute('data-value', d.value);
|
|
1674
1787
|
const cb = document.createElement('input');
|
|
1675
1788
|
cb.type = 'checkbox';
|
|
1676
1789
|
cb.className = 'wuic-sf-filter-popup-cb';
|
|
1677
|
-
|
|
1790
|
+
// Memorizziamo `d.value` (raw FK ID) nel selectedSet, ma renderizziamo
|
|
1791
|
+
// `d.display` (descrittivo) nello span — cosi' UI = descrittivo e
|
|
1792
|
+
// filter value emesso al server = raw ID (cross-DBMS safe).
|
|
1793
|
+
cb.checked = selectedSet.has(d.value);
|
|
1678
1794
|
cb.addEventListener('change', () => {
|
|
1679
1795
|
if (cb.checked)
|
|
1680
|
-
selectedSet.add(d.
|
|
1796
|
+
selectedSet.add(d.value);
|
|
1681
1797
|
else
|
|
1682
|
-
selectedSet.delete(d.
|
|
1798
|
+
selectedSet.delete(d.value);
|
|
1683
1799
|
updateSelectAllState();
|
|
1684
1800
|
});
|
|
1685
1801
|
const span = document.createElement('span');
|
|
@@ -1698,7 +1814,7 @@ class SpreadsheetListSfComponent {
|
|
|
1698
1814
|
};
|
|
1699
1815
|
selectAllCb.addEventListener('change', () => {
|
|
1700
1816
|
if (selectAllCb.checked) {
|
|
1701
|
-
distinctList.forEach(d => selectedSet.add(d.
|
|
1817
|
+
distinctList.forEach(d => selectedSet.add(d.value));
|
|
1702
1818
|
}
|
|
1703
1819
|
else {
|
|
1704
1820
|
selectedSet.clear();
|
|
@@ -1798,23 +1914,33 @@ class SpreadsheetListSfComponent {
|
|
|
1798
1914
|
return this.fetchDistinctValuesFromServer(col, fieldName);
|
|
1799
1915
|
}
|
|
1800
1916
|
const baseRecords = this.records.filter(rec => this.recordMatchesColumnFilters(rec, fieldName));
|
|
1917
|
+
// Per colonne lookupByID il `value` deve essere il FK ID raw (rec[fieldName]),
|
|
1918
|
+
// mentre il `display` è il descrittivo risolto via lookup alias. Senza questa
|
|
1919
|
+
// separazione il filter server-side invierebbe descrittivi (stringa) contro
|
|
1920
|
+
// colonna FK numerica → 42601 PG / ORA-00904 Oracle.
|
|
1801
1921
|
const distinctMap = new Map();
|
|
1802
1922
|
for (const rec of baseRecords) {
|
|
1803
1923
|
const raw = rec?.[fieldName];
|
|
1804
1924
|
const display = this.formatValueForDisplay(raw, col, rec);
|
|
1805
|
-
const
|
|
1806
|
-
const
|
|
1925
|
+
const displayKey = display == null ? '' : String(display);
|
|
1926
|
+
const valueKey = raw == null ? '' : String(raw);
|
|
1927
|
+
const entry = distinctMap.get(valueKey);
|
|
1807
1928
|
if (entry)
|
|
1808
1929
|
entry.count++;
|
|
1809
1930
|
else
|
|
1810
|
-
distinctMap.set(
|
|
1931
|
+
distinctMap.set(valueKey, { display: displayKey, value: valueKey, count: 1 });
|
|
1811
1932
|
}
|
|
1812
1933
|
return this.sortDistinctList(distinctMap);
|
|
1813
1934
|
}
|
|
1814
1935
|
/**
|
|
1815
1936
|
* Chiama `MetaService.getDistinctValues(column_id, text, filter_type,
|
|
1816
1937
|
* max_results, user_id)` (endpoint AsmxProxy) e trasforma `rawPagedResult`
|
|
1817
|
-
* in `Array<{display, count}>` compatibile col popup.
|
|
1938
|
+
* in `Array<{display, value, count}>` compatibile col popup.
|
|
1939
|
+
*
|
|
1940
|
+
* Per lookupByID il server (PG provider — vedi metaQueryPostgreSql.cs distinct
|
|
1941
|
+
* branch) emette ANCHE l'alias `<entity>___<dataTextField>__<colName>` con il
|
|
1942
|
+
* descrittivo. `formatValueForDisplay` lo legge da `row` e ritorna la stringa
|
|
1943
|
+
* human-readable. Il filter usa invece il FK ID raw (`row[fieldName]`).
|
|
1818
1944
|
*
|
|
1819
1945
|
* In caso di errore ritorna array vuoto (il popup mostrerà nessuna opzione
|
|
1820
1946
|
* — un notification error è comunque inviata).
|
|
@@ -1847,13 +1973,16 @@ class SpreadsheetListSfComponent {
|
|
|
1847
1973
|
// FastExpando conserva il case originale.
|
|
1848
1974
|
const raw = row?.[fieldName] ?? row?.[fieldName?.toLowerCase?.()]
|
|
1849
1975
|
?? row?.[fieldName?.toUpperCase?.()] ?? Object.values(row || {})[0];
|
|
1976
|
+
// display: descrittivo se lookup (via alias in row), altrimenti formatted raw.
|
|
1850
1977
|
const display = this.formatValueForDisplay(raw, col, row);
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1978
|
+
const displayKey = display == null ? '' : String(display);
|
|
1979
|
+
// value: FK ID raw (sempre, anche per lookup). Usato come filter value.
|
|
1980
|
+
const valueKey = raw == null ? '' : String(raw);
|
|
1981
|
+
const entry = distinctMap.get(valueKey);
|
|
1853
1982
|
if (entry)
|
|
1854
1983
|
entry.count++;
|
|
1855
1984
|
else
|
|
1856
|
-
distinctMap.set(
|
|
1985
|
+
distinctMap.set(valueKey, { display: displayKey, value: valueKey, count: 1 });
|
|
1857
1986
|
}
|
|
1858
1987
|
return this.sortDistinctList(distinctMap);
|
|
1859
1988
|
}
|
|
@@ -1867,7 +1996,8 @@ class SpreadsheetListSfComponent {
|
|
|
1867
1996
|
return [];
|
|
1868
1997
|
}
|
|
1869
1998
|
}
|
|
1870
|
-
/** Ordinamento comune della lista distinct (numerico se possibile, poi locale).
|
|
1999
|
+
/** Ordinamento comune della lista distinct (numerico se possibile, poi locale).
|
|
2000
|
+
* Ordina per `display` (descrittivo) cosi' il popup e' navigabile dall'utente. */
|
|
1871
2001
|
sortDistinctList(distinctMap) {
|
|
1872
2002
|
return Array.from(distinctMap.values()).sort((a, b) => {
|
|
1873
2003
|
const na = Number(a.display);
|
|
@@ -1897,15 +2027,14 @@ class SpreadsheetListSfComponent {
|
|
|
1897
2027
|
recordMatchesColumnFilters(rec, excludeField) {
|
|
1898
2028
|
if (this.columnFilters.size === 0)
|
|
1899
2029
|
return true;
|
|
1900
|
-
const cols = this.metaInfo?.columnMetadata || [];
|
|
1901
2030
|
for (const [field, selSet] of this.columnFilters.entries()) {
|
|
1902
2031
|
if (excludeField && field === excludeField)
|
|
1903
2032
|
continue;
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
const
|
|
1908
|
-
const key =
|
|
2033
|
+
// selSet contiene raw values (FK ID per lookupByID, raw altrimenti) — vedi
|
|
2034
|
+
// computeDistinctValuesForPopup. Match contro raw rec[field], NON contro
|
|
2035
|
+
// display formattato (per parity con server-side WHERE col = <raw>).
|
|
2036
|
+
const raw = rec?.[field];
|
|
2037
|
+
const key = raw == null ? '' : String(raw);
|
|
1909
2038
|
if (!selSet.has(key))
|
|
1910
2039
|
return false;
|
|
1911
2040
|
}
|
|
@@ -3845,8 +3974,320 @@ class SpreadsheetListSfComponent {
|
|
|
3845
3974
|
onWindowResize() {
|
|
3846
3975
|
this.scheduleLayoutPass();
|
|
3847
3976
|
}
|
|
3977
|
+
// ============================================================
|
|
3978
|
+
// Metadata-driven formulas (Sprint 9.4)
|
|
3979
|
+
// ------------------------------------------------------------
|
|
3980
|
+
// Le formule sono definite dichiarativamente in metadata route:
|
|
3981
|
+
// _metadati__tabelle.mdpropsbag → archetypes.spreadsheet.formulas
|
|
3982
|
+
// Shape:
|
|
3983
|
+
// {
|
|
3984
|
+
// "<target_column_name>": "=<expression with {col_name} tokens>",
|
|
3985
|
+
// "balance": "={planned}-{actual}",
|
|
3986
|
+
// "variance_pct": "=IF({planned}=0,0,({actual}/{planned})*100)",
|
|
3987
|
+
// "remaining": "={planned}-{actual}-{committed}"
|
|
3988
|
+
// }
|
|
3989
|
+
// Pattern:
|
|
3990
|
+
// - {col_name} viene sostituito a runtime con l'indirizzo cella Syncfusion
|
|
3991
|
+
// della colonna corrispondente sulla riga corrente (es. row 2 → C2, D2)
|
|
3992
|
+
// - L'engine Syncfusion (enableCalculation=true) ricalcola i valori automaticamente
|
|
3993
|
+
// - Quando user modifica una cella source, le celle dipendenti si aggiornano
|
|
3994
|
+
// - On save (cellSave event), il VALUE COMPUTED viene persistito su DB,
|
|
3995
|
+
// la formula non viene memorizzata (resta nei metadati, ri-applicata al refresh)
|
|
3996
|
+
// ============================================================
|
|
3997
|
+
/**
|
|
3998
|
+
* Risolve la lista colonne visibili (idx → col name) dal metaInfo corrente.
|
|
3999
|
+
* Indice 0-based; il caller mappa a column letter A/B/C tramite indexToColumnLetter().
|
|
4000
|
+
*/
|
|
4001
|
+
getVisibleColumnNames() {
|
|
4002
|
+
if (!this.metaInfo?.columnMetadata)
|
|
4003
|
+
return [];
|
|
4004
|
+
return this.metaInfo.columnMetadata
|
|
4005
|
+
.filter(c => !c.mc_hide_in_edit)
|
|
4006
|
+
.map(c => c.mc_nome_colonna || '');
|
|
4007
|
+
}
|
|
4008
|
+
/** Converte indice 0-based in letter A1-notation (0 → 'A', 25 → 'Z', 26 → 'AA'). */
|
|
4009
|
+
indexToColumnLetter(idx) {
|
|
4010
|
+
let s = '';
|
|
4011
|
+
let n = idx;
|
|
4012
|
+
while (n >= 0) {
|
|
4013
|
+
s = String.fromCharCode(65 + (n % 26)) + s;
|
|
4014
|
+
n = Math.floor(n / 26) - 1;
|
|
4015
|
+
}
|
|
4016
|
+
return s;
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* Risolve il blocco formulas da `metaInfo.tableMetadata.md_props_bag.archetypes.spreadsheet.formulas`.
|
|
4020
|
+
* Ritorna `{}` se non configurato.
|
|
4021
|
+
*/
|
|
4022
|
+
getMetadataFormulas() {
|
|
4023
|
+
const tm = this.metaInfo?.tableMetadata;
|
|
4024
|
+
if (!tm)
|
|
4025
|
+
return {};
|
|
4026
|
+
const pb = tm.md_props_bag || tm.mdpropsbag || tm.extraProps?.md_props_bag;
|
|
4027
|
+
const arch = pb?.archetypes?.spreadsheet;
|
|
4028
|
+
const formulas = arch?.formulas;
|
|
4029
|
+
if (!formulas || typeof formulas !== 'object')
|
|
4030
|
+
return {};
|
|
4031
|
+
return formulas;
|
|
4032
|
+
}
|
|
4033
|
+
/**
|
|
4034
|
+
* Applica le formule metadata-driven a tutte le righe correntemente caricate.
|
|
4035
|
+
* Chiamato post-dataBound. Usa Syncfusion `updateCell({formula: '=...'}, address)`.
|
|
4036
|
+
* Idempotente: ri-chiamabile (sovrascrive la formula con la stessa).
|
|
4037
|
+
*/
|
|
4038
|
+
applyMetadataFormulas() {
|
|
4039
|
+
const formulas = this.getMetadataFormulas();
|
|
4040
|
+
if (!formulas || Object.keys(formulas).length === 0)
|
|
4041
|
+
return;
|
|
4042
|
+
if (!this.sfSpreadsheet)
|
|
4043
|
+
return;
|
|
4044
|
+
const visibleCols = this.getVisibleColumnNames();
|
|
4045
|
+
if (visibleCols.length === 0)
|
|
4046
|
+
return;
|
|
4047
|
+
// Map column name → A1 letter
|
|
4048
|
+
const colIndex = {};
|
|
4049
|
+
visibleCols.forEach((name, idx) => { colIndex[name] = this.indexToColumnLetter(idx); });
|
|
4050
|
+
const rowOffset = 2; // Syncfusion riga 1 = header (showFieldAsHeader), dati partono da riga 2
|
|
4051
|
+
const rowCount = (this.pagedRecords?.length || this.records?.length || 0);
|
|
4052
|
+
for (const [targetCol, formulaTemplate] of Object.entries(formulas)) {
|
|
4053
|
+
const targetLetter = colIndex[targetCol];
|
|
4054
|
+
if (!targetLetter) {
|
|
4055
|
+
console.warn(`[wuic-spreadsheet] formula target column '${targetCol}' not visible — skipped`);
|
|
4056
|
+
continue;
|
|
4057
|
+
}
|
|
4058
|
+
for (let r = 0; r < rowCount; r++) {
|
|
4059
|
+
const sheetRow = rowOffset + r;
|
|
4060
|
+
// Sostituisci tutti i token {colName} con <colLetter><rowNumber>
|
|
4061
|
+
const resolved = formulaTemplate.replace(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g, (_m, colName) => {
|
|
4062
|
+
const letter = colIndex[colName];
|
|
4063
|
+
if (!letter) {
|
|
4064
|
+
console.warn(`[wuic-spreadsheet] formula token {${colName}} → column not visible, leaving as-is`);
|
|
4065
|
+
return `{${colName}}`;
|
|
4066
|
+
}
|
|
4067
|
+
return `${letter}${sheetRow}`;
|
|
4068
|
+
});
|
|
4069
|
+
const address = `${targetLetter}${sheetRow}`;
|
|
4070
|
+
try {
|
|
4071
|
+
this.sfSpreadsheet.updateCell?.({ formula: resolved }, address);
|
|
4072
|
+
}
|
|
4073
|
+
catch (e) {
|
|
4074
|
+
console.warn(`[wuic-spreadsheet] updateCell failed at ${address}:`, e);
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
// ============================================================
|
|
4080
|
+
// Lock-awareness public API (Sprint 9.4 — generic, metadata-driven)
|
|
4081
|
+
// ------------------------------------------------------------
|
|
4082
|
+
// Tutta la config (endpoint base, primaryScopeField, required fields,
|
|
4083
|
+
// heartbeat interval, auto-enable) viene letta da
|
|
4084
|
+
// `metaInfo.tableMetadata.md_props_bag.archetypes.spreadsheet.lock`.
|
|
4085
|
+
// L'@Input `lockScope` (Record<string,any>) e' il dictionary generico
|
|
4086
|
+
// dei valori di lock (es. `{program_id:1, year:2026, scenario_id:7}`),
|
|
4087
|
+
// passato dalla lazy-wrapper a partire dai query param della URL.
|
|
4088
|
+
// ============================================================
|
|
4089
|
+
/**
|
|
4090
|
+
* Estrae la config lock da `mdpropsbag.archetypes.spreadsheet.lock`.
|
|
4091
|
+
* Defaults conservativi se la config manca.
|
|
4092
|
+
*/
|
|
4093
|
+
getLockConfig() {
|
|
4094
|
+
const pb = this.metaInfo?.tableMetadata?.md_props_bag
|
|
4095
|
+
|| this.metaInfo?.tableMetadata?.mdpropsbag
|
|
4096
|
+
|| {};
|
|
4097
|
+
const arch = pb?.archetypes?.spreadsheet?.lock || {};
|
|
4098
|
+
return {
|
|
4099
|
+
endpointBase: typeof arch.endpointBase === 'string' && arch.endpointBase.length > 0
|
|
4100
|
+
? arch.endpointBase
|
|
4101
|
+
: '/api/spreadsheet',
|
|
4102
|
+
primaryScopeField: typeof arch.primaryScopeField === 'string' && arch.primaryScopeField.length > 0
|
|
4103
|
+
? arch.primaryScopeField
|
|
4104
|
+
: null,
|
|
4105
|
+
requiredScopeFields: Array.isArray(arch.requiredScopeFields)
|
|
4106
|
+
? arch.requiredScopeFields.filter((s) => typeof s === 'string')
|
|
4107
|
+
: (arch.primaryScopeField ? [arch.primaryScopeField] : []),
|
|
4108
|
+
autoEnableOnScope: arch.autoEnableOnScope === true,
|
|
4109
|
+
heartbeatIntervalSec: typeof this.heartbeatIntervalSec === 'number' && this.heartbeatIntervalSec > 0
|
|
4110
|
+
? this.heartbeatIntervalSec
|
|
4111
|
+
: (typeof arch.heartbeatIntervalSec === 'number' && arch.heartbeatIntervalSec > 0
|
|
4112
|
+
? arch.heartbeatIntervalSec
|
|
4113
|
+
: 60),
|
|
4114
|
+
};
|
|
4115
|
+
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Valida che lo scope contenga tutti i required fields.
|
|
4118
|
+
* Ritorna lista dei missing (vuota = scope valido).
|
|
4119
|
+
*/
|
|
4120
|
+
validateLockScope(cfg) {
|
|
4121
|
+
return cfg.requiredScopeFields.filter((f) => this.lockScope?.[f] == null);
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Trigger auto-acquire post-dataBound (quando metaInfo e' loaded). Idempotente:
|
|
4125
|
+
* non riacquista se gia' c'e' un lockToken attivo. Chiamabile manualmente.
|
|
4126
|
+
* Condizioni per auto-acquire:
|
|
4127
|
+
* - lockToken non gia' attivo
|
|
4128
|
+
* - mdpropsbag.archetypes.spreadsheet.lock.autoEnableOnScope === true
|
|
4129
|
+
* - tutti i requiredScopeFields presenti in lockScope
|
|
4130
|
+
*/
|
|
4131
|
+
tryAutoAcquireLockFromMetadata() {
|
|
4132
|
+
if (this.lockToken)
|
|
4133
|
+
return;
|
|
4134
|
+
const cfg = this.getLockConfig();
|
|
4135
|
+
if (!cfg.autoEnableOnScope)
|
|
4136
|
+
return;
|
|
4137
|
+
const missing = this.validateLockScope(cfg);
|
|
4138
|
+
if (missing.length > 0) {
|
|
4139
|
+
console.info(`[wuic-spreadsheet] auto-acquire skipped — missing scope: ${missing.join(', ')}`);
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
4142
|
+
this.enableLockAwareness = true;
|
|
4143
|
+
void this.acquireLock();
|
|
4144
|
+
}
|
|
4145
|
+
/**
|
|
4146
|
+
* Acquisisce lock pessimistic su `lockScope`. Endpoint:
|
|
4147
|
+
* POST {endpointBase}/lock-range/{lockScope[primaryScopeField]} body = lockScope
|
|
4148
|
+
* Emette onLockAcquired (acquired|refreshed) o onLockConflict.
|
|
4149
|
+
* Auto-avvia heartbeat timer se outcome=acquired|refreshed.
|
|
4150
|
+
*/
|
|
4151
|
+
async acquireLock() {
|
|
4152
|
+
const cfg = this.getLockConfig();
|
|
4153
|
+
if (!cfg.primaryScopeField) {
|
|
4154
|
+
console.warn('[wuic-spreadsheet] acquireLock skipped: primaryScopeField non configurato in mdpropsbag.archetypes.spreadsheet.lock');
|
|
4155
|
+
return { outcome: 'skipped' };
|
|
4156
|
+
}
|
|
4157
|
+
const primaryVal = this.lockScope?.[cfg.primaryScopeField];
|
|
4158
|
+
if (primaryVal == null) {
|
|
4159
|
+
console.warn(`[wuic-spreadsheet] acquireLock skipped: lockScope[${cfg.primaryScopeField}] non valorizzato`);
|
|
4160
|
+
return { outcome: 'skipped' };
|
|
4161
|
+
}
|
|
4162
|
+
const missing = this.validateLockScope(cfg);
|
|
4163
|
+
if (missing.length > 0) {
|
|
4164
|
+
console.warn(`[wuic-spreadsheet] acquireLock skipped: missing required scope fields: ${missing.join(', ')}`);
|
|
4165
|
+
return { outcome: 'skipped' };
|
|
4166
|
+
}
|
|
4167
|
+
const url = `${cfg.endpointBase}/lock-range/${encodeURIComponent(String(primaryVal))}`;
|
|
4168
|
+
// Body = lockScope MENO il primaryScopeField (gia' in URL path)
|
|
4169
|
+
const body = {};
|
|
4170
|
+
for (const [k, v] of Object.entries(this.lockScope || {})) {
|
|
4171
|
+
if (k === cfg.primaryScopeField)
|
|
4172
|
+
continue;
|
|
4173
|
+
if (v == null)
|
|
4174
|
+
continue;
|
|
4175
|
+
body[k] = v;
|
|
4176
|
+
}
|
|
4177
|
+
try {
|
|
4178
|
+
const resp = await this.http.post(url, body, { withCredentials: true }).toPromise();
|
|
4179
|
+
if (!resp?.ok) {
|
|
4180
|
+
if (resp?.outcome === 'conflict') {
|
|
4181
|
+
this.onLockConflict.emit({
|
|
4182
|
+
conflictUserId: resp.conflictUserId ?? null,
|
|
4183
|
+
lockExpiresUtc: resp.lockExpiresUtc ? new Date(resp.lockExpiresUtc) : null,
|
|
4184
|
+
});
|
|
4185
|
+
return { outcome: 'conflict', conflictUserId: resp.conflictUserId };
|
|
4186
|
+
}
|
|
4187
|
+
return { outcome: resp?.outcome ?? 'failed' };
|
|
4188
|
+
}
|
|
4189
|
+
this.lockToken = resp.lockToken;
|
|
4190
|
+
this.lockId = resp.lockId;
|
|
4191
|
+
this.lockExpiresUtc = new Date(resp.lockExpiresUtc);
|
|
4192
|
+
this.onLockAcquired.emit({
|
|
4193
|
+
lockToken: resp.lockToken,
|
|
4194
|
+
lockId: resp.lockId,
|
|
4195
|
+
lockExpiresUtc: this.lockExpiresUtc,
|
|
4196
|
+
outcome: resp.outcome,
|
|
4197
|
+
});
|
|
4198
|
+
this.startHeartbeat(cfg.heartbeatIntervalSec);
|
|
4199
|
+
return { outcome: resp.outcome, lockToken: resp.lockToken };
|
|
4200
|
+
}
|
|
4201
|
+
catch (e) {
|
|
4202
|
+
console.error('[wuic-spreadsheet] acquireLock failed', e);
|
|
4203
|
+
return { outcome: 'error' };
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
/**
|
|
4207
|
+
* Single heartbeat (extends TTL). Auto-called dal timer ogni heartbeatIntervalSec.
|
|
4208
|
+
* Emette onLockExpired se backend ritorna 410 Gone.
|
|
4209
|
+
*/
|
|
4210
|
+
async heartbeat() {
|
|
4211
|
+
if (!this.lockToken)
|
|
4212
|
+
return false;
|
|
4213
|
+
const cfg = this.getLockConfig();
|
|
4214
|
+
try {
|
|
4215
|
+
const resp = await this.http.post(`${cfg.endpointBase}/heartbeat`, { lockToken: this.lockToken }, { withCredentials: true }).toPromise();
|
|
4216
|
+
if (resp?.ok) {
|
|
4217
|
+
this.lockExpiresUtc = new Date(resp.lockExpiresUtc);
|
|
4218
|
+
return true;
|
|
4219
|
+
}
|
|
4220
|
+
this.onLockExpired.emit();
|
|
4221
|
+
this.stopHeartbeat();
|
|
4222
|
+
return false;
|
|
4223
|
+
}
|
|
4224
|
+
catch (e) {
|
|
4225
|
+
if (e?.status === 410) {
|
|
4226
|
+
this.onLockExpired.emit();
|
|
4227
|
+
this.stopHeartbeat();
|
|
4228
|
+
}
|
|
4229
|
+
console.warn('[wuic-spreadsheet] heartbeat failed', e?.status ?? e);
|
|
4230
|
+
return false;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
/**
|
|
4234
|
+
* Batch save delle modifiche (alternativa al CRUD-per-row standard).
|
|
4235
|
+
* Useful per applicare copy/paste range o bulk edit con singolo round-trip.
|
|
4236
|
+
* @param changes array di { factsId, field, newValue }
|
|
4237
|
+
*/
|
|
4238
|
+
async saveCellsBatch(changes) {
|
|
4239
|
+
if (!this.lockToken) {
|
|
4240
|
+
console.warn('[wuic-spreadsheet] saveCellsBatch: nessun lockToken — call acquireLock() first');
|
|
4241
|
+
return null;
|
|
4242
|
+
}
|
|
4243
|
+
if (!changes || changes.length === 0)
|
|
4244
|
+
return { applied: 0, failed: 0 };
|
|
4245
|
+
const cfg = this.getLockConfig();
|
|
4246
|
+
try {
|
|
4247
|
+
const resp = await this.http.post(`${cfg.endpointBase}/save-cells`, { lockToken: this.lockToken, changes }, { withCredentials: true }).toPromise();
|
|
4248
|
+
return { applied: resp?.applied ?? 0, failed: resp?.failed ?? 0 };
|
|
4249
|
+
}
|
|
4250
|
+
catch (e) {
|
|
4251
|
+
console.error('[wuic-spreadsheet] saveCellsBatch failed', e);
|
|
4252
|
+
return null;
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* Rilascia lock esplicitamente. Idempotente. Stop heartbeat timer.
|
|
4257
|
+
* Auto-chiamato in ngOnDestroy.
|
|
4258
|
+
*/
|
|
4259
|
+
async releaseLock() {
|
|
4260
|
+
if (!this.lockToken)
|
|
4261
|
+
return 0;
|
|
4262
|
+
this.stopHeartbeat();
|
|
4263
|
+
const tok = this.lockToken;
|
|
4264
|
+
this.lockToken = undefined;
|
|
4265
|
+
this.lockExpiresUtc = undefined;
|
|
4266
|
+
const cfg = this.getLockConfig();
|
|
4267
|
+
try {
|
|
4268
|
+
const resp = await this.http.post(`${cfg.endpointBase}/release-lock`, { lockToken: tok }, { withCredentials: true }).toPromise();
|
|
4269
|
+
const released = resp?.released ?? 0;
|
|
4270
|
+
this.onLockReleased.emit({ released });
|
|
4271
|
+
return released;
|
|
4272
|
+
}
|
|
4273
|
+
catch (e) {
|
|
4274
|
+
console.warn('[wuic-spreadsheet] releaseLock failed', e);
|
|
4275
|
+
return 0;
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
startHeartbeat(intervalSec = 60) {
|
|
4279
|
+
this.stopHeartbeat();
|
|
4280
|
+
const intervalMs = Math.max(15, intervalSec) * 1000;
|
|
4281
|
+
this.heartbeatTimer = setInterval(() => { void this.heartbeat(); }, intervalMs);
|
|
4282
|
+
}
|
|
4283
|
+
stopHeartbeat() {
|
|
4284
|
+
if (this.heartbeatTimer) {
|
|
4285
|
+
clearInterval(this.heartbeatTimer);
|
|
4286
|
+
this.heartbeatTimer = undefined;
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
3848
4289
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpreadsheetListSfComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.Title }, { token: i1$1.ActivatedRoute }, { token: i1$2.HttpClient }, { token: WtoolboxService }, { token: MetadataProviderService }, { token: UserInfoService }], target: i0.ɵɵFactoryTarget.Component });
|
|
3849
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SpreadsheetListSfComponent, isStandalone: true, selector: "wuic-spreadsheet-list-sf", inputs: { hardcodedRoute: "hardcodedRoute", parentRecord: "parentRecord", parentMetaInfo: "parentMetaInfo", datasource: "datasource", hardcodedDatasource: "hardcodedDatasource", hideToolbar: "hideToolbar" }, outputs: { onSpreadsheetDataBound: "onSpreadsheetDataBound", onSpreadsheetBeforePageChange: "onSpreadsheetBeforePageChange", onSpreadsheetPageChange: "onSpreadsheetPageChange", onSpreadsheetRowInserted: "onSpreadsheetRowInserted", onSpreadsheetRowsDeleted: "onSpreadsheetRowsDeleted", onSpreadsheetBatchSaved: "onSpreadsheetBatchSaved" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "sfSpreadsheet", first: true, predicate: ["sfSpreadsheet"], descendants: true }, { propertyName: "lookupSource", first: true, predicate: ["lookupSource"], descendants: true }], ngImport: i0, template: "<!--\n Layout root wrapper:\n 1. Toolbar WUIC custom (insert / save batch / export / refresh)\n 2. Syncfusion Spreadsheet host\n 3. Pagination custom (Syncfusion non ha pagination built-in, quindi la\n rendiamo sotto il foglio con nav + page-size dropdown)\n 4. Lookup datasource (invisibile, pattern identico al vecchio componente)\n-->\n\n<!-- Bootstrap CSS Syncfusion (~500 KiB): si monta solo quando questo\n componente lazy \u00E8 istanziato \u2192 CSS NON nel bundle iniziale globale,\n risparmia ~500 KiB su tutte le pagine che non aprono spreadsheet. -->\n<wuic-syncfusion-styles-bootstrap></wuic-syncfusion-styles-bootstrap>\n\n<div class=\"wuic-sf-spreadsheet-wrapper\">\n\n @if (!hideToolbar && metaInfo) {\n <!-- Stile allineato a <wuic-list-grid>: stessa caption-bar, stessi p-button\n PrimeNG con icona + label, stesso componente import/export. -->\n <div class=\"grid-caption-bar wuic-sf-caption-bar\" role=\"toolbar\" aria-label=\"Spreadsheet toolbar\">\n <div class=\"grid-caption-left\">\n @if (!metaInfo.tableMetadata.md_hide_refresh) {\n <p-button icon=\"pi pi-refresh\" label=\"{{'refresh_grid' | translate}}\"\n (onClick)=\"onToolbarRefresh()\" />\n }\n @if (canInsertRows()) {\n <p-button icon=\"pi pi-plus\" label=\"{{'add_grid' | translate}}\"\n (onClick)=\"onToolbarInsert()\" />\n }\n @if (!metaInfo.tableMetadata.md_hide_export_xls) {\n <!-- Include dialog con progress bar + bottoni \"Stoppa e salva\n parziale\" / \"Stoppa\" / \"Continua in background\". -->\n <wuic-import-export-button\n [routeName]=\"metaInfo.tableMetadata.md_route_name\"\n [tableMetadata]=\"metaInfo.tableMetadata\"\n [filterInfoProvider]=\"getCurrentFilterInfoForExport\"></wuic-import-export-button>\n }\n @if (isBatchSaveEnabled()) {\n <p-button icon=\"pi pi-save\" label=\"{{'list_grid.save_changes' | translate}}\"\n (onClick)=\"onToolbarSaveBatch()\" />\n }\n </div>\n </div>\n }\n\n <div class=\"wuic-sf-spreadsheet-host\">\n @if (metaInfo && sfSheets.length > 0) {\n <ejs-spreadsheet #sfSpreadsheet\n [sheets]=\"sfSheets\"\n [height]=\"sfHeight\"\n [width]=\"sfWidth\"\n [scrollSettings]=\"sfScrollSettings\"\n [showRibbon]=\"false\"\n [showFormulaBar]=\"false\"\n [showSheetTabs]=\"false\"\n [allowEditing]=\"true\"\n [allowSorting]=\"true\"\n [allowFiltering]=\"false\"\n [allowResizing]=\"true\"\n [allowInsert]=\"canInsertRows()\"\n [allowDelete]=\"canDeleteRows()\"\n [enableContextMenu]=\"true\"\n [enableKeyboardNavigation]=\"true\"\n [enableClipboard]=\"true\"\n (created)=\"onSyncfusionCreated()\"\n (dataBound)=\"onSyncfusionDataBound()\"\n (cellSave)=\"onSyncfusionCellSave($event)\"\n (beforeCellSave)=\"onSyncfusionBeforeCellSave($event)\"\n (beforeCellRender)=\"onSyncfusionBeforeCellRender($event)\"\n (cellEdit)=\"onSyncfusionCellEdit($event)\"\n (contextMenuBeforeOpen)=\"onSyncfusionContextMenuBeforeOpen($event)\"\n (contextMenuItemSelect)=\"onSyncfusionContextMenuItemSelect($event)\"\n (actionBegin)=\"onSyncfusionActionBegin($event)\"\n (actionComplete)=\"onSyncfusionActionComplete($event)\"\n (dialogBeforeOpen)=\"onSyncfusionDialogBeforeOpen($event)\">\n </ejs-spreadsheet>\n }\n </div>\n\n @if (totalPages > 1 || totalRecords > pageSize) {\n <div class=\"wuic-sf-pagination\" role=\"navigation\" aria-label=\"Paginazione\">\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(1, pageSize) : setClientPage(1)\"\n title=\"Prima pagina\">\n <i class=\"pi pi-angle-double-left\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex - 1, pageSize) : setClientPage(pageIndex - 1)\"\n title=\"Pagina precedente\">\n <i class=\"pi pi-angle-left\"></i>\n </button>\n <span class=\"wuic-sf-page-info\">\n Pagina <strong>{{ pageIndex }}</strong> di <strong>{{ totalPages }}</strong>\n <span class=\"wuic-sf-page-total\">({{ totalRecords }} record)</span>\n </span>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex + 1, pageSize) : setClientPage(pageIndex + 1)\"\n title=\"Pagina successiva\">\n <i class=\"pi pi-angle-right\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(totalPages, pageSize) : setClientPage(totalPages)\"\n title=\"Ultima pagina\">\n <i class=\"pi pi-angle-double-right\"></i>\n </button>\n <select class=\"wuic-sf-page-size\" [value]=\"pageSize\"\n (change)=\"setPageSize($any($event.target).value)\"\n title=\"Record per pagina\">\n <option [value]=\"10\">10</option>\n <option [value]=\"25\">25</option>\n <option [value]=\"50\">50</option>\n <option [value]=\"100\">100</option>\n </select>\n </div>\n }\n\n</div>\n\n@if (!hardcodedRoute) {\n <wuic-data-source #lookupSource [autoload]=\"true\" [routeFromRouting]=\"false\"\n [route]=\"currentRoute\" [parentRecord]=\"currentRecord\"\n [parentMetaInfo]=\"metaInfo\"></wuic-data-source>\n}\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.wuic-sf-spreadsheet-wrapper{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;overflow:hidden}.wuic-sf-caption-bar{flex:0 0 auto;padding:5px}.grid-caption-left{display:flex;align-items:stretch;gap:.5rem;flex-wrap:nowrap;min-height:2.25rem;height:2.25rem}.wuic-sf-spreadsheet-host{flex:1 1 auto;min-height:0;width:100%;position:relative;overflow:visible}::ng-deep .e-dialog.e-popup-open,::ng-deep .e-excelfilter,::ng-deep .e-filter-popup{z-index:10010!important;min-width:240px}::ng-deep .e-excelfilter .e-footer-content,::ng-deep .e-filter-popup .e-footer-content{display:flex!important;justify-content:flex-end;gap:6px;padding:8px 12px!important}.wuic-sf-spreadsheet-host ejs-spreadsheet{display:block;width:100%;height:100%}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header .e-header-cell-div{font-weight:700!important;text-align:left!important;padding-left:6px!important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wuic-sf-pagination{display:flex;gap:6px;align-items:center;justify-content:flex-end;padding:6px 10px;background-color:var(--wuic-surface-a, #ffffff);border-top:1px solid var(--wuic-surface-border, #e5e7eb);flex:0 0 auto;font-size:13px}.wuic-sf-page-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;cursor:pointer;color:var(--wuic-text-color, #1f2937);transition:background-color .12s ease,border-color .12s ease}.wuic-sf-page-btn:hover:not(:disabled){background-color:var(--wuic-hover-bg, #f3f4f6);border-color:var(--wuic-primary-color, #6366f1)}.wuic-sf-page-btn:disabled{opacity:.35;cursor:not-allowed}.wuic-sf-page-info{margin:0 8px;white-space:nowrap;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-info strong{color:var(--wuic-text-color, #1f2937)}.wuic-sf-page-total{margin-left:6px;font-size:12px;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-size{margin-left:8px;height:30px;padding:0 8px;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;color:var(--wuic-text-color, #1f2937);cursor:pointer}:host-context(.theme-dark) .wuic-sf-pagination,:host-context([data-theme=\"dark\"]) .wuic-sf-pagination{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn,:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn,:host-context(.theme-dark) .wuic-sf-page-size,:host-context([data-theme=\"dark\"]) .wuic-sf-page-size{color:#f3f4f6;border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn:hover:not(:disabled),:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn:hover:not(:disabled){background-color:#374151}:host-context(.theme-dark) .wuic-sf-page-info,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info{color:#9ca3af}:host-context(.theme-dark) .wuic-sf-page-info strong,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info strong{color:#f3f4f6}:host-context(.theme-dark) .wuic-sf-page-total,:host-context([data-theme=\"dark\"]) .wuic-sf-page-total{color:#9ca3af}:host ::ng-deep .e-spreadsheet{font-family:var(--wuic-font-family, inherit)}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .e-header-cell.wuic-sf-colname-header{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep .e-spreadsheet .e-sheet-content .e-filter-btn{opacity:0!important;pointer-events:none!important;width:0!important;height:0!important;min-width:0!important;padding:0!important;margin:0!important;overflow:hidden!important;position:absolute!important;right:0;top:0}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header{position:relative!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn{position:absolute;right:2px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;background:transparent;border:none;border-radius:3px;cursor:pointer;color:#475569;flex-shrink:0;transition:background-color .12s,color .12s;z-index:2}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#e2e8f0;color:#1f2937}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg{display:block;pointer-events:none}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet svg,:host ::ng-deep .e-spreadsheet svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#2563eb}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-colname-header .e-header-cell-div{padding-right:22px!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn{color:#cbd5e1}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#334155;color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#60a5fa}:host ::ng-deep .e-spreadsheet .e-sheet-panel{background-color:var(--wuic-surface-a, #ffffff)}:host-context(.theme-dark) ::ng-deep .e-spreadsheet,:host-context([data-theme=\"dark\"]) ::ng-deep .e-spreadsheet,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-sheet-panel,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-main-panel{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-cell{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-row .e-header-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-row-header .e-header-cell{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell.row-danger{background-color:#7f1d1d!important;color:#fecaca!important}:host ::ng-deep .e-cell.row-danger{background-color:#fee2e2!important}:host ::ng-deep .e-cell.row-warning{background-color:#fef3c7!important}:host ::ng-deep .e-cell.row-success{background-color:#d1fae5!important}::ng-deep .wuic-sf-filter-popup{position:fixed;z-index:99999;min-width:260px;max-width:340px;background:#fff;color:#111827;border:1px solid #d1d5db;border-radius:6px;box-shadow:0 8px 24px #0000002e;font-family:inherit;font-size:13px;display:flex;flex-direction:column;overflow:hidden;-webkit-user-select:none;user-select:none}::ng-deep .wuic-sf-filter-popup-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#f3f4f6;border-bottom:1px solid #e5e7eb;font-weight:600}::ng-deep .wuic-sf-filter-popup-title{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-close{background:transparent;border:none;cursor:pointer;font-size:18px;line-height:1;padding:0 4px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-close:hover{color:#111827}::ng-deep .wuic-sf-filter-popup-sort{display:flex;flex-direction:column;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-sort-btn{background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#111827;display:flex;align-items:center;gap:6px}::ng-deep .wuic-sf-filter-popup-sort-btn:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-sort-icon{font-size:10px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-clear{border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-clear-btn{width:100%;background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#2563eb}::ng-deep .wuic-sf-filter-popup-clear-btn:hover:not(:disabled){background:#eff6ff}::ng-deep .wuic-sf-filter-popup-clear-btn:disabled{color:#9ca3af;cursor:not-allowed}::ng-deep .wuic-sf-filter-popup-search{padding:8px 12px;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-search-input{width:100%;padding:4px 8px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;outline:none;box-sizing:border-box}::ng-deep .wuic-sf-filter-popup-search-input:focus{border-color:#2563eb;box-shadow:0 0 0 2px #2563eb33}::ng-deep .wuic-sf-filter-popup-list-wrap{display:flex;flex-direction:column;max-height:260px;overflow:hidden}::ng-deep .wuic-sf-filter-popup-selectall{padding:4px 12px;border-bottom:1px solid #e5e7eb;background:#f9fafb;font-weight:500}::ng-deep .wuic-sf-filter-popup-list{flex:1 1 auto;overflow-y:auto;max-height:220px}::ng-deep .wuic-sf-filter-popup-item{display:flex;align-items:center;gap:6px;padding:4px 12px;cursor:pointer}::ng-deep .wuic-sf-filter-popup-item:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-item input[type=checkbox]{margin:0;flex:0 0 auto}::ng-deep .wuic-sf-filter-popup-item span{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-footer{display:flex;justify-content:flex-end;gap:8px;padding:8px 12px;border-top:1px solid #e5e7eb;background:#f9fafb}::ng-deep .wuic-sf-filter-popup-ok,::ng-deep .wuic-sf-filter-popup-cancel{padding:4px 14px;border:1px solid #d1d5db;border-radius:4px;background:#fff;cursor:pointer;font-size:13px}::ng-deep .wuic-sf-filter-popup-ok{background:#2563eb;color:#fff;border-color:#2563eb}::ng-deep .wuic-sf-filter-popup-ok:hover{background:#1d4ed8}::ng-deep .wuic-sf-filter-popup-cancel:hover{background:#f3f4f6}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active{color:#2563eb}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active svg{fill:#2563eb}::ng-deep .wuic-sf-filter-popup.wuic-sf-filter-popup--dark{background:#1f2937;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-header,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-footer,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-selectall{background:#111827;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-clear,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search{border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{color:#f3f4f6;background:transparent;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-item:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel:hover{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search-input{background:#111827;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close{color:#9ca3af}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close:hover{color:#f3f4f6}::ng-deep .wuic-sf-cell-editor{box-sizing:border-box;padding:2px 6px;border:2px solid #2563eb;background:#fff;color:#111827;font-family:inherit;font-size:13px;outline:none;box-shadow:0 2px 8px #00000026;border-radius:2px}::ng-deep .wuic-sf-cell-editor:focus{border-color:#1d4ed8;box-shadow:0 0 0 2px #2563eb40}::ng-deep select.wuic-sf-cell-editor{cursor:pointer}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark{background:#1f2937;color:#f3f4f6;border-color:#2563eb}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark option{background:#1f2937;color:#f3f4f6}\n"], dependencies: [{ kind: "component", type: DataSourceComponent, selector: "wuic-data-source", inputs: ["route", "routeFromRouting", "hardcodedRoute", "autoload", "loading", "changeTracking", "parentRecord", "parentMetaInfo", "parentDatasource", "useComboEndpoint", "comboFormulaLookup", "comboMcId", "comboField", "comboExtraFields", "comboCurrentRecord", "componentRef"] }, { kind: "ngmodule", type: SpreadsheetModule }, { kind: "component", type: i7.SpreadsheetComponent, selector: "ejs-spreadsheet", inputs: ["activeSheetIndex", "allowAutoFill", "allowCellFormatting", "allowChart", "allowConditionalFormat", "allowDataValidation", "allowDelete", "allowEditing", "allowFiltering", "allowFindAndReplace", "allowFreezePane", "allowHyperlink", "allowImage", "allowInsert", "allowMerge", "allowNumberFormatting", "allowOpen", "allowPrint", "allowResizing", "allowSave", "allowScrolling", "allowSorting", "allowUndoRedo", "allowWrap", "author", "autoFillSettings", "calculationMode", "cellStyle", "cssClass", "currencyCode", "definedNames", "enableClipboard", "enableContextMenu", "enableKeyboardNavigation", "enableKeyboardShortcut", "enableNotes", "enablePersistence", "enableRtl", "height", "isProtected", "listSeparator", "locale", "openSettings", "openUrl", "password", "saveUrl", "scrollSettings", "selectionSettings", "sheets", "showAggregate", "showCommentsPane", "showFormulaBar", "showRibbon", "showSheetTabs", "width"], outputs: ["actionBegin", "actionComplete", "afterHyperlinkClick", "afterHyperlinkCreate", "beforeCellFormat", "beforeCellRender", "beforeCellSave", "beforeCellUpdate", "beforeConditionalFormat", "beforeDataBound", "beforeHyperlinkClick", "beforeHyperlinkCreate", "beforeOpen", "beforeSave", "beforeSelect", "beforeSort", "cellEdit", "cellEdited", "cellEditing", "cellSave", "contextMenuBeforeClose", "contextMenuBeforeOpen", "contextMenuItemSelect", "created", "dataBound", "dataSourceChanged", "dialogBeforeOpen", "fileMenuBeforeClose", "fileMenuBeforeOpen", "fileMenuItemSelect", "openComplete", "openFailure", "queryCellInfo", "saveComplete", "select", "sortComplete"] }, { kind: "component", type: ImportExportButtonComponent, selector: "wuic-import-export-button", inputs: ["routeName", "tableMetadata", "filterInfoProvider"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: SyncfusionStylesBootstrapComponent, selector: "wuic-syncfusion-styles-bootstrap" }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }] });
|
|
4290
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SpreadsheetListSfComponent, isStandalone: true, selector: "wuic-spreadsheet-list-sf", inputs: { hardcodedRoute: "hardcodedRoute", parentRecord: "parentRecord", parentMetaInfo: "parentMetaInfo", datasource: "datasource", hardcodedDatasource: "hardcodedDatasource", hideToolbar: "hideToolbar", enableLockAwareness: "enableLockAwareness", lockScope: "lockScope", heartbeatIntervalSec: "heartbeatIntervalSec", showFormulaBar: "showFormulaBar", showRibbon: "showRibbon" }, outputs: { onSpreadsheetDataBound: "onSpreadsheetDataBound", onSpreadsheetBeforePageChange: "onSpreadsheetBeforePageChange", onSpreadsheetPageChange: "onSpreadsheetPageChange", onSpreadsheetRowInserted: "onSpreadsheetRowInserted", onSpreadsheetRowsDeleted: "onSpreadsheetRowsDeleted", onSpreadsheetBatchSaved: "onSpreadsheetBatchSaved", onLockAcquired: "onLockAcquired", onLockConflict: "onLockConflict", onLockReleased: "onLockReleased", onLockExpired: "onLockExpired" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "sfSpreadsheet", first: true, predicate: ["sfSpreadsheet"], descendants: true }, { propertyName: "lookupSource", first: true, predicate: ["lookupSource"], descendants: true }], ngImport: i0, template: "<!--\n Layout root wrapper:\n 1. Toolbar WUIC custom (insert / save batch / export / refresh)\n 2. Syncfusion Spreadsheet host\n 3. Pagination custom (Syncfusion non ha pagination built-in, quindi la\n rendiamo sotto il foglio con nav + page-size dropdown)\n 4. Lookup datasource (invisibile, pattern identico al vecchio componente)\n-->\n\n<!-- Bootstrap CSS Syncfusion (~500 KiB): si monta solo quando questo\n componente lazy \u00E8 istanziato \u2192 CSS NON nel bundle iniziale globale,\n risparmia ~500 KiB su tutte le pagine che non aprono spreadsheet. -->\n<wuic-syncfusion-styles-bootstrap></wuic-syncfusion-styles-bootstrap>\n\n<div class=\"wuic-sf-spreadsheet-wrapper\">\n\n @if (!hideToolbar && metaInfo) {\n <!-- Stile allineato a <wuic-list-grid>: stessa caption-bar, stessi p-button\n PrimeNG con icona + label, stesso componente import/export. -->\n <div class=\"grid-caption-bar wuic-sf-caption-bar\" role=\"toolbar\" aria-label=\"Spreadsheet toolbar\">\n <div class=\"grid-caption-left\">\n @if (!metaInfo.tableMetadata.md_hide_refresh) {\n <p-button icon=\"pi pi-refresh\" label=\"{{'refresh_grid' | translate}}\"\n (onClick)=\"onToolbarRefresh()\" />\n }\n @if (canInsertRows()) {\n <p-button icon=\"pi pi-plus\" label=\"{{'add_grid' | translate}}\"\n (onClick)=\"onToolbarInsert()\" />\n }\n @if (!metaInfo.tableMetadata.md_hide_export_xls) {\n <!-- Include dialog con progress bar + bottoni \"Stoppa e salva\n parziale\" / \"Stoppa\" / \"Continua in background\". -->\n <wuic-import-export-button\n [routeName]=\"metaInfo.tableMetadata.md_route_name\"\n [tableMetadata]=\"metaInfo.tableMetadata\"\n [filterInfoProvider]=\"getCurrentFilterInfoForExport\"></wuic-import-export-button>\n }\n @if (isBatchSaveEnabled()) {\n <p-button icon=\"pi pi-save\" label=\"{{'list_grid.save_changes' | translate}}\"\n (onClick)=\"onToolbarSaveBatch()\" />\n }\n </div>\n </div>\n }\n\n <div class=\"wuic-sf-spreadsheet-host\">\n @if (metaInfo && sfSheets.length > 0) {\n <ejs-spreadsheet #sfSpreadsheet\n [sheets]=\"sfSheets\"\n [height]=\"sfHeight\"\n [width]=\"sfWidth\"\n [scrollSettings]=\"sfScrollSettings\"\n [showRibbon]=\"showRibbon\"\n [showFormulaBar]=\"showFormulaBar\"\n [showSheetTabs]=\"false\"\n [allowEditing]=\"true\"\n [allowSorting]=\"true\"\n [allowFiltering]=\"false\"\n [allowResizing]=\"true\"\n [allowInsert]=\"canInsertRows()\"\n [allowDelete]=\"canDeleteRows()\"\n [enableContextMenu]=\"true\"\n [enableKeyboardNavigation]=\"true\"\n [enableClipboard]=\"true\"\n (created)=\"onSyncfusionCreated()\"\n (dataBound)=\"onSyncfusionDataBound()\"\n (cellSave)=\"onSyncfusionCellSave($event)\"\n (beforeCellSave)=\"onSyncfusionBeforeCellSave($event)\"\n (beforeCellRender)=\"onSyncfusionBeforeCellRender($event)\"\n (cellEdit)=\"onSyncfusionCellEdit($event)\"\n (contextMenuBeforeOpen)=\"onSyncfusionContextMenuBeforeOpen($event)\"\n (contextMenuItemSelect)=\"onSyncfusionContextMenuItemSelect($event)\"\n (actionBegin)=\"onSyncfusionActionBegin($event)\"\n (actionComplete)=\"onSyncfusionActionComplete($event)\"\n (dialogBeforeOpen)=\"onSyncfusionDialogBeforeOpen($event)\">\n </ejs-spreadsheet>\n }\n </div>\n\n @if (totalPages > 1 || totalRecords > pageSize) {\n <div class=\"wuic-sf-pagination\" role=\"navigation\" aria-label=\"Paginazione\">\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(1, pageSize) : setClientPage(1)\"\n title=\"Prima pagina\">\n <i class=\"pi pi-angle-double-left\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex - 1, pageSize) : setClientPage(pageIndex - 1)\"\n title=\"Pagina precedente\">\n <i class=\"pi pi-angle-left\"></i>\n </button>\n <span class=\"wuic-sf-page-info\">\n Pagina <strong>{{ pageIndex }}</strong> di <strong>{{ totalPages }}</strong>\n <span class=\"wuic-sf-page-total\">({{ totalRecords }} record)</span>\n </span>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex + 1, pageSize) : setClientPage(pageIndex + 1)\"\n title=\"Pagina successiva\">\n <i class=\"pi pi-angle-right\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(totalPages, pageSize) : setClientPage(totalPages)\"\n title=\"Ultima pagina\">\n <i class=\"pi pi-angle-double-right\"></i>\n </button>\n <select class=\"wuic-sf-page-size\" [value]=\"pageSize\"\n (change)=\"setPageSize($any($event.target).value)\"\n title=\"Record per pagina\">\n <option [value]=\"10\">10</option>\n <option [value]=\"25\">25</option>\n <option [value]=\"50\">50</option>\n <option [value]=\"100\">100</option>\n </select>\n </div>\n }\n\n</div>\n\n@if (!hardcodedRoute) {\n <wuic-data-source #lookupSource [autoload]=\"true\" [routeFromRouting]=\"false\"\n [route]=\"currentRoute\" [parentRecord]=\"currentRecord\"\n [parentMetaInfo]=\"metaInfo\"></wuic-data-source>\n}\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.wuic-sf-spreadsheet-wrapper{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;overflow:hidden}.wuic-sf-caption-bar{flex:0 0 auto;padding:5px}.grid-caption-left{display:flex;align-items:stretch;gap:.5rem;flex-wrap:nowrap;min-height:2.25rem;height:2.25rem}.wuic-sf-spreadsheet-host{flex:1 1 auto;min-height:0;width:100%;position:relative;overflow:visible}::ng-deep .e-dialog.e-popup-open,::ng-deep .e-excelfilter,::ng-deep .e-filter-popup{z-index:10010!important;min-width:240px}::ng-deep .e-excelfilter .e-footer-content,::ng-deep .e-filter-popup .e-footer-content{display:flex!important;justify-content:flex-end;gap:6px;padding:8px 12px!important}.wuic-sf-spreadsheet-host ejs-spreadsheet{display:block;width:100%;height:100%}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header .e-header-cell-div{font-weight:700!important;text-align:left!important;padding-left:6px!important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wuic-sf-pagination{display:flex;gap:6px;align-items:center;justify-content:flex-end;padding:6px 10px;background-color:var(--wuic-surface-a, #ffffff);border-top:1px solid var(--wuic-surface-border, #e5e7eb);flex:0 0 auto;font-size:13px}.wuic-sf-page-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;cursor:pointer;color:var(--wuic-text-color, #1f2937);transition:background-color .12s ease,border-color .12s ease}.wuic-sf-page-btn:hover:not(:disabled){background-color:var(--wuic-hover-bg, #f3f4f6);border-color:var(--wuic-primary-color, #6366f1)}.wuic-sf-page-btn:disabled{opacity:.35;cursor:not-allowed}.wuic-sf-page-info{margin:0 8px;white-space:nowrap;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-info strong{color:var(--wuic-text-color, #1f2937)}.wuic-sf-page-total{margin-left:6px;font-size:12px;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-size{margin-left:8px;height:30px;padding:0 8px;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;color:var(--wuic-text-color, #1f2937);cursor:pointer}:host-context(.theme-dark) .wuic-sf-pagination,:host-context([data-theme=\"dark\"]) .wuic-sf-pagination{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn,:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn,:host-context(.theme-dark) .wuic-sf-page-size,:host-context([data-theme=\"dark\"]) .wuic-sf-page-size{color:#f3f4f6;border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn:hover:not(:disabled),:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn:hover:not(:disabled){background-color:#374151}:host-context(.theme-dark) .wuic-sf-page-info,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info{color:#9ca3af}:host-context(.theme-dark) .wuic-sf-page-info strong,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info strong{color:#f3f4f6}:host-context(.theme-dark) .wuic-sf-page-total,:host-context([data-theme=\"dark\"]) .wuic-sf-page-total{color:#9ca3af}:host ::ng-deep .e-spreadsheet{font-family:var(--wuic-font-family, inherit)}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .e-header-cell.wuic-sf-colname-header{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep .e-spreadsheet .e-sheet-content .e-filter-btn{opacity:0!important;pointer-events:none!important;width:0!important;height:0!important;min-width:0!important;padding:0!important;margin:0!important;overflow:hidden!important;position:absolute!important;right:0;top:0}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header{position:relative!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn{position:absolute;right:2px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;background:transparent;border:none;border-radius:3px;cursor:pointer;color:#475569;flex-shrink:0;transition:background-color .12s,color .12s;z-index:2}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#e2e8f0;color:#1f2937}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg{display:block;pointer-events:none}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet svg,:host ::ng-deep .e-spreadsheet svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#2563eb}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-colname-header .e-header-cell-div{padding-right:22px!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn{color:#cbd5e1}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#334155;color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#60a5fa}:host ::ng-deep .e-spreadsheet .e-sheet-panel{background-color:var(--wuic-surface-a, #ffffff)}:host-context(.theme-dark) ::ng-deep .e-spreadsheet,:host-context([data-theme=\"dark\"]) ::ng-deep .e-spreadsheet,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-sheet-panel,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-main-panel{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-cell{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-row .e-header-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-row-header .e-header-cell{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell.row-danger{background-color:#7f1d1d!important;color:#fecaca!important}:host ::ng-deep .e-cell.row-danger{background-color:#fee2e2!important}:host ::ng-deep .e-cell.row-warning{background-color:#fef3c7!important}:host ::ng-deep .e-cell.row-success{background-color:#d1fae5!important}::ng-deep .wuic-sf-filter-popup{position:fixed;z-index:99999;min-width:260px;max-width:340px;background:#fff;color:#111827;border:1px solid #d1d5db;border-radius:6px;box-shadow:0 8px 24px #0000002e;font-family:inherit;font-size:13px;display:flex;flex-direction:column;overflow:hidden;-webkit-user-select:none;user-select:none}::ng-deep .wuic-sf-filter-popup-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#f3f4f6;border-bottom:1px solid #e5e7eb;font-weight:600}::ng-deep .wuic-sf-filter-popup-title{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-close{background:transparent;border:none;cursor:pointer;font-size:18px;line-height:1;padding:0 4px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-close:hover{color:#111827}::ng-deep .wuic-sf-filter-popup-sort{display:flex;flex-direction:column;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-sort-btn{background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#111827;display:flex;align-items:center;gap:6px}::ng-deep .wuic-sf-filter-popup-sort-btn:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-sort-icon{font-size:10px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-clear{border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-clear-btn{width:100%;background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#2563eb}::ng-deep .wuic-sf-filter-popup-clear-btn:hover:not(:disabled){background:#eff6ff}::ng-deep .wuic-sf-filter-popup-clear-btn:disabled{color:#9ca3af;cursor:not-allowed}::ng-deep .wuic-sf-filter-popup-search{padding:8px 12px;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-search-input{width:100%;padding:4px 8px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;outline:none;box-sizing:border-box}::ng-deep .wuic-sf-filter-popup-search-input:focus{border-color:#2563eb;box-shadow:0 0 0 2px #2563eb33}::ng-deep .wuic-sf-filter-popup-list-wrap{display:flex;flex-direction:column;max-height:260px;overflow:hidden}::ng-deep .wuic-sf-filter-popup-selectall{padding:4px 12px;border-bottom:1px solid #e5e7eb;background:#f9fafb;font-weight:500}::ng-deep .wuic-sf-filter-popup-list{flex:1 1 auto;overflow-y:auto;max-height:220px}::ng-deep .wuic-sf-filter-popup-item{display:flex;align-items:center;gap:6px;padding:4px 12px;cursor:pointer}::ng-deep .wuic-sf-filter-popup-item:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-item input[type=checkbox]{margin:0;flex:0 0 auto}::ng-deep .wuic-sf-filter-popup-item span{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-footer{display:flex;justify-content:flex-end;gap:8px;padding:8px 12px;border-top:1px solid #e5e7eb;background:#f9fafb}::ng-deep .wuic-sf-filter-popup-ok,::ng-deep .wuic-sf-filter-popup-cancel{padding:4px 14px;border:1px solid #d1d5db;border-radius:4px;background:#fff;cursor:pointer;font-size:13px}::ng-deep .wuic-sf-filter-popup-ok{background:#2563eb;color:#fff;border-color:#2563eb}::ng-deep .wuic-sf-filter-popup-ok:hover{background:#1d4ed8}::ng-deep .wuic-sf-filter-popup-cancel:hover{background:#f3f4f6}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active{color:#2563eb}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active svg{fill:#2563eb}::ng-deep .wuic-sf-filter-popup.wuic-sf-filter-popup--dark{background:#1f2937;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-header,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-footer,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-selectall{background:#111827;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-clear,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search{border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{color:#f3f4f6;background:transparent;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-item:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel:hover{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search-input{background:#111827;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close{color:#9ca3af}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close:hover{color:#f3f4f6}::ng-deep .wuic-sf-cell-editor{box-sizing:border-box;padding:2px 6px;border:2px solid #2563eb;background:#fff;color:#111827;font-family:inherit;font-size:13px;outline:none;box-shadow:0 2px 8px #00000026;border-radius:2px}::ng-deep .wuic-sf-cell-editor:focus{border-color:#1d4ed8;box-shadow:0 0 0 2px #2563eb40}::ng-deep select.wuic-sf-cell-editor{cursor:pointer}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark{background:#1f2937;color:#f3f4f6;border-color:#2563eb}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark option{background:#1f2937;color:#f3f4f6}\n"], dependencies: [{ kind: "component", type: DataSourceComponent, selector: "wuic-data-source", inputs: ["route", "routeFromRouting", "hardcodedRoute", "autoload", "loading", "changeTracking", "parentRecord", "parentMetaInfo", "parentDatasource", "useComboEndpoint", "comboFormulaLookup", "comboMcId", "comboField", "comboExtraFields", "comboCurrentRecord", "componentRef"] }, { kind: "ngmodule", type: SpreadsheetModule }, { kind: "component", type: i7.SpreadsheetComponent, selector: "ejs-spreadsheet", inputs: ["activeSheetIndex", "allowAutoFill", "allowCellFormatting", "allowChart", "allowConditionalFormat", "allowDataValidation", "allowDelete", "allowEditing", "allowFiltering", "allowFindAndReplace", "allowFreezePane", "allowHyperlink", "allowImage", "allowInsert", "allowMerge", "allowNumberFormatting", "allowOpen", "allowPrint", "allowResizing", "allowSave", "allowScrolling", "allowSorting", "allowUndoRedo", "allowWrap", "author", "autoFillSettings", "calculationMode", "cellStyle", "cssClass", "currencyCode", "definedNames", "enableClipboard", "enableContextMenu", "enableKeyboardNavigation", "enableKeyboardShortcut", "enableNotes", "enablePersistence", "enableRtl", "height", "isProtected", "listSeparator", "locale", "openSettings", "openUrl", "password", "saveUrl", "scrollSettings", "selectionSettings", "sheets", "showAggregate", "showCommentsPane", "showFormulaBar", "showRibbon", "showSheetTabs", "width"], outputs: ["actionBegin", "actionComplete", "afterHyperlinkClick", "afterHyperlinkCreate", "beforeCellFormat", "beforeCellRender", "beforeCellSave", "beforeCellUpdate", "beforeConditionalFormat", "beforeDataBound", "beforeHyperlinkClick", "beforeHyperlinkCreate", "beforeOpen", "beforeSave", "beforeSelect", "beforeSort", "cellEdit", "cellEdited", "cellEditing", "cellSave", "contextMenuBeforeClose", "contextMenuBeforeOpen", "contextMenuItemSelect", "created", "dataBound", "dataSourceChanged", "dialogBeforeOpen", "fileMenuBeforeClose", "fileMenuBeforeOpen", "fileMenuItemSelect", "openComplete", "openFailure", "queryCellInfo", "saveComplete", "select", "sortComplete"] }, { kind: "component", type: ImportExportButtonComponent, selector: "wuic-import-export-button", inputs: ["routeName", "tableMetadata", "filterInfoProvider"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: SyncfusionStylesBootstrapComponent, selector: "wuic-syncfusion-styles-bootstrap" }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }] });
|
|
3850
4291
|
}
|
|
3851
4292
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpreadsheetListSfComponent, decorators: [{
|
|
3852
4293
|
type: Component,
|
|
@@ -3857,7 +4298,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3857
4298
|
ButtonModule,
|
|
3858
4299
|
TranslateModule,
|
|
3859
4300
|
SyncfusionStylesBootstrapComponent
|
|
3860
|
-
], template: "<!--\n Layout root wrapper:\n 1. Toolbar WUIC custom (insert / save batch / export / refresh)\n 2. Syncfusion Spreadsheet host\n 3. Pagination custom (Syncfusion non ha pagination built-in, quindi la\n rendiamo sotto il foglio con nav + page-size dropdown)\n 4. Lookup datasource (invisibile, pattern identico al vecchio componente)\n-->\n\n<!-- Bootstrap CSS Syncfusion (~500 KiB): si monta solo quando questo\n componente lazy \u00E8 istanziato \u2192 CSS NON nel bundle iniziale globale,\n risparmia ~500 KiB su tutte le pagine che non aprono spreadsheet. -->\n<wuic-syncfusion-styles-bootstrap></wuic-syncfusion-styles-bootstrap>\n\n<div class=\"wuic-sf-spreadsheet-wrapper\">\n\n @if (!hideToolbar && metaInfo) {\n <!-- Stile allineato a <wuic-list-grid>: stessa caption-bar, stessi p-button\n PrimeNG con icona + label, stesso componente import/export. -->\n <div class=\"grid-caption-bar wuic-sf-caption-bar\" role=\"toolbar\" aria-label=\"Spreadsheet toolbar\">\n <div class=\"grid-caption-left\">\n @if (!metaInfo.tableMetadata.md_hide_refresh) {\n <p-button icon=\"pi pi-refresh\" label=\"{{'refresh_grid' | translate}}\"\n (onClick)=\"onToolbarRefresh()\" />\n }\n @if (canInsertRows()) {\n <p-button icon=\"pi pi-plus\" label=\"{{'add_grid' | translate}}\"\n (onClick)=\"onToolbarInsert()\" />\n }\n @if (!metaInfo.tableMetadata.md_hide_export_xls) {\n <!-- Include dialog con progress bar + bottoni \"Stoppa e salva\n parziale\" / \"Stoppa\" / \"Continua in background\". -->\n <wuic-import-export-button\n [routeName]=\"metaInfo.tableMetadata.md_route_name\"\n [tableMetadata]=\"metaInfo.tableMetadata\"\n [filterInfoProvider]=\"getCurrentFilterInfoForExport\"></wuic-import-export-button>\n }\n @if (isBatchSaveEnabled()) {\n <p-button icon=\"pi pi-save\" label=\"{{'list_grid.save_changes' | translate}}\"\n (onClick)=\"onToolbarSaveBatch()\" />\n }\n </div>\n </div>\n }\n\n <div class=\"wuic-sf-spreadsheet-host\">\n @if (metaInfo && sfSheets.length > 0) {\n <ejs-spreadsheet #sfSpreadsheet\n [sheets]=\"sfSheets\"\n [height]=\"sfHeight\"\n [width]=\"sfWidth\"\n [scrollSettings]=\"sfScrollSettings\"\n [showRibbon]=\"false\"\n [showFormulaBar]=\"false\"\n [showSheetTabs]=\"false\"\n [allowEditing]=\"true\"\n [allowSorting]=\"true\"\n [allowFiltering]=\"false\"\n [allowResizing]=\"true\"\n [allowInsert]=\"canInsertRows()\"\n [allowDelete]=\"canDeleteRows()\"\n [enableContextMenu]=\"true\"\n [enableKeyboardNavigation]=\"true\"\n [enableClipboard]=\"true\"\n (created)=\"onSyncfusionCreated()\"\n (dataBound)=\"onSyncfusionDataBound()\"\n (cellSave)=\"onSyncfusionCellSave($event)\"\n (beforeCellSave)=\"onSyncfusionBeforeCellSave($event)\"\n (beforeCellRender)=\"onSyncfusionBeforeCellRender($event)\"\n (cellEdit)=\"onSyncfusionCellEdit($event)\"\n (contextMenuBeforeOpen)=\"onSyncfusionContextMenuBeforeOpen($event)\"\n (contextMenuItemSelect)=\"onSyncfusionContextMenuItemSelect($event)\"\n (actionBegin)=\"onSyncfusionActionBegin($event)\"\n (actionComplete)=\"onSyncfusionActionComplete($event)\"\n (dialogBeforeOpen)=\"onSyncfusionDialogBeforeOpen($event)\">\n </ejs-spreadsheet>\n }\n </div>\n\n @if (totalPages > 1 || totalRecords > pageSize) {\n <div class=\"wuic-sf-pagination\" role=\"navigation\" aria-label=\"Paginazione\">\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(1, pageSize) : setClientPage(1)\"\n title=\"Prima pagina\">\n <i class=\"pi pi-angle-double-left\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex - 1, pageSize) : setClientPage(pageIndex - 1)\"\n title=\"Pagina precedente\">\n <i class=\"pi pi-angle-left\"></i>\n </button>\n <span class=\"wuic-sf-page-info\">\n Pagina <strong>{{ pageIndex }}</strong> di <strong>{{ totalPages }}</strong>\n <span class=\"wuic-sf-page-total\">({{ totalRecords }} record)</span>\n </span>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex + 1, pageSize) : setClientPage(pageIndex + 1)\"\n title=\"Pagina successiva\">\n <i class=\"pi pi-angle-right\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(totalPages, pageSize) : setClientPage(totalPages)\"\n title=\"Ultima pagina\">\n <i class=\"pi pi-angle-double-right\"></i>\n </button>\n <select class=\"wuic-sf-page-size\" [value]=\"pageSize\"\n (change)=\"setPageSize($any($event.target).value)\"\n title=\"Record per pagina\">\n <option [value]=\"10\">10</option>\n <option [value]=\"25\">25</option>\n <option [value]=\"50\">50</option>\n <option [value]=\"100\">100</option>\n </select>\n </div>\n }\n\n</div>\n\n@if (!hardcodedRoute) {\n <wuic-data-source #lookupSource [autoload]=\"true\" [routeFromRouting]=\"false\"\n [route]=\"currentRoute\" [parentRecord]=\"currentRecord\"\n [parentMetaInfo]=\"metaInfo\"></wuic-data-source>\n}\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.wuic-sf-spreadsheet-wrapper{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;overflow:hidden}.wuic-sf-caption-bar{flex:0 0 auto;padding:5px}.grid-caption-left{display:flex;align-items:stretch;gap:.5rem;flex-wrap:nowrap;min-height:2.25rem;height:2.25rem}.wuic-sf-spreadsheet-host{flex:1 1 auto;min-height:0;width:100%;position:relative;overflow:visible}::ng-deep .e-dialog.e-popup-open,::ng-deep .e-excelfilter,::ng-deep .e-filter-popup{z-index:10010!important;min-width:240px}::ng-deep .e-excelfilter .e-footer-content,::ng-deep .e-filter-popup .e-footer-content{display:flex!important;justify-content:flex-end;gap:6px;padding:8px 12px!important}.wuic-sf-spreadsheet-host ejs-spreadsheet{display:block;width:100%;height:100%}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header .e-header-cell-div{font-weight:700!important;text-align:left!important;padding-left:6px!important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wuic-sf-pagination{display:flex;gap:6px;align-items:center;justify-content:flex-end;padding:6px 10px;background-color:var(--wuic-surface-a, #ffffff);border-top:1px solid var(--wuic-surface-border, #e5e7eb);flex:0 0 auto;font-size:13px}.wuic-sf-page-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;cursor:pointer;color:var(--wuic-text-color, #1f2937);transition:background-color .12s ease,border-color .12s ease}.wuic-sf-page-btn:hover:not(:disabled){background-color:var(--wuic-hover-bg, #f3f4f6);border-color:var(--wuic-primary-color, #6366f1)}.wuic-sf-page-btn:disabled{opacity:.35;cursor:not-allowed}.wuic-sf-page-info{margin:0 8px;white-space:nowrap;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-info strong{color:var(--wuic-text-color, #1f2937)}.wuic-sf-page-total{margin-left:6px;font-size:12px;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-size{margin-left:8px;height:30px;padding:0 8px;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;color:var(--wuic-text-color, #1f2937);cursor:pointer}:host-context(.theme-dark) .wuic-sf-pagination,:host-context([data-theme=\"dark\"]) .wuic-sf-pagination{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn,:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn,:host-context(.theme-dark) .wuic-sf-page-size,:host-context([data-theme=\"dark\"]) .wuic-sf-page-size{color:#f3f4f6;border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn:hover:not(:disabled),:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn:hover:not(:disabled){background-color:#374151}:host-context(.theme-dark) .wuic-sf-page-info,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info{color:#9ca3af}:host-context(.theme-dark) .wuic-sf-page-info strong,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info strong{color:#f3f4f6}:host-context(.theme-dark) .wuic-sf-page-total,:host-context([data-theme=\"dark\"]) .wuic-sf-page-total{color:#9ca3af}:host ::ng-deep .e-spreadsheet{font-family:var(--wuic-font-family, inherit)}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .e-header-cell.wuic-sf-colname-header{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep .e-spreadsheet .e-sheet-content .e-filter-btn{opacity:0!important;pointer-events:none!important;width:0!important;height:0!important;min-width:0!important;padding:0!important;margin:0!important;overflow:hidden!important;position:absolute!important;right:0;top:0}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header{position:relative!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn{position:absolute;right:2px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;background:transparent;border:none;border-radius:3px;cursor:pointer;color:#475569;flex-shrink:0;transition:background-color .12s,color .12s;z-index:2}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#e2e8f0;color:#1f2937}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg{display:block;pointer-events:none}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet svg,:host ::ng-deep .e-spreadsheet svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#2563eb}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-colname-header .e-header-cell-div{padding-right:22px!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn{color:#cbd5e1}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#334155;color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#60a5fa}:host ::ng-deep .e-spreadsheet .e-sheet-panel{background-color:var(--wuic-surface-a, #ffffff)}:host-context(.theme-dark) ::ng-deep .e-spreadsheet,:host-context([data-theme=\"dark\"]) ::ng-deep .e-spreadsheet,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-sheet-panel,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-main-panel{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-cell{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-row .e-header-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-row-header .e-header-cell{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell.row-danger{background-color:#7f1d1d!important;color:#fecaca!important}:host ::ng-deep .e-cell.row-danger{background-color:#fee2e2!important}:host ::ng-deep .e-cell.row-warning{background-color:#fef3c7!important}:host ::ng-deep .e-cell.row-success{background-color:#d1fae5!important}::ng-deep .wuic-sf-filter-popup{position:fixed;z-index:99999;min-width:260px;max-width:340px;background:#fff;color:#111827;border:1px solid #d1d5db;border-radius:6px;box-shadow:0 8px 24px #0000002e;font-family:inherit;font-size:13px;display:flex;flex-direction:column;overflow:hidden;-webkit-user-select:none;user-select:none}::ng-deep .wuic-sf-filter-popup-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#f3f4f6;border-bottom:1px solid #e5e7eb;font-weight:600}::ng-deep .wuic-sf-filter-popup-title{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-close{background:transparent;border:none;cursor:pointer;font-size:18px;line-height:1;padding:0 4px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-close:hover{color:#111827}::ng-deep .wuic-sf-filter-popup-sort{display:flex;flex-direction:column;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-sort-btn{background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#111827;display:flex;align-items:center;gap:6px}::ng-deep .wuic-sf-filter-popup-sort-btn:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-sort-icon{font-size:10px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-clear{border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-clear-btn{width:100%;background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#2563eb}::ng-deep .wuic-sf-filter-popup-clear-btn:hover:not(:disabled){background:#eff6ff}::ng-deep .wuic-sf-filter-popup-clear-btn:disabled{color:#9ca3af;cursor:not-allowed}::ng-deep .wuic-sf-filter-popup-search{padding:8px 12px;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-search-input{width:100%;padding:4px 8px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;outline:none;box-sizing:border-box}::ng-deep .wuic-sf-filter-popup-search-input:focus{border-color:#2563eb;box-shadow:0 0 0 2px #2563eb33}::ng-deep .wuic-sf-filter-popup-list-wrap{display:flex;flex-direction:column;max-height:260px;overflow:hidden}::ng-deep .wuic-sf-filter-popup-selectall{padding:4px 12px;border-bottom:1px solid #e5e7eb;background:#f9fafb;font-weight:500}::ng-deep .wuic-sf-filter-popup-list{flex:1 1 auto;overflow-y:auto;max-height:220px}::ng-deep .wuic-sf-filter-popup-item{display:flex;align-items:center;gap:6px;padding:4px 12px;cursor:pointer}::ng-deep .wuic-sf-filter-popup-item:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-item input[type=checkbox]{margin:0;flex:0 0 auto}::ng-deep .wuic-sf-filter-popup-item span{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-footer{display:flex;justify-content:flex-end;gap:8px;padding:8px 12px;border-top:1px solid #e5e7eb;background:#f9fafb}::ng-deep .wuic-sf-filter-popup-ok,::ng-deep .wuic-sf-filter-popup-cancel{padding:4px 14px;border:1px solid #d1d5db;border-radius:4px;background:#fff;cursor:pointer;font-size:13px}::ng-deep .wuic-sf-filter-popup-ok{background:#2563eb;color:#fff;border-color:#2563eb}::ng-deep .wuic-sf-filter-popup-ok:hover{background:#1d4ed8}::ng-deep .wuic-sf-filter-popup-cancel:hover{background:#f3f4f6}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active{color:#2563eb}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active svg{fill:#2563eb}::ng-deep .wuic-sf-filter-popup.wuic-sf-filter-popup--dark{background:#1f2937;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-header,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-footer,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-selectall{background:#111827;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-clear,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search{border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{color:#f3f4f6;background:transparent;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-item:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel:hover{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search-input{background:#111827;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close{color:#9ca3af}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close:hover{color:#f3f4f6}::ng-deep .wuic-sf-cell-editor{box-sizing:border-box;padding:2px 6px;border:2px solid #2563eb;background:#fff;color:#111827;font-family:inherit;font-size:13px;outline:none;box-shadow:0 2px 8px #00000026;border-radius:2px}::ng-deep .wuic-sf-cell-editor:focus{border-color:#1d4ed8;box-shadow:0 0 0 2px #2563eb40}::ng-deep select.wuic-sf-cell-editor{cursor:pointer}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark{background:#1f2937;color:#f3f4f6;border-color:#2563eb}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark option{background:#1f2937;color:#f3f4f6}\n"] }]
|
|
4301
|
+
], template: "<!--\n Layout root wrapper:\n 1. Toolbar WUIC custom (insert / save batch / export / refresh)\n 2. Syncfusion Spreadsheet host\n 3. Pagination custom (Syncfusion non ha pagination built-in, quindi la\n rendiamo sotto il foglio con nav + page-size dropdown)\n 4. Lookup datasource (invisibile, pattern identico al vecchio componente)\n-->\n\n<!-- Bootstrap CSS Syncfusion (~500 KiB): si monta solo quando questo\n componente lazy \u00E8 istanziato \u2192 CSS NON nel bundle iniziale globale,\n risparmia ~500 KiB su tutte le pagine che non aprono spreadsheet. -->\n<wuic-syncfusion-styles-bootstrap></wuic-syncfusion-styles-bootstrap>\n\n<div class=\"wuic-sf-spreadsheet-wrapper\">\n\n @if (!hideToolbar && metaInfo) {\n <!-- Stile allineato a <wuic-list-grid>: stessa caption-bar, stessi p-button\n PrimeNG con icona + label, stesso componente import/export. -->\n <div class=\"grid-caption-bar wuic-sf-caption-bar\" role=\"toolbar\" aria-label=\"Spreadsheet toolbar\">\n <div class=\"grid-caption-left\">\n @if (!metaInfo.tableMetadata.md_hide_refresh) {\n <p-button icon=\"pi pi-refresh\" label=\"{{'refresh_grid' | translate}}\"\n (onClick)=\"onToolbarRefresh()\" />\n }\n @if (canInsertRows()) {\n <p-button icon=\"pi pi-plus\" label=\"{{'add_grid' | translate}}\"\n (onClick)=\"onToolbarInsert()\" />\n }\n @if (!metaInfo.tableMetadata.md_hide_export_xls) {\n <!-- Include dialog con progress bar + bottoni \"Stoppa e salva\n parziale\" / \"Stoppa\" / \"Continua in background\". -->\n <wuic-import-export-button\n [routeName]=\"metaInfo.tableMetadata.md_route_name\"\n [tableMetadata]=\"metaInfo.tableMetadata\"\n [filterInfoProvider]=\"getCurrentFilterInfoForExport\"></wuic-import-export-button>\n }\n @if (isBatchSaveEnabled()) {\n <p-button icon=\"pi pi-save\" label=\"{{'list_grid.save_changes' | translate}}\"\n (onClick)=\"onToolbarSaveBatch()\" />\n }\n </div>\n </div>\n }\n\n <div class=\"wuic-sf-spreadsheet-host\">\n @if (metaInfo && sfSheets.length > 0) {\n <ejs-spreadsheet #sfSpreadsheet\n [sheets]=\"sfSheets\"\n [height]=\"sfHeight\"\n [width]=\"sfWidth\"\n [scrollSettings]=\"sfScrollSettings\"\n [showRibbon]=\"showRibbon\"\n [showFormulaBar]=\"showFormulaBar\"\n [showSheetTabs]=\"false\"\n [allowEditing]=\"true\"\n [allowSorting]=\"true\"\n [allowFiltering]=\"false\"\n [allowResizing]=\"true\"\n [allowInsert]=\"canInsertRows()\"\n [allowDelete]=\"canDeleteRows()\"\n [enableContextMenu]=\"true\"\n [enableKeyboardNavigation]=\"true\"\n [enableClipboard]=\"true\"\n (created)=\"onSyncfusionCreated()\"\n (dataBound)=\"onSyncfusionDataBound()\"\n (cellSave)=\"onSyncfusionCellSave($event)\"\n (beforeCellSave)=\"onSyncfusionBeforeCellSave($event)\"\n (beforeCellRender)=\"onSyncfusionBeforeCellRender($event)\"\n (cellEdit)=\"onSyncfusionCellEdit($event)\"\n (contextMenuBeforeOpen)=\"onSyncfusionContextMenuBeforeOpen($event)\"\n (contextMenuItemSelect)=\"onSyncfusionContextMenuItemSelect($event)\"\n (actionBegin)=\"onSyncfusionActionBegin($event)\"\n (actionComplete)=\"onSyncfusionActionComplete($event)\"\n (dialogBeforeOpen)=\"onSyncfusionDialogBeforeOpen($event)\">\n </ejs-spreadsheet>\n }\n </div>\n\n @if (totalPages > 1 || totalRecords > pageSize) {\n <div class=\"wuic-sf-pagination\" role=\"navigation\" aria-label=\"Paginazione\">\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(1, pageSize) : setClientPage(1)\"\n title=\"Prima pagina\">\n <i class=\"pi pi-angle-double-left\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex <= 1\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex - 1, pageSize) : setClientPage(pageIndex - 1)\"\n title=\"Pagina precedente\">\n <i class=\"pi pi-angle-left\"></i>\n </button>\n <span class=\"wuic-sf-page-info\">\n Pagina <strong>{{ pageIndex }}</strong> di <strong>{{ totalPages }}</strong>\n <span class=\"wuic-sf-page-total\">({{ totalRecords }} record)</span>\n </span>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(pageIndex + 1, pageSize) : setClientPage(pageIndex + 1)\"\n title=\"Pagina successiva\">\n <i class=\"pi pi-angle-right\"></i>\n </button>\n <button type=\"button\" class=\"wuic-sf-page-btn\"\n [disabled]=\"pageIndex >= totalPages\"\n (click)=\"isServerSide ? handleServerPageChange(totalPages, pageSize) : setClientPage(totalPages)\"\n title=\"Ultima pagina\">\n <i class=\"pi pi-angle-double-right\"></i>\n </button>\n <select class=\"wuic-sf-page-size\" [value]=\"pageSize\"\n (change)=\"setPageSize($any($event.target).value)\"\n title=\"Record per pagina\">\n <option [value]=\"10\">10</option>\n <option [value]=\"25\">25</option>\n <option [value]=\"50\">50</option>\n <option [value]=\"100\">100</option>\n </select>\n </div>\n }\n\n</div>\n\n@if (!hardcodedRoute) {\n <wuic-data-source #lookupSource [autoload]=\"true\" [routeFromRouting]=\"false\"\n [route]=\"currentRoute\" [parentRecord]=\"currentRecord\"\n [parentMetaInfo]=\"metaInfo\"></wuic-data-source>\n}\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.wuic-sf-spreadsheet-wrapper{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;overflow:hidden}.wuic-sf-caption-bar{flex:0 0 auto;padding:5px}.grid-caption-left{display:flex;align-items:stretch;gap:.5rem;flex-wrap:nowrap;min-height:2.25rem;height:2.25rem}.wuic-sf-spreadsheet-host{flex:1 1 auto;min-height:0;width:100%;position:relative;overflow:visible}::ng-deep .e-dialog.e-popup-open,::ng-deep .e-excelfilter,::ng-deep .e-filter-popup{z-index:10010!important;min-width:240px}::ng-deep .e-excelfilter .e-footer-content,::ng-deep .e-filter-popup .e-footer-content{display:flex!important;justify-content:flex-end;gap:6px;padding:8px 12px!important}.wuic-sf-spreadsheet-host ejs-spreadsheet{display:block;width:100%;height:100%}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header .e-header-cell-div{font-weight:700!important;text-align:left!important;padding-left:6px!important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wuic-sf-pagination{display:flex;gap:6px;align-items:center;justify-content:flex-end;padding:6px 10px;background-color:var(--wuic-surface-a, #ffffff);border-top:1px solid var(--wuic-surface-border, #e5e7eb);flex:0 0 auto;font-size:13px}.wuic-sf-page-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;cursor:pointer;color:var(--wuic-text-color, #1f2937);transition:background-color .12s ease,border-color .12s ease}.wuic-sf-page-btn:hover:not(:disabled){background-color:var(--wuic-hover-bg, #f3f4f6);border-color:var(--wuic-primary-color, #6366f1)}.wuic-sf-page-btn:disabled{opacity:.35;cursor:not-allowed}.wuic-sf-page-info{margin:0 8px;white-space:nowrap;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-info strong{color:var(--wuic-text-color, #1f2937)}.wuic-sf-page-total{margin-left:6px;font-size:12px;color:var(--wuic-text-muted, #6b7280)}.wuic-sf-page-size{margin-left:8px;height:30px;padding:0 8px;border:1px solid var(--wuic-surface-border, #e5e7eb);border-radius:4px;background:transparent;color:var(--wuic-text-color, #1f2937);cursor:pointer}:host-context(.theme-dark) .wuic-sf-pagination,:host-context([data-theme=\"dark\"]) .wuic-sf-pagination{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn,:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn,:host-context(.theme-dark) .wuic-sf-page-size,:host-context([data-theme=\"dark\"]) .wuic-sf-page-size{color:#f3f4f6;border-color:var(--wuic-surface-border-dark, #374151)}:host-context(.theme-dark) .wuic-sf-page-btn:hover:not(:disabled),:host-context([data-theme=\"dark\"]) .wuic-sf-page-btn:hover:not(:disabled){background-color:#374151}:host-context(.theme-dark) .wuic-sf-page-info,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info{color:#9ca3af}:host-context(.theme-dark) .wuic-sf-page-info strong,:host-context([data-theme=\"dark\"]) .wuic-sf-page-info strong{color:#f3f4f6}:host-context(.theme-dark) .wuic-sf-page-total,:host-context([data-theme=\"dark\"]) .wuic-sf-page-total{color:#9ca3af}:host ::ng-deep .e-spreadsheet{font-family:var(--wuic-font-family, inherit)}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .e-header-cell.wuic-sf-colname-header{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep .e-spreadsheet .e-sheet-content .e-filter-btn{opacity:0!important;pointer-events:none!important;width:0!important;height:0!important;min-width:0!important;padding:0!important;margin:0!important;overflow:hidden!important;position:absolute!important;right:0;top:0}:host ::ng-deep .e-spreadsheet .e-column-header .e-header-cell.wuic-sf-colname-header{position:relative!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn{position:absolute;right:2px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;background:transparent;border:none;border-radius:3px;cursor:pointer;color:#475569;flex-shrink:0;transition:background-color .12s,color .12s;z-index:2}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#e2e8f0;color:#1f2937}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg{display:block;pointer-events:none}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet svg,:host ::ng-deep .e-spreadsheet svg *{pointer-events:none!important}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#2563eb}:host ::ng-deep .e-spreadsheet .e-column-header .wuic-sf-colname-header .e-header-cell-div{padding-right:22px!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn{color:#cbd5e1}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn:hover{background-color:#334155;color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-column-header .wuic-sf-header-filter-btn.wuic-filtered{color:#60a5fa}:host ::ng-deep .e-spreadsheet .e-sheet-panel{background-color:var(--wuic-surface-a, #ffffff)}:host-context(.theme-dark) ::ng-deep .e-spreadsheet,:host-context([data-theme=\"dark\"]) ::ng-deep .e-spreadsheet,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-sheet-panel,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-main-panel{background-color:var(--wuic-surface-a-dark, #1f2937);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-cell{background-color:var(--wuic-surface-a-dark, #1f2937);border-color:var(--wuic-surface-border-dark, #374151);color:#f3f4f6}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-header-row .e-header-cell,:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-row-header .e-header-cell{background-color:#111827!important;color:#e5e7eb!important}:host ::ng-deep ejs-spreadsheet.wuic-sf-dark .e-cell.row-danger{background-color:#7f1d1d!important;color:#fecaca!important}:host ::ng-deep .e-cell.row-danger{background-color:#fee2e2!important}:host ::ng-deep .e-cell.row-warning{background-color:#fef3c7!important}:host ::ng-deep .e-cell.row-success{background-color:#d1fae5!important}::ng-deep .wuic-sf-filter-popup{position:fixed;z-index:99999;min-width:260px;max-width:340px;background:#fff;color:#111827;border:1px solid #d1d5db;border-radius:6px;box-shadow:0 8px 24px #0000002e;font-family:inherit;font-size:13px;display:flex;flex-direction:column;overflow:hidden;-webkit-user-select:none;user-select:none}::ng-deep .wuic-sf-filter-popup-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#f3f4f6;border-bottom:1px solid #e5e7eb;font-weight:600}::ng-deep .wuic-sf-filter-popup-title{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-close{background:transparent;border:none;cursor:pointer;font-size:18px;line-height:1;padding:0 4px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-close:hover{color:#111827}::ng-deep .wuic-sf-filter-popup-sort{display:flex;flex-direction:column;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-sort-btn{background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#111827;display:flex;align-items:center;gap:6px}::ng-deep .wuic-sf-filter-popup-sort-btn:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-sort-icon{font-size:10px;color:#6b7280}::ng-deep .wuic-sf-filter-popup-clear{border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-clear-btn{width:100%;background:transparent;border:none;text-align:left;padding:6px 12px;cursor:pointer;font-size:13px;color:#2563eb}::ng-deep .wuic-sf-filter-popup-clear-btn:hover:not(:disabled){background:#eff6ff}::ng-deep .wuic-sf-filter-popup-clear-btn:disabled{color:#9ca3af;cursor:not-allowed}::ng-deep .wuic-sf-filter-popup-search{padding:8px 12px;border-bottom:1px solid #e5e7eb}::ng-deep .wuic-sf-filter-popup-search-input{width:100%;padding:4px 8px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;outline:none;box-sizing:border-box}::ng-deep .wuic-sf-filter-popup-search-input:focus{border-color:#2563eb;box-shadow:0 0 0 2px #2563eb33}::ng-deep .wuic-sf-filter-popup-list-wrap{display:flex;flex-direction:column;max-height:260px;overflow:hidden}::ng-deep .wuic-sf-filter-popup-selectall{padding:4px 12px;border-bottom:1px solid #e5e7eb;background:#f9fafb;font-weight:500}::ng-deep .wuic-sf-filter-popup-list{flex:1 1 auto;overflow-y:auto;max-height:220px}::ng-deep .wuic-sf-filter-popup-item{display:flex;align-items:center;gap:6px;padding:4px 12px;cursor:pointer}::ng-deep .wuic-sf-filter-popup-item:hover{background:#f3f4f6}::ng-deep .wuic-sf-filter-popup-item input[type=checkbox]{margin:0;flex:0 0 auto}::ng-deep .wuic-sf-filter-popup-item span{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .wuic-sf-filter-popup-footer{display:flex;justify-content:flex-end;gap:8px;padding:8px 12px;border-top:1px solid #e5e7eb;background:#f9fafb}::ng-deep .wuic-sf-filter-popup-ok,::ng-deep .wuic-sf-filter-popup-cancel{padding:4px 14px;border:1px solid #d1d5db;border-radius:4px;background:#fff;cursor:pointer;font-size:13px}::ng-deep .wuic-sf-filter-popup-ok{background:#2563eb;color:#fff;border-color:#2563eb}::ng-deep .wuic-sf-filter-popup-ok:hover{background:#1d4ed8}::ng-deep .wuic-sf-filter-popup-cancel:hover{background:#f3f4f6}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active{color:#2563eb}:host ::ng-deep .wuic-sf-header-filter-btn.wuic-sf-header-filter-btn--active svg{fill:#2563eb}::ng-deep .wuic-sf-filter-popup.wuic-sf-filter-popup--dark{background:#1f2937;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-header,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-footer,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-selectall{background:#111827;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-clear,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search{border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{color:#f3f4f6;background:transparent;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-sort-btn:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-item:hover,::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-cancel:hover{background:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-search-input{background:#111827;color:#f3f4f6;border-color:#374151}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close{color:#9ca3af}::ng-deep .wuic-sf-filter-popup--dark .wuic-sf-filter-popup-close:hover{color:#f3f4f6}::ng-deep .wuic-sf-cell-editor{box-sizing:border-box;padding:2px 6px;border:2px solid #2563eb;background:#fff;color:#111827;font-family:inherit;font-size:13px;outline:none;box-shadow:0 2px 8px #00000026;border-radius:2px}::ng-deep .wuic-sf-cell-editor:focus{border-color:#1d4ed8;box-shadow:0 0 0 2px #2563eb40}::ng-deep select.wuic-sf-cell-editor{cursor:pointer}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark{background:#1f2937;color:#f3f4f6;border-color:#2563eb}::ng-deep .wuic-sf-cell-editor.wuic-sf-cell-editor--dark option{background:#1f2937;color:#f3f4f6}\n"] }]
|
|
3861
4302
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.Title }, { type: i1$1.ActivatedRoute }, { type: i1$2.HttpClient }, { type: WtoolboxService }, { type: MetadataProviderService }, { type: UserInfoService }], propDecorators: { sfSpreadsheet: [{
|
|
3862
4303
|
type: ViewChild,
|
|
3863
4304
|
args: ['sfSpreadsheet']
|
|
@@ -3876,6 +4317,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3876
4317
|
type: Input
|
|
3877
4318
|
}], hideToolbar: [{
|
|
3878
4319
|
type: Input
|
|
4320
|
+
}], enableLockAwareness: [{
|
|
4321
|
+
type: Input
|
|
4322
|
+
}], lockScope: [{
|
|
4323
|
+
type: Input
|
|
4324
|
+
}], heartbeatIntervalSec: [{
|
|
4325
|
+
type: Input
|
|
4326
|
+
}], showFormulaBar: [{
|
|
4327
|
+
type: Input
|
|
4328
|
+
}], showRibbon: [{
|
|
4329
|
+
type: Input
|
|
3879
4330
|
}], onSpreadsheetDataBound: [{
|
|
3880
4331
|
type: Output
|
|
3881
4332
|
}], onSpreadsheetBeforePageChange: [{
|
|
@@ -3888,10 +4339,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3888
4339
|
type: Output
|
|
3889
4340
|
}], onSpreadsheetBatchSaved: [{
|
|
3890
4341
|
type: Output
|
|
4342
|
+
}], onLockAcquired: [{
|
|
4343
|
+
type: Output
|
|
4344
|
+
}], onLockConflict: [{
|
|
4345
|
+
type: Output
|
|
4346
|
+
}], onLockReleased: [{
|
|
4347
|
+
type: Output
|
|
4348
|
+
}], onLockExpired: [{
|
|
4349
|
+
type: Output
|
|
3891
4350
|
}], onWindowResize: [{
|
|
3892
4351
|
type: HostListener,
|
|
3893
4352
|
args: ['window:resize']
|
|
3894
4353
|
}] } });
|
|
3895
4354
|
|
|
3896
4355
|
export { SpreadsheetListSfComponent };
|
|
3897
|
-
//# sourceMappingURL=wuic-framework-lib-spreadsheet-list-sf.component-
|
|
4356
|
+
//# sourceMappingURL=wuic-framework-lib-spreadsheet-list-sf.component-D9P9WbDl.mjs.map
|