@esfaenza/es-table 20.3.18 → 20.3.20

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.
@@ -6214,10 +6214,14 @@ class EsTable2PagerComponent {
6214
6214
  </label>
6215
6215
 
6216
6216
  <div class="est2-pager__nav" *ngIf="showButtons() && pages() > 1">
6217
- <button type="button" class="est2-pager__btn"
6217
+ <button type="button" class="est2-pager__btn est2-pager__btn--nav"
6218
6218
  [disabled]="page()! <= 1"
6219
6219
  (click)="goto(page()! - 1)"
6220
- aria-label="Pagina precedente">‹</button>
6220
+ aria-label="Pagina precedente">
6221
+ <svg class="est2-pager__chev" viewBox="0 0 24 24" aria-hidden="true">
6222
+ <path d="M15 5l-7 7 7 7" />
6223
+ </svg>
6224
+ </button>
6221
6225
 
6222
6226
  @for (p of visiblePages(); track p.key) {
6223
6227
  @if (p.page === -1) {
@@ -6230,13 +6234,17 @@ class EsTable2PagerComponent {
6230
6234
  }
6231
6235
  }
6232
6236
 
6233
- <button type="button" class="est2-pager__btn"
6237
+ <button type="button" class="est2-pager__btn est2-pager__btn--nav"
6234
6238
  [disabled]="page()! >= pages()"
6235
6239
  (click)="goto(page()! + 1)"
6236
- aria-label="Pagina successiva">›</button>
6240
+ aria-label="Pagina successiva">
6241
+ <svg class="est2-pager__chev" viewBox="0 0 24 24" aria-hidden="true">
6242
+ <path d="M9 5l7 7-7 7" />
6243
+ </svg>
6244
+ </button>
6237
6245
  </div>
6238
6246
  </div>
6239
- `, isInline: true, styles: [".est2-pager{--est-ctrl-h: 30px;display:flex;align-items:center;gap:12px;padding:10px 4px 2px;font-size:var(--est-fs-sm);color:var(--est-fg-muted);flex-wrap:wrap;line-height:1}.est2-pager__spacer{flex:1 1 auto}.est2-pager__count{display:inline-flex;align-items:center;height:var(--est-ctrl-h)}.est2-pager__count strong{color:var(--est-fg);font-weight:600}.est2-pager__ipp{display:inline-flex;align-items:center;height:var(--est-ctrl-h);margin:0;gap:6px}.est2-pager__select{font:inherit;height:var(--est-ctrl-h);color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);padding:0 8px;cursor:pointer;transition:border-color var(--est-transition)}.est2-pager__select:hover{border-color:var(--est-border-strong)}.est2-pager__select:focus-visible{outline:none;box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2-pager__nav{display:inline-flex;align-items:center;gap:4px}.est2-pager__btn{display:inline-flex;align-items:center;justify-content:center;min-width:var(--est-ctrl-h);height:var(--est-ctrl-h);padding:0 8px;border:1px solid var(--est-border);border-radius:var(--est-radius-sm);background:var(--est-bg);color:var(--est-fg);font:inherit;cursor:pointer;transition:background var(--est-transition),border-color var(--est-transition),color var(--est-transition)}.est2-pager__btn:hover:not(:disabled){background:var(--est-bg-hover);border-color:var(--est-border-strong)}.est2-pager__btn:disabled{opacity:.4;cursor:default}.est2-pager__btn:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2-pager__btn--active{background:var(--est-accent);border-color:var(--est-accent);color:var(--est-accent-fg);font-weight:600}.est2-pager__btn--active:hover:not(:disabled){background:var(--est-accent)}.est2-pager__ellipsis{color:var(--est-fg-faint);padding:0 2px}\n"], dependencies: [{ kind: "directive", type: i8.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
6247
+ `, isInline: true, styles: [".est2-pager{--est-ctrl-h: 30px;display:flex;align-items:center;gap:12px;padding:10px 4px 2px;font-size:var(--est-fs);color:var(--est-fg-muted);flex-wrap:wrap;line-height:1}.est2-pager__spacer{flex:1 1 auto}.est2-pager__count{display:inline-flex;align-items:center;height:var(--est-ctrl-h)}.est2-pager__count strong{color:var(--est-fg);font-weight:600;margin-left:6px}.est2-pager__ipp{display:inline-flex;align-items:center;height:var(--est-ctrl-h);margin:0;gap:6px}.est2-pager__select{font:inherit;height:var(--est-ctrl-h);color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);padding:0 8px;cursor:pointer;transition:border-color var(--est-transition)}.est2-pager__select:hover{border-color:var(--est-border-strong)}.est2-pager__select:focus-visible{outline:none;box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2-pager__nav{display:inline-flex;align-items:center;gap:4px}.est2-pager__btn{display:inline-flex;align-items:center;justify-content:center;min-width:var(--est-ctrl-h);height:var(--est-ctrl-h);padding:0 8px;border:1px solid var(--est-border);border-radius:var(--est-radius-sm);background:var(--est-bg);color:var(--est-fg);font:inherit;cursor:pointer;transition:background var(--est-transition),border-color var(--est-transition),color var(--est-transition)}.est2-pager__btn:hover:not(:disabled){background:var(--est-bg-hover);border-color:var(--est-border-strong)}.est2-pager__btn:disabled{opacity:.4;cursor:default}.est2-pager__btn:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2-pager__btn--active{background:var(--est-accent);border-color:var(--est-accent);color:var(--est-accent-fg);font-weight:600}.est2-pager__btn--active:hover:not(:disabled){background:var(--est-accent)}.est2-pager__btn--nav{padding:0}.est2-pager__chev{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;display:block}.est2-pager__ellipsis{color:var(--est-fg-faint);padding:0 2px}\n"], dependencies: [{ kind: "directive", type: i8.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
6240
6248
  }
6241
6249
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: EsTable2PagerComponent, decorators: [{
6242
6250
  type: Component,
@@ -6260,10 +6268,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
6260
6268
  </label>
6261
6269
 
6262
6270
  <div class="est2-pager__nav" *ngIf="showButtons() && pages() > 1">
6263
- <button type="button" class="est2-pager__btn"
6271
+ <button type="button" class="est2-pager__btn est2-pager__btn--nav"
6264
6272
  [disabled]="page()! <= 1"
6265
6273
  (click)="goto(page()! - 1)"
6266
- aria-label="Pagina precedente">‹</button>
6274
+ aria-label="Pagina precedente">
6275
+ <svg class="est2-pager__chev" viewBox="0 0 24 24" aria-hidden="true">
6276
+ <path d="M15 5l-7 7 7 7" />
6277
+ </svg>
6278
+ </button>
6267
6279
 
6268
6280
  @for (p of visiblePages(); track p.key) {
6269
6281
  @if (p.page === -1) {
@@ -6276,13 +6288,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
6276
6288
  }
6277
6289
  }
6278
6290
 
6279
- <button type="button" class="est2-pager__btn"
6291
+ <button type="button" class="est2-pager__btn est2-pager__btn--nav"
6280
6292
  [disabled]="page()! >= pages()"
6281
6293
  (click)="goto(page()! + 1)"
6282
- aria-label="Pagina successiva">›</button>
6294
+ aria-label="Pagina successiva">
6295
+ <svg class="est2-pager__chev" viewBox="0 0 24 24" aria-hidden="true">
6296
+ <path d="M9 5l7 7-7 7" />
6297
+ </svg>
6298
+ </button>
6283
6299
  </div>
6284
6300
  </div>
6285
- `, styles: [".est2-pager{--est-ctrl-h: 30px;display:flex;align-items:center;gap:12px;padding:10px 4px 2px;font-size:var(--est-fs-sm);color:var(--est-fg-muted);flex-wrap:wrap;line-height:1}.est2-pager__spacer{flex:1 1 auto}.est2-pager__count{display:inline-flex;align-items:center;height:var(--est-ctrl-h)}.est2-pager__count strong{color:var(--est-fg);font-weight:600}.est2-pager__ipp{display:inline-flex;align-items:center;height:var(--est-ctrl-h);margin:0;gap:6px}.est2-pager__select{font:inherit;height:var(--est-ctrl-h);color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);padding:0 8px;cursor:pointer;transition:border-color var(--est-transition)}.est2-pager__select:hover{border-color:var(--est-border-strong)}.est2-pager__select:focus-visible{outline:none;box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2-pager__nav{display:inline-flex;align-items:center;gap:4px}.est2-pager__btn{display:inline-flex;align-items:center;justify-content:center;min-width:var(--est-ctrl-h);height:var(--est-ctrl-h);padding:0 8px;border:1px solid var(--est-border);border-radius:var(--est-radius-sm);background:var(--est-bg);color:var(--est-fg);font:inherit;cursor:pointer;transition:background var(--est-transition),border-color var(--est-transition),color var(--est-transition)}.est2-pager__btn:hover:not(:disabled){background:var(--est-bg-hover);border-color:var(--est-border-strong)}.est2-pager__btn:disabled{opacity:.4;cursor:default}.est2-pager__btn:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2-pager__btn--active{background:var(--est-accent);border-color:var(--est-accent);color:var(--est-accent-fg);font-weight:600}.est2-pager__btn--active:hover:not(:disabled){background:var(--est-accent)}.est2-pager__ellipsis{color:var(--est-fg-faint);padding:0 2px}\n"] }]
6301
+ `, styles: [".est2-pager{--est-ctrl-h: 30px;display:flex;align-items:center;gap:12px;padding:10px 4px 2px;font-size:var(--est-fs);color:var(--est-fg-muted);flex-wrap:wrap;line-height:1}.est2-pager__spacer{flex:1 1 auto}.est2-pager__count{display:inline-flex;align-items:center;height:var(--est-ctrl-h)}.est2-pager__count strong{color:var(--est-fg);font-weight:600;margin-left:6px}.est2-pager__ipp{display:inline-flex;align-items:center;height:var(--est-ctrl-h);margin:0;gap:6px}.est2-pager__select{font:inherit;height:var(--est-ctrl-h);color:var(--est-fg);background:var(--est-bg);border:1px solid var(--est-border);border-radius:var(--est-radius-sm);padding:0 8px;cursor:pointer;transition:border-color var(--est-transition)}.est2-pager__select:hover{border-color:var(--est-border-strong)}.est2-pager__select:focus-visible{outline:none;box-shadow:var(--est-ring);border-color:var(--est-accent)}.est2-pager__nav{display:inline-flex;align-items:center;gap:4px}.est2-pager__btn{display:inline-flex;align-items:center;justify-content:center;min-width:var(--est-ctrl-h);height:var(--est-ctrl-h);padding:0 8px;border:1px solid var(--est-border);border-radius:var(--est-radius-sm);background:var(--est-bg);color:var(--est-fg);font:inherit;cursor:pointer;transition:background var(--est-transition),border-color var(--est-transition),color var(--est-transition)}.est2-pager__btn:hover:not(:disabled){background:var(--est-bg-hover);border-color:var(--est-border-strong)}.est2-pager__btn:disabled{opacity:.4;cursor:default}.est2-pager__btn:focus-visible{outline:none;box-shadow:var(--est-ring)}.est2-pager__btn--active{background:var(--est-accent);border-color:var(--est-accent);color:var(--est-accent-fg);font-weight:600}.est2-pager__btn--active:hover:not(:disabled){background:var(--est-accent)}.est2-pager__btn--nav{padding:0}.est2-pager__chev{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2.25;stroke-linecap:round;stroke-linejoin:round;display:block}.est2-pager__ellipsis{color:var(--est-fg-faint);padding:0 2px}\n"] }]
6286
6302
  }], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], pages: [{ type: i0.Input, args: [{ isSignal: true, alias: "pages", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], itemsPerPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPage", required: false }] }], countLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "countLabel", required: false }] }], showCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCount", required: false }] }], showButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtons", required: false }] }], showPagingOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPagingOptions", required: false }] }], allowAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowAll", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], itemsPerPageChange: [{ type: i0.Output, args: ["itemsPerPageChange"] }] } });
