wuic-framework-lib 1.0.20 → 1.1.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.
Files changed (16) hide show
  1. package/fesm2022/{wuic-framework-lib-chart-list.component-B44k_wZe.mjs → wuic-framework-lib-chart-list.component-C9_EDkGu.mjs} +2 -2
  2. package/fesm2022/{wuic-framework-lib-code-editor.component-D1dy4Dl9.mjs → wuic-framework-lib-code-editor.component-D_p7LGSQ.mjs} +4 -4
  3. package/fesm2022/{wuic-framework-lib-designer.component-CnOQbvEH.mjs → wuic-framework-lib-designer.component-Dx6RraAn.mjs} +4 -4
  4. package/fesm2022/{wuic-framework-lib-first-run-wizard.component-C9lswoxG.mjs → wuic-framework-lib-first-run-wizard.component-Cp8IKDb9.mjs} +4 -4
  5. package/fesm2022/{wuic-framework-lib-framework-docs.component-B6qNTs5H.mjs → wuic-framework-lib-framework-docs.component-CxckBsIX.mjs} +406 -10
  6. package/fesm2022/{wuic-framework-lib-pivot-builder.component-DMk26H0M.mjs → wuic-framework-lib-pivot-builder.component-C-ceJi48.mjs} +10 -10
  7. package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-y3shST6g.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-DPZCgxOB.mjs} +3 -3
  8. package/fesm2022/{wuic-framework-lib-report-designer.component-BnngR2eL.mjs → wuic-framework-lib-report-designer.component-3-hUYft8.mjs} +2 -2
  9. package/fesm2022/{wuic-framework-lib-report-viewer.component-LGNRIQh2.mjs → wuic-framework-lib-report-viewer.component-D4QD_wPl.mjs} +2 -2
  10. package/fesm2022/{wuic-framework-lib-scheduler-list.component-DbcFMwx5.mjs → wuic-framework-lib-scheduler-list.component-C0nY8Wd6.mjs} +2 -2
  11. package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-DXyRX0YK.mjs → wuic-framework-lib-spreadsheet-list-sf.component-C9sWgq61.mjs} +443 -4
  12. package/fesm2022/{wuic-framework-lib-workflow-designer.component-Bm719AWV.mjs → wuic-framework-lib-workflow-designer.component-8mv_Rbz9.mjs} +4 -4
  13. package/fesm2022/{wuic-framework-lib-wuic-framework-lib-CGCZvtKK.mjs → wuic-framework-lib-wuic-framework-lib-DJZAYFw8.mjs} +4887 -3819
  14. package/fesm2022/wuic-framework-lib.mjs +1 -1
  15. package/package.json +1 -1
  16. package/types/wuic-framework-lib.d.ts +1575 -1005
