@massimo-cassandro/minimo 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -354,28 +354,43 @@ Ricerca senza risultati:
|
|
|
354
354
|
|
|
355
355
|
Il testo si aggiorna automaticamente ad ogni cambio pagina, ricerca e multisearch.
|
|
356
356
|
|
|
357
|
-
##
|
|
357
|
+
## Parametri `topSlot` e `showInfoAtTop`
|
|
358
358
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
o qualsiasi controllo da affiancare alla search box.
|
|
359
|
+
Entrambi occupano l'area in alto a sinistra del datatable — lo spazio normalmente
|
|
360
|
+
riservato al selettore "righe per pagina" (qui disabilitato). I due parametri si
|
|
361
|
+
escludono: se `showInfoAtTop` è `true`, `topSlot` viene ignorato.
|
|
363
362
|
|
|
364
|
-
|
|
365
|
-
come primo figlio dell'area superiore, prima della search box.
|
|
363
|
+
### `showInfoAtTop`
|
|
366
364
|
|
|
367
|
-
|
|
368
|
-
|
|
365
|
+
Replica nell'area superiore lo stesso testo dell'area `.info` in basso,
|
|
366
|
+
mantenuto in sincronia ad ogni cambio pagina o filtro. Il nodo viene nascosto
|
|
367
|
+
automaticamente quando non ci sono record o la ricerca non produce risultati
|
|
368
|
+
(evita di mostrare "Nessun risultato…" in due posti).
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
el.init({
|
|
372
|
+
json: '/api/data.json',
|
|
373
|
+
cols: [...],
|
|
374
|
+
showInfoAtTop: true,
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### `topSlot`
|
|
379
|
+
|
|
380
|
+
Inserisce un elemento DOM o markup HTML arbitrario, avvolto in un
|
|
381
|
+
`<div class="datatable-top-slot">`. Accetta sia una stringa HTML che un `Element`
|
|
382
|
+
già costruito (utile per mantenere un riferimento al nodo e aggiungervi listener).
|
|
383
|
+
Non è leggibile da attributo HTML, solo via `init()`.
|
|
369
384
|
|
|
370
385
|
```javascript
|
|
371
386
|
// Stringa HTML
|
|
372
387
|
el.init({
|
|
373
388
|
json: '/api/data.json',
|
|
374
389
|
cols: [...],
|
|
375
|
-
topSlot: '<button class="btn btn-sm btn-primary"
|
|
390
|
+
topSlot: '<button class="btn btn-sm btn-primary">Esporta CSV</button>',
|
|
376
391
|
});
|
|
377
392
|
|
|
378
|
-
// Elemento DOM
|
|
393
|
+
// Elemento DOM
|
|
379
394
|
const btn = document.createElement('button');
|
|
380
395
|
btn.className = 'btn btn-sm btn-outline-secondary';
|
|
381
396
|
btn.textContent = 'Nuova commessa';
|
|
@@ -114,8 +114,17 @@ import caretRightIcon from '../../icons/caret-right.svg?inline';
|
|
|
114
114
|
* Accetta:
|
|
115
115
|
* - `Element`: un nodo DOM già costruito
|
|
116
116
|
* - stringa HTML: parsata via innerHTML
|
|
117
|
+
* Ignorato se `showInfoAtTop` è true.
|
|
117
118
|
* Non è leggibile da attributo HTML (solo via script).
|
|
118
119
|
*
|
|
120
|
+
* @param {boolean} showInfoAtTop Se true, mostra nell'area superiore (spazio top-sinistra)
|
|
121
|
+
* lo stesso testo dell'area `.info` in basso, mantenuto in
|
|
122
|
+
* sync ad ogni cambio pagina/filtro. Il nodo viene nascosto
|
|
123
|
+
* automaticamente quando non ci sono record o la ricerca
|
|
124
|
+
* non produce risultati. Ha precedenza su `topSlot`:
|
|
125
|
+
* se entrambi sono definiti, `topSlot` viene ignorato.
|
|
126
|
+
* Default: false.
|
|
127
|
+
*
|
|
119
128
|
* @param {string[]} refs Elenco di percorsi URL da cui, se si proviene,
|
|
120
129
|
* la pagina corrente viene ripristinata dal cookie
|
|
121
130
|
* di sessione `sd-pag`. Se il referrer non è tra
|
|
@@ -760,24 +769,25 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
760
769
|
});
|
|
761
770
|
|
|
762
771
|
this._dt.on('datatable.init', () => {
|
|
772
|
+
// ── Icone paginazione ─────────────────────────────────────────────────
|
|
763
773
|
const pagination = this.querySelector(`.${styles.paginationList}`);
|
|
764
|
-
if (
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
774
|
+
if (pagination) {
|
|
775
|
+
const items = pagination.querySelectorAll(`.${styles.paginationListItem}`);
|
|
776
|
+
if (items.length) {
|
|
777
|
+
pagination.querySelector(
|
|
778
|
+
`.${styles.paginationListItem}:first-child .${styles.paginationListItemBtn}`
|
|
779
|
+
).innerHTML = caretLeftIcon;
|
|
780
|
+
|
|
781
|
+
pagination.querySelector(
|
|
782
|
+
`.${styles.paginationListItem}:last-child .${styles.paginationListItemBtn}`
|
|
783
|
+
).innerHTML = caretRightIcon;
|
|
784
|
+
|
|
785
|
+
pagination.querySelectorAll(`.${styles.paginationListItemBtn}:has(svg)`)
|
|
786
|
+
.forEach(btn => btn.classList.add(styles.hasSvg));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
779
789
|
|
|
780
|
-
// Ripristino pagina dal cookie di sessione (se referrer autorizzato)
|
|
790
|
+
// ── Ripristino pagina dal cookie di sessione (se referrer autorizzato) ─
|
|
781
791
|
// const isRefUrl = this._isReferrer(), refsCallback = this._getParam('refsCallback', null);
|
|
782
792
|
// if (isRefUrl) {
|
|
783
793
|
if (this._isReferrer()) {
|
|
@@ -793,6 +803,7 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
793
803
|
// refsCallback(isRefUrl);
|
|
794
804
|
// }
|
|
795
805
|
|
|
806
|
+
// ── Filtro contestuale post-reload ────────────────────────────────────
|
|
796
807
|
// Applica eventuale filtro contestuale passato via reload({ search: … })
|
|
797
808
|
// Lo facciamo qui, dentro datatable.init, per essere certi che
|
|
798
809
|
// simple-datatables abbia completato il render dei nuovi dati.
|
|
@@ -807,14 +818,11 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
807
818
|
}
|
|
808
819
|
}
|
|
809
820
|
|
|
810
|
-
//
|
|
821
|
+
// ── Tfoot, info, top slot ─────────────────────────────────────────────
|
|
822
|
+
// Eseguiti sempre, indipendentemente dalla presenza della paginazione.
|
|
811
823
|
this._renderTfoot();
|
|
812
|
-
|
|
813
|
-
// Aggiorna il testo info con il totale assoluto
|
|
824
|
+
this._injectTopSlot(); // va prima di _updateInfo per creare il nodo top-info
|
|
814
825
|
this._updateInfo();
|
|
815
|
-
|
|
816
|
-
// Inietta l'elemento nello slot top-sinistra (se configurato)
|
|
817
|
-
this._injectTopSlot();
|
|
818
826
|
});
|
|
819
827
|
|
|
820
828
|
// Salva la pagina corrente nel cookie ad ogni cambio di pagina;
|
|
@@ -927,84 +935,123 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
927
935
|
/**
|
|
928
936
|
* Sovrascrive il testo dell'area `.info` generato da simple-datatables per
|
|
929
937
|
* includere sempre il totale assoluto dei record (indipendente dal filtro).
|
|
938
|
+
* Se `showInfoAtTop` è attivo, aggiorna in sincronia anche il nodo `.info`
|
|
939
|
+
* nell'area superiore (creato da `_injectTopSlot`).
|
|
940
|
+
*
|
|
941
|
+
* Calcolo di start/end: si conta il numero di righe renderizzate nel tbody
|
|
942
|
+
* (`dt.pages[currentPage - 1]`) invece di fare aritmetica su perPage,
|
|
943
|
+
* perché `dt.currentPage` può essere undefined al primo render.
|
|
930
944
|
*
|
|
931
945
|
* Comportamento:
|
|
932
946
|
* - Senza ricerca attiva:
|
|
933
947
|
* "Stai visualizzando le righe da {start} a {end}, su un totale di {total}."
|
|
934
|
-
* - Con ricerca attiva:
|
|
935
|
-
* "Stai visualizzando le righe da {start} a {end} dei {filtered} record
|
|
936
|
-
* su un totale di {total}."
|
|
948
|
+
* - Con ricerca attiva (risultati > 0):
|
|
949
|
+
* "Stai visualizzando le righe da {start} a {end} dei {filtered} record
|
|
950
|
+
* filtrati, su un totale di {total}."
|
|
937
951
|
* - Nessun risultato (ricerca attiva, 0 match):
|
|
938
952
|
* "Nessun risultato per la ricerca corrente (totale record: {total})."
|
|
939
953
|
*
|
|
940
|
-
*
|
|
941
|
-
* datatable.multisearch.
|
|
954
|
+
* Il nodo top-info viene nascosto quando filtered = 0 o total = 0.
|
|
942
955
|
*/
|
|
943
956
|
_updateInfo() {
|
|
944
|
-
const infoEl
|
|
945
|
-
|
|
957
|
+
const infoEl = this.querySelector(`.${styles.info}`);
|
|
958
|
+
const topInfoEl = this.querySelector('.datatable-top-info');
|
|
946
959
|
|
|
947
|
-
|
|
948
|
-
const total = this._rawData?.length ?? 0;
|
|
960
|
+
if (!infoEl && !topInfoEl) return;
|
|
949
961
|
|
|
950
|
-
|
|
962
|
+
const dt = this._dt;
|
|
963
|
+
const total = this._rawData?.length ?? 0;
|
|
951
964
|
const filtered = dt.searching ? dt.searchData.length : total;
|
|
952
965
|
|
|
953
|
-
|
|
954
|
-
|
|
966
|
+
let text;
|
|
967
|
+
|
|
968
|
+
if (total === 0 || (filtered === 0 && dt.searching)) {
|
|
969
|
+
// Nessun risultato — nasconde il nodo top-info se presente
|
|
970
|
+
if (topInfoEl) topInfoEl.hidden = true;
|
|
971
|
+
|
|
972
|
+
text = filtered === 0 && dt.searching
|
|
973
|
+
? `Nessun risultato per la ricerca corrente (totale record: ${total}).`
|
|
974
|
+
: `Nessun record trovato.`;
|
|
975
|
+
|
|
976
|
+
if (infoEl) infoEl.textContent = text;
|
|
955
977
|
return;
|
|
956
978
|
}
|
|
957
979
|
|
|
958
|
-
// Calcola start/end
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
const
|
|
962
|
-
const
|
|
980
|
+
// Calcola start/end dalla pagina corrente in dt.pages (più affidabile di
|
|
981
|
+
// currentPage * perPage, che può dare NaN se currentPage è undefined
|
|
982
|
+
// al primo render oppure quando non c'è paginazione).
|
|
983
|
+
const currentPage = dt.currentPage ?? 1;
|
|
984
|
+
const pageRows = dt.pages?.[currentPage - 1] ?? [];
|
|
985
|
+
const perPage = dt.options?.perPage ?? filtered;
|
|
986
|
+
const start = pageRows.length > 0
|
|
987
|
+
? (currentPage - 1) * perPage + 1
|
|
988
|
+
: 1;
|
|
989
|
+
const end = pageRows.length > 0
|
|
990
|
+
? start + pageRows.length - 1
|
|
991
|
+
: filtered;
|
|
963
992
|
|
|
964
993
|
if (dt.searching) {
|
|
965
|
-
|
|
994
|
+
text =
|
|
966
995
|
`Stai visualizzando le righe da ${start} a ${end} ` +
|
|
967
996
|
`dei ${filtered} record filtrati, su un totale di ${total}.`;
|
|
968
997
|
} else {
|
|
969
|
-
|
|
998
|
+
text =
|
|
970
999
|
`Stai visualizzando le righe da ${start} a ${end}, ` +
|
|
971
1000
|
`su un totale di ${total}.`;
|
|
972
1001
|
}
|
|
1002
|
+
|
|
1003
|
+
if (infoEl) infoEl.textContent = text;
|
|
1004
|
+
if (topInfoEl) { topInfoEl.textContent = text; topInfoEl.hidden = false; }
|
|
973
1005
|
}
|
|
974
1006
|
|
|
975
1007
|
|
|
976
1008
|
// ─── Top slot ────────────────────────────────────────────────────────────────
|
|
977
1009
|
|
|
978
1010
|
/**
|
|
979
|
-
* Inietta
|
|
980
|
-
* del datatable — lo spazio normalmente occupato dal selettore "righe per
|
|
981
|
-
* pagina" (qui disabilitato con `perPageSelect: false`).
|
|
1011
|
+
* Inietta nell'area in alto a sinistra del datatable il contenuto configurato.
|
|
982
1012
|
*
|
|
983
|
-
*
|
|
984
|
-
* - un `Element` (già costruito fuori dal componente)
|
|
985
|
-
* - una stringa HTML (viene parsata con `innerHTML`)
|
|
1013
|
+
* Modalità:
|
|
986
1014
|
*
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
*
|
|
1015
|
+
* showInfoAtTop: true (ha precedenza su topSlot)
|
|
1016
|
+
* Inserisce un nodo `.datatable-top-info` con la stessa stringa dell'area
|
|
1017
|
+
* `.info` in basso, mantenuta in sync da `_updateInfo`. Viene nascosto
|
|
1018
|
+
* automaticamente quando filtered = 0 o total = 0.
|
|
1019
|
+
* Il parametro `topSlot`, se presente, viene ignorato.
|
|
990
1020
|
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
*
|
|
1021
|
+
* topSlot: Element | string (usato solo se showInfoAtTop è false/assente)
|
|
1022
|
+
* Inserisce un elemento DOM o markup HTML arbitrario, avvolto in un
|
|
1023
|
+
* `<div class="datatable-top-slot">`.
|
|
1024
|
+
*
|
|
1025
|
+
* In entrambi i casi l'elemento viene inserito come primo figlio di `.topArea`,
|
|
1026
|
+
* prima della search box.
|
|
1027
|
+
*
|
|
1028
|
+
* Esempi:
|
|
1029
|
+
* el.init({ showInfoAtTop: true });
|
|
1030
|
+
*
|
|
1031
|
+
* el.init({ topSlot: '<button class="btn btn-sm btn-primary">Esporta</button>' });
|
|
995
1032
|
*
|
|
996
|
-
* Esempio – elemento DOM:
|
|
997
1033
|
* const btn = document.createElement('button');
|
|
998
|
-
* btn.textContent = '
|
|
1034
|
+
* btn.textContent = 'Nuovo';
|
|
999
1035
|
* el.init({ topSlot: btn });
|
|
1000
1036
|
*/
|
|
1001
1037
|
_injectTopSlot() {
|
|
1002
|
-
const topSlot = this._getParam('topSlot', null);
|
|
1003
|
-
if (topSlot == null) return;
|
|
1004
|
-
|
|
1005
1038
|
const topArea = this.querySelector(`.${styles.topArea}`);
|
|
1006
1039
|
if (!topArea) return;
|
|
1007
1040
|
|
|
1041
|
+
const showInfoAtTop = this._getParam('showInfoAtTop', false);
|
|
1042
|
+
|
|
1043
|
+
if (showInfoAtTop) {
|
|
1044
|
+
// Crea il nodo info top (il testo verrà scritto da _updateInfo)
|
|
1045
|
+
const infoTop = document.createElement('div');
|
|
1046
|
+
infoTop.className = `datatable-top-info ${styles.info}`;
|
|
1047
|
+
infoTop.hidden = true; // nascosto finché _updateInfo non lo popola
|
|
1048
|
+
topArea.insertBefore(infoTop, topArea.firstChild);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const topSlot = this._getParam('topSlot', null);
|
|
1053
|
+
if (topSlot == null) return;
|
|
1054
|
+
|
|
1008
1055
|
const wrapper = document.createElement('div');
|
|
1009
1056
|
wrapper.className = 'datatable-top-slot';
|
|
1010
1057
|
|
|
@@ -1014,7 +1061,6 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
1014
1061
|
wrapper.innerHTML = topSlot;
|
|
1015
1062
|
}
|
|
1016
1063
|
|
|
1017
|
-
// Inserisce prima della search box (primo figlio esistente)
|
|
1018
1064
|
topArea.insertBefore(wrapper, topArea.firstChild);
|
|
1019
1065
|
}
|
|
1020
1066
|
|