6287
6303
 
6288
6304
  // Angular
@@ -6683,6 +6699,8 @@ class EsTable2Component {
6683
6699
  // --- Export (CSV self-contained / XLSX via SheetJS opzionale) --------------
6684
6700
  this.exportMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "exportMenuOpen" }] : []));
6685
6701
  this.cornerMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "cornerMenuOpen" }] : []));
6702
+ /** @ignore Evita esportazioni concorrenti (parità con l'`ExportHandler` originale) */
6703
+ this.exportInProgress = false;
6686
6704
  this.trackByIndex = (i) => i;
6687
6705
  this.trackRow = (i, item) => item?._hash ?? item?.id ?? item;
6688
6706
  this.trackCol = (_, col) => col.id;
@@ -7458,6 +7476,15 @@ class EsTable2Component {
7458
7476
  const sel = this.currentSelection();
7459
7477
  const shift = !!event?.shiftKey;
7460
7478
  const ctrl = !!(event?.ctrlKey || event?.metaKey);
7479
+ // In modalità "tutto selezionato" (selectEverything → sel.all), un Ctrl+click
7480
+ // NON deve azzerare la selezione: deve solo togliere/rimettere la singola riga
7481
+ // dalle esclusioni, mantenendo sel.all. `toggleRow` gestisce già esattamente
7482
+ // questo caso (come fa la checkbox). Senza questo ramo, il reset di sel.all/
7483
+ // exclusions in coda a questa funzione collasserebbe la selezione a 0.
7484
+ if (sel.all && ctrl && !shift) {
7485
+ this.toggleRow(item);
7486
+ return;
7487
+ }
7461
7488
  if (shift && this.selectionAnchor != null && rows.indexOf(this.selectionAnchor) >= 0) {
7462
7489
  const a = rows.indexOf(this.selectionAnchor);
7463
7490
  const b = rows.indexOf(item);
@@ -8201,7 +8228,15 @@ class EsTable2Component {
8201
8228
  // `pinned` è un'estensione retro-compatibile: se assente (prefs vecchie/originali)
8202
8229
  // resta il pin di default delle direttive.
8203
8230
  const pinned = Array.isArray(prefs.pinned) ? prefs.pinned.filter(id => currentIds.has(id)) : undefined;
8204
- this.columnPrefs = { order, hidden: [...currentIds].filter(id => !visible.includes(id)), pinned };
8231
+ // Nascondo SOLO le colonne che le preferenze conoscevano davvero (presenti
8232
+ // nell'ordinamento o nell'elenco visibili) ma non marcate come visibili.
8233
+ // Una colonna del tutto assente dalle prefs è NUOVA (aggiunta dopo il
8234
+ // salvataggio): resta visibile — `setColumns` la accoda in fondo. Diversamente
8235
+ // dall'originale (che nascondeva ogni colonna non salvata), così gli utenti non
8236
+ // "perdono" le colonne aggiunte a una tabella con preferenze già memorizzate.
8237
+ const knownToPrefs = new Set([...order, ...visible]);
8238
+ const hidden = [...currentIds].filter(id => knownToPrefs.has(id) && !visible.includes(id));
8239
+ this.columnPrefs = { order, hidden, pinned };
8205
8240
  this.setColumns(this.columns());
8206
8241
  if (prefs.orders?.length) {
8207
8242
  this.orders.set([...prefs.orders]);
@@ -8457,9 +8492,30 @@ class EsTable2Component {
8457
8492
  }
8458
8493
  export(format = 'CSV') {
8459
8494
  this.exportMenuOpen.set(false);
8460
- // Parità con l'originale: se il consumer fornisce ExportFunction gliela lascio gestire
8495
+ // Parità con l'originale (`ExportHandler.tryExport`): se il consumer fornisce
8496
+ // una ExportFunction gliela lascio gestire. La callback ha la STESSA firma
8497
+ // dell'es-table1 — `(data, type, cancel?)` — così le funzioni di export dei
8498
+ // componenti legacy funzionano invariate: `cancel === true` annulla l'export
8499
+ // (es. l'utente ha abortito il recupero dati lato server) senza scaricare nulla.
8461
8500
  if (this.ExportFunction) {
8462
- this.ExportFunction((data, _type) => this.exportData(data, format), format);
8501
+ if (this.exportInProgress) {
8502
+ this.flashNotice("Un'esportazione è già in corso, attendere.");
8503
+ return;
8504
+ }
8505
+ this.exportInProgress = true;
8506
+ try {
8507
+ this.ExportFunction((data, _type, cancel = false) => {
8508
+ this.exportInProgress = false;
8509
+ if (cancel)
8510
+ return;
8511
+ this.exportData(data, format);
8512
+ }, format);
8513
+ }
8514
+ catch (e) {
8515
+ // Se la ExportFunction lancia in modo sincrono, non lascio il guard bloccato
8516
+ this.exportInProgress = false;
8517
+ throw e;
8518
+ }
8463
8519
  return;
8464
8520
  }
8465
8521
  const matrix = this.buildExportMatrix();
@@ -8549,11 +8605,19 @@ class EsTable2Component {
8549
8605
  n++;
8550
8606
  return Math.max(1, n);
8551
8607
  }
8552
- /** Valore grezzo di una proprietà, gestendo gli oggetti "generic" con `properties` */
8608
+ /**
8609
+ * Valore grezzo di una proprietà, gestendo gli oggetti "generic" con `properties`.
8610
+ * Se la colonna non dichiara una `Property` esplicita si usa l'`id` del `*th` come
8611
+ * chiave (convenzione: l'id della colonna è il nome della proprietà) — così le
8612
+ * colonne rese solo via template `*td` (es. `*th='id'` senza `Property`) hanno
8613
+ * comunque un valore per export/copia. È un percorso di SOLA LETTURA: l'editing/
8614
+ * paste continua a richiedere una `Property` esplicita (vedi `rawCellValue`).
8615
+ */
8553
8616
  cellValue(item, col) {
8554
- if (!col.property)
8617
+ const key = col.property || col.id;
8618
+ if (!key)
8555
8619
  return null;
8556
- return item?.properties ? item.properties[col.property] : item?.[col.property];
8620
+ return item?.properties ? item.properties[key] : item?.[key];
8557
8621
  }
8558
8622
  /** Entry Multi corrispondente a una cella (`item[multiProp][multiIndex]`) */
8559
8623
  multiCell(item, col) {