@@ -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-CGCZvtKK.mjs';
10
+ import { W as WtoolboxService, M as MetadataProviderService, U as UserInfoService, b as DataSourceComponent, I as ImportExportButtonComponent } from './wuic-framework-lib-wuic-framework-lib-DJZAYFw8.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
@@ -3845,8 +3954,320 @@ class SpreadsheetListSfComponent {
3845
3954
  onWindowResize() {
3846
3955
  this.scheduleLayoutPass();
3847
3956
  }
3957
+ // ============================================================
3958
+ // Metadata-driven formulas (Sprint 9.4)
3959
+ // ------------------------------------------------------------
3960
+ // Le formule sono definite dichiarativamente in metadata route:
3961
+ // _metadati__tabelle.mdpropsbag → archetypes.spreadsheet.formulas
3962
+ // Shape:
3963
+ // {
3964
+ // "<target_column_name>": "=<expression with {col_name} tokens>",
3965
+ // "balance": "={planned}-{actual}",
3966
+ // "variance_pct": "=IF({planned}=0,0,({actual}/{planned})*100)",
3967
+ // "remaining": "={planned}-{actual}-{committed}"
3968
+ // }
3969
+ // Pattern:
3970
+ // - {col_name} viene sostituito a runtime con l'indirizzo cella Syncfusion
3971
+ // della colonna corrispondente sulla riga corrente (es. row 2 → C2, D2)
3972
+ // - L'engine Syncfusion (enableCalculation=true) ricalcola i valori automaticamente
3973
+ // - Quando user modifica una cella source, le celle dipendenti si aggiornano
3974
+ // - On save (cellSave event), il VALUE COMPUTED viene persistito su DB,
3975
+ // la formula non viene memorizzata (resta nei metadati, ri-applicata al refresh)
3976
+ // ============================================================
3977
+ /**
3978
+ * Risolve la lista colonne visibili (idx → col name) dal metaInfo corrente.
3979
+ * Indice 0-based; il caller mappa a column letter A/B/C tramite indexToColumnLetter().
3980
+ */
3981
+ getVisibleColumnNames() {
3982
+ if (!this.metaInfo?.columnMetadata)
3983
+ return [];
3984
+ return this.metaInfo.columnMetadata
3985
+ .filter(c => !c.mc_hide_in_edit)
3986
+ .map(c => c.mc_nome_colonna || '');
3987
+ }
3988
+ /** Converte indice 0-based in letter A1-notation (0 → 'A', 25 → 'Z', 26 → 'AA'). */
3989
+ indexToColumnLetter(idx) {
3990
+ let s = '';
3991
+ let n = idx;
3992
+ while (n >= 0) {
3993
+ s = String.fromCharCode(65 + (n % 26)) + s;
3994
+ n = Math.floor(n / 26) - 1;
3995
+ }
3996
+ return s;
3997
+ }
3998
+ /**
3999
+ * Risolve il blocco formulas da `metaInfo.tableMetadata.md_props_bag.archetypes.spreadsheet.formulas`.
4000
+ * Ritorna `{}` se non configurato.
4001
+ */
4002
+ getMetadataFormulas() {
4003
+ const tm = this.metaInfo?.tableMetadata;
4004
+ if (!tm)
4005
+ return {};
4006
+ const pb = tm.md_props_bag || tm.mdpropsbag || tm.extraProps?.md_props_bag;
4007
+ const arch = pb?.archetypes?.spreadsheet;
4008
+ const formulas = arch?.formulas;
4009
+ if (!formulas || typeof formulas !== 'object')
4010
+ return {};
4011
+ return formulas;
4012
+ }
4013
+ /**
4014
+ * Applica le formule metadata-driven a tutte le righe correntemente caricate.
4015
+ * Chiamato post-dataBound. Usa Syncfusion `updateCell({formula: '=...'}, address)`.
4016
+ * Idempotente: ri-chiamabile (sovrascrive la formula con la stessa).
4017
+ */
4018
+ applyMetadataFormulas() {
4019
+ const formulas = this.getMetadataFormulas();
4020
+ if (!formulas || Object.keys(formulas).length === 0)
4021
+ return;
4022
+ if (!this.sfSpreadsheet)
4023
+ return;
4024
+ const visibleCols = this.getVisibleColumnNames();
4025
+ if (visibleCols.length === 0)
4026
+ return;
4027
+ // Map column name → A1 letter
4028
+ const colIndex = {};
4029
+ visibleCols.forEach((name, idx) => { colIndex[name] = this.indexToColumnLetter(idx); });
4030
+ const rowOffset = 2; // Syncfusion riga 1 = header (showFieldAsHeader), dati partono da riga 2
4031
+ const rowCount = (this.pagedRecords?.length || this.records?.length || 0);
4032
+ for (const [targetCol, formulaTemplate] of Object.entries(formulas)) {
4033
+ const targetLetter = colIndex[targetCol];
4034
+ if (!targetLetter) {
4035
+ console.warn(`[wuic-spreadsheet] formula target column '${targetCol}' not visible — skipped`);
4036
+ continue;
4037
+ }
4038
+ for (let r = 0; r < rowCount; r++) {
4039
+ const sheetRow = rowOffset + r;
4040
+ // Sostituisci tutti i token {colName} con <colLetter><rowNumber>
4041
+ const resolved = formulaTemplate.replace(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g, (_m, colName) => {
4042
+ const letter = colIndex[colName];
4043
+ if (!letter) {
4044
+ console.warn(`[wuic-spreadsheet] formula token {${colName}} → column not visible, leaving as-is`);
4045
+ return `{${colName}}`;
4046
+ }
4047
+ return `${letter}${sheetRow}`;
4048
+ });
4049
+ const address = `${targetLetter}${sheetRow}`;
4050
+ try {
4051
+ this.sfSpreadsheet.updateCell?.({ formula: resolved }, address);
4052
+ }
4053
+ catch (e) {
4054
+ console.warn(`[wuic-spreadsheet] updateCell failed at ${address}:`, e);
4055
+ }
4056
+ }
4057
+ }
4058
+ }
4059
+ // ============================================================
4060
+ // Lock-awareness public API (Sprint 9.4 — generic, metadata-driven)
4061
+ // ------------------------------------------------------------
4062
+ // Tutta la config (endpoint base, primaryScopeField, required fields,
4063
+ // heartbeat interval, auto-enable) viene letta da
4064
+ // `metaInfo.tableMetadata.md_props_bag.archetypes.spreadsheet.lock`.
4065
+ // L'@Input `lockScope` (Record<string,any>) e' il dictionary generico
4066
+ // dei valori di lock (es. `{program_id:1, year:2026, scenario_id:7}`),
4067
+ // passato dalla lazy-wrapper a partire dai query param della URL.
4068
+ // ============================================================
4069
+ /**
4070
+ * Estrae la config lock da `mdpropsbag.archetypes.spreadsheet.lock`.
4071
+ * Defaults conservativi se la config manca.
4072
+ */
4073
+ getLockConfig() {
4074
+ const pb = this.metaInfo?.tableMetadata?.md_props_bag
4075
+ || this.metaInfo?.tableMetadata?.mdpropsbag
4076
+ || {};
4077
+ const arch = pb?.archetypes?.spreadsheet?.lock || {};
4078
+ return {
4079
+ endpointBase: typeof arch.endpointBase === 'string' && arch.endpointBase.length > 0
4080
+ ? arch.endpointBase
4081
+ : '/api/spreadsheet',
4082
+ primaryScopeField: typeof arch.primaryScopeField === 'string' && arch.primaryScopeField.length > 0
4083
+ ? arch.primaryScopeField
4084
+ : null,
4085
+ requiredScopeFields: Array.isArray(arch.requiredScopeFields)
4086
+ ? arch.requiredScopeFields.filter((s) => typeof s === 'string')
4087
+ : (arch.primaryScopeField ? [arch.primaryScopeField] : []),
4088
+ autoEnableOnScope: arch.autoEnableOnScope === true,
4089
+ heartbeatIntervalSec: typeof this.heartbeatIntervalSec === 'number' && this.heartbeatIntervalSec > 0
4090
+ ? this.heartbeatIntervalSec
4091
+ : (typeof arch.heartbeatIntervalSec === 'number' && arch.heartbeatIntervalSec > 0
4092
+ ? arch.heartbeatIntervalSec
4093
+ : 60),
4094
+ };
4095
+ }
4096
+ /**
4097
+ * Valida che lo scope contenga tutti i required fields.
4098
+ * Ritorna lista dei missing (vuota = scope valido).
4099
+ */
4100
+ validateLockScope(cfg) {
4101
+ return cfg.requiredScopeFields.filter((f) => this.lockScope?.[f] == null);
4102
+ }
4103
+ /**
4104
+ * Trigger auto-acquire post-dataBound (quando metaInfo e' loaded). Idempotente:
4105
+ * non riacquista se gia' c'e' un lockToken attivo. Chiamabile manualmente.
4106
+ * Condizioni per auto-acquire:
4107
+ * - lockToken non gia' attivo
4108
+ * - mdpropsbag.archetypes.spreadsheet.lock.autoEnableOnScope === true
4109
+ * - tutti i requiredScopeFields presenti in lockScope
4110
+ */
4111
+ tryAutoAcquireLockFromMetadata() {
4112
+ if (this.lockToken)
4113
+ return;
4114
+ const cfg = this.getLockConfig();
4115
+ if (!cfg.autoEnableOnScope)
4116
+ return;
4117
+ const missing = this.validateLockScope(cfg);
4118
+ if (missing.length > 0) {
4119
+ console.info(`[wuic-spreadsheet] auto-acquire skipped — missing scope: ${missing.join(', ')}`);
4120
+ return;
4121
+ }
4122
+ this.enableLockAwareness = true;
4123
+ void this.acquireLock();
4124
+ }
4125
+ /**
4126
+ * Acquisisce lock pessimistic su `lockScope`. Endpoint:
4127
+ * POST {endpointBase}/lock-range/{lockScope[primaryScopeField]} body = lockScope
4128
+ * Emette onLockAcquired (acquired|refreshed) o onLockConflict.
4129
+ * Auto-avvia heartbeat timer se outcome=acquired|refreshed.
4130
+ */
4131
+ async acquireLock() {
4132
+ const cfg = this.getLockConfig();
4133
+ if (!cfg.primaryScopeField) {
4134
+ console.warn('[wuic-spreadsheet] acquireLock skipped: primaryScopeField non configurato in mdpropsbag.archetypes.spreadsheet.lock');
4135
+ return { outcome: 'skipped' };
4136
+ }
4137
+ const primaryVal = this.lockScope?.[cfg.primaryScopeField];
4138
+ if (primaryVal == null) {
4139
+ console.warn(`[wuic-spreadsheet] acquireLock skipped: lockScope[${cfg.primaryScopeField}] non valorizzato`);
4140
+ return { outcome: 'skipped' };
4141
+ }
4142
+ const missing = this.validateLockScope(cfg);
4143
+ if (missing.length > 0) {
4144
+ console.warn(`[wuic-spreadsheet] acquireLock skipped: missing required scope fields: ${missing.join(', ')}`);
4145
+ return { outcome: 'skipped' };
4146
+ }
4147
+ const url = `${cfg.endpointBase}/lock-range/${encodeURIComponent(String(primaryVal))}`;
4148
+ // Body = lockScope MENO il primaryScopeField (gia' in URL path)
4149
+ const body = {};
4150
+ for (const [k, v] of Object.entries(this.lockScope || {})) {
4151
+ if (k === cfg.primaryScopeField)
4152
+ continue;
4153
+ if (v == null)
4154
+ continue;
4155
+ body[k] = v;
4156
+ }
4157
+ try {
4158
+ const resp = await this.http.post(url, body, { withCredentials: true }).toPromise();
4159
+ if (!resp?.ok) {
4160
+ if (resp?.outcome === 'conflict') {
4161
+ this.onLockConflict.emit({
4162
+ conflictUserId: resp.conflictUserId ?? null,
4163
+ lockExpiresUtc: resp.lockExpiresUtc ? new Date(resp.lockExpiresUtc) : null,
4164
+ });
4165
+ return { outcome: 'conflict', conflictUserId: resp.conflictUserId };
4166
+ }
4167
+ return { outcome: resp?.outcome ?? 'failed' };
4168
+ }
4169
+ this.lockToken = resp.lockToken;
4170
+ this.lockId = resp.lockId;
4171
+ this.lockExpiresUtc = new Date(resp.lockExpiresUtc);
4172
+ this.onLockAcquired.emit({
4173
+ lockToken: resp.lockToken,
4174
+ lockId: resp.lockId,
4175
+ lockExpiresUtc: this.lockExpiresUtc,
4176
+ outcome: resp.outcome,
4177
+ });
4178
+ this.startHeartbeat(cfg.heartbeatIntervalSec);
4179
+ return { outcome: resp.outcome, lockToken: resp.lockToken };
4180
+ }
4181
+ catch (e) {
4182
+ console.error('[wuic-spreadsheet] acquireLock failed', e);
4183
+ return { outcome: 'error' };
4184
+ }
4185
+ }
4186
+ /**
4187
+ * Single heartbeat (extends TTL). Auto-called dal timer ogni heartbeatIntervalSec.
4188
+ * Emette onLockExpired se backend ritorna 410 Gone.
4189
+ */
4190
+ async heartbeat() {
4191
+ if (!this.lockToken)
4192
+ return false;
4193
+ const cfg = this.getLockConfig();
4194
+ try {
4195
+ const resp = await this.http.post(`${cfg.endpointBase}/heartbeat`, { lockToken: this.lockToken }, { withCredentials: true }).toPromise();
4196
+ if (resp?.ok) {
4197
+ this.lockExpiresUtc = new Date(resp.lockExpiresUtc);
4198
+ return true;
4199
+ }
4200
+ this.onLockExpired.emit();
4201
+ this.stopHeartbeat();
4202
+ return false;
4203
+ }
4204
+ catch (e) {
4205
+ if (e?.status === 410) {
4206
+ this.onLockExpired.emit();
4207
+ this.stopHeartbeat();
4208
+ }
4209
+ console.warn('[wuic-spreadsheet] heartbeat failed', e?.status ?? e);
4210
+ return false;
4211
+ }
4212
+ }
4213
+ /**
4214
+ * Batch save delle modifiche (alternativa al CRUD-per-row standard).
4215
+ * Useful per applicare copy/paste range o bulk edit con singolo round-trip.
4216
+ * @param changes array di { factsId, field, newValue }
4217
+ */
4218
+ async saveCellsBatch(changes) {
4219
+ if (!this.lockToken) {
4220
+ console.warn('[wuic-spreadsheet] saveCellsBatch: nessun lockToken — call acquireLock() first');
4221
+ return null;
4222
+ }
4223
+ if (!changes || changes.length === 0)
4224
+ return { applied: 0, failed: 0 };
4225
+ const cfg = this.getLockConfig();
4226
+ try {
4227
+ const resp = await this.http.post(`${cfg.endpointBase}/save-cells`, { lockToken: this.lockToken, changes }, { withCredentials: true }).toPromise();
4228
+ return { applied: resp?.applied ?? 0, failed: resp?.failed ?? 0 };
4229
+ }
4230
+ catch (e) {
4231
+ console.error('[wuic-spreadsheet] saveCellsBatch failed', e);
4232
+ return null;
4233
+ }
4234
+ }
4235
+ /**
4236
+ * Rilascia lock esplicitamente. Idempotente. Stop heartbeat timer.
4237
+ * Auto-chiamato in ngOnDestroy.
4238
+ */
4239
+ async releaseLock() {
4240
+ if (!this.lockToken)
4241
+ return 0;
4242
+ this.stopHeartbeat();
4243
+ const tok = this.lockToken;
4244
+ this.lockToken = undefined;
4245
+ this.lockExpiresUtc = undefined;
4246
+ const cfg = this.getLockConfig();
4247
+ try {
4248
+ const resp = await this.http.post(`${cfg.endpointBase}/release-lock`, { lockToken: tok }, { withCredentials: true }).toPromise();
4249
+ const released = resp?.released ?? 0;
4250
+ this.onLockReleased.emit({ released });
4251
+ return released;
4252
+ }
4253
+ catch (e) {
4254
+ console.warn('[wuic-spreadsheet] releaseLock failed', e);
4255
+ return 0;
4256
+ }
4257
+ }
4258
+ startHeartbeat(intervalSec = 60) {
4259
+ this.stopHeartbeat();
4260
+ const intervalMs = Math.max(15, intervalSec) * 1000;
4261
+ this.heartbeatTimer = setInterval(() => { void this.heartbeat(); }, intervalMs);
4262
+ }
4263
+ stopHeartbeat() {
4264
+ if (this.heartbeatTimer) {
4265
+ clearInterval(this.heartbeatTimer);
4266
+ this.heartbeatTimer = undefined;
4267
+ }
4268
+ }
3848
4269
  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" }] });
4270
+ 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
4271
  }
3851
4272
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpreadsheetListSfComponent, decorators: [{
3852
4273
  type: Component,
@@ -3857,7 +4278,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3857
4278
  ButtonModule,
3858
4279
  TranslateModule,
3859
4280
  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"] }]
4281
+ ], 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
4282
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.Title }, { type: i1$1.ActivatedRoute }, { type: i1$2.HttpClient }, { type: WtoolboxService }, { type: MetadataProviderService }, { type: UserInfoService }], propDecorators: { sfSpreadsheet: [{
3862
4283
  type: ViewChild,
3863
4284
  args: ['sfSpreadsheet']
@@ -3876,6 +4297,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3876
4297
  type: Input
3877
4298
  }], hideToolbar: [{
3878
4299
  type: Input
4300
+ }], enableLockAwareness: [{
4301
+ type: Input
4302
+ }], lockScope: [{
4303
+ type: Input
4304
+ }], heartbeatIntervalSec: [{
4305
+ type: Input
4306
+ }], showFormulaBar: [{
4307
+ type: Input
4308
+ }], showRibbon: [{
4309
+ type: Input
3879
4310
  }], onSpreadsheetDataBound: [{
3880
4311
  type: Output
3881
4312
  }], onSpreadsheetBeforePageChange: [{
@@ -3888,10 +4319,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3888
4319
  type: Output
3889
4320
  }], onSpreadsheetBatchSaved: [{
3890
4321
  type: Output
4322
+ }], onLockAcquired: [{
4323
+ type: Output
4324
+ }], onLockConflict: [{
4325
+ type: Output
4326
+ }], onLockReleased: [{
4327
+ type: Output
4328
+ }], onLockExpired: [{
4329
+ type: Output
3891
4330
  }], onWindowResize: [{
3892
4331
  type: HostListener,
3893
4332
  args: ['window:resize']
3894
4333
  }] } });
3895
4334
 
3896
4335
  export { SpreadsheetListSfComponent };
3897
- //# sourceMappingURL=wuic-framework-lib-spreadsheet-list-sf.component-DXyRX0YK.mjs.map
4336
+ //# sourceMappingURL=wuic-framework-lib-spreadsheet-list-sf.component-C9sWgq61.mjs.map