@massimo-cassandro/minimo 1.29.1 → 1.29.3
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 +1 -1
- package/src/web-components/s-datatable-component/docs.md +484 -0
- package/src/web-components/s-datatable-component/s-datatable-component.js +1210 -0
- package/src/web-components/s-datatable-component/s-datatable-component.module.css +220 -0
- package/src/web-components/s-datatable-component/simple-datatable.minimo.tokens.mjs +47 -0
- package/src/web-components/s-datatable-component/src/parse-cols.js +158 -0
- package/src/web-components/s-datatable-component/src/search-method.js +84 -0
package/package.json
CHANGED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
# `s-datatable` web-component per simple-datatables
|
|
2
|
+
|
|
3
|
+
<https://fiduswriter.github.io/simple-datatables/documentation/>
|
|
4
|
+
|
|
5
|
+
## Installazione
|
|
6
|
+
|
|
7
|
+
`simple-datatables` è una **peer dependency opzionale** di minimo: va installata
|
|
8
|
+
solo nei progetti che usano questo componente.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i simple-datatables
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Utilizzo da markup
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<s-datatable
|
|
18
|
+
json="/api/data.json"
|
|
19
|
+
cols='[{"_heading":"Nome","_field":"name"}, ...]'
|
|
20
|
+
perPage="10"
|
|
21
|
+
></s-datatable>
|
|
22
|
+
|
|
23
|
+
<!-- Alternativa: dati inline via attributo (utile per SSR/Twig/Blade).
|
|
24
|
+
Se presenti entrambi, `data` ha la precedenza su `json`. -->
|
|
25
|
+
<s-datatable
|
|
26
|
+
data='[{"name":"Mario","city":"Roma"}, ...]'
|
|
27
|
+
cols='[{"_heading":"Nome","_field":"name"}, ...]'
|
|
28
|
+
perPage="10"
|
|
29
|
+
></s-datatable>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Utilizzo da script
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
const el = document.querySelector('s-datatable');
|
|
36
|
+
// oppure: const el = document.createElement('s-datatable');
|
|
37
|
+
|
|
38
|
+
el.init({
|
|
39
|
+
json: '/api/data.json',
|
|
40
|
+
cols: [{ _heading: 'Nome', _field: 'name' }, ...],
|
|
41
|
+
perPage: 10
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Alternativa: dati inline (array di oggetti) al posto di json
|
|
45
|
+
el.init({
|
|
46
|
+
data: [{ name: 'Mario', city: 'Roma' }, ...],
|
|
47
|
+
cols: [{ _heading: 'Nome', _field: 'name' }, ...],
|
|
48
|
+
});
|
|
49
|
+
// Se presenti entrambi, `data` ha la precedenza su `json`.
|
|
50
|
+
|
|
51
|
+
// Se appena creato, appendere dopo init():
|
|
52
|
+
document.body.appendChild(el);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Utilizzo misto – attributi HTML + script
|
|
56
|
+
|
|
57
|
+
I parametri non inclusi in `init()` vengono letti dall'attributo HTML corrispondente.
|
|
58
|
+
Si possono quindi separare configurazione statica (`cols`, `perPage`) nel markup
|
|
59
|
+
e dati dinamici (`json`) nello script:
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<s-datatable
|
|
63
|
+
id="table-utenti"
|
|
64
|
+
cols='[{"_heading":"Nome","_field":"name"},{"_heading":"Città","_field":"city"}]'
|
|
65
|
+
perPage="20"
|
|
66
|
+
></s-datatable>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
el.init({ json: '/api/utenti.json' }); // → json da script, cols e perPage da attributo
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Ordine di precedenza: config (script) > attributo HTML > default interno.
|
|
74
|
+
|
|
75
|
+
Per ignorare esplicitamente un attributo e ricadere sul default, passare `null` nel config:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
el.init({ json: '/api/data.json', cols: null }); // → cols = null, l'attributo viene saltato
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Reazione ad eventi esterni
|
|
82
|
+
|
|
83
|
+
Il componente ascolta tre CustomEvent che altri componenti possono emettere
|
|
84
|
+
sul `document` (o su qualsiasi antenato del componente nel DOM):
|
|
85
|
+
|
|
86
|
+
### `datatable:search`
|
|
87
|
+
|
|
88
|
+
Applica una ricerca su una o più colonne.
|
|
89
|
+
|
|
90
|
+
`detail: { term: string, columns?: number[] }`
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// Ricerca globale
|
|
94
|
+
document.dispatchEvent(new CustomEvent('datatable:search', {
|
|
95
|
+
detail: { term: 'mario' }
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
// Ricerca su colonna 0 e 2
|
|
99
|
+
document.dispatchEvent(new CustomEvent('datatable:search', {
|
|
100
|
+
detail: { term: 'mario', columns: [0, 2] }
|
|
101
|
+
}));
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `datatable:multisearch`
|
|
105
|
+
|
|
106
|
+
Applica più filtri contemporaneamente (uno per colonna).
|
|
107
|
+
|
|
108
|
+
`detail: { queries: Array<{ terms: string[], columns?: number[] }> }`
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Filtra colonna 1 per "mario" E colonna 3 per "roma"
|
|
112
|
+
document.dispatchEvent(new CustomEvent('datatable:multisearch', {
|
|
113
|
+
detail: {
|
|
114
|
+
queries: [
|
|
115
|
+
{ terms: ['mario'], columns: [1] },
|
|
116
|
+
{ terms: ['roma'], columns: [3] }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `datatable:reload`
|
|
123
|
+
|
|
124
|
+
Ricarica i dati da un nuovo URL JSON (o dallo stesso URL originale se `json` è omesso),
|
|
125
|
+
opzionalmente con nuove colonne, dati inline e/o un filtro da applicare al termine del
|
|
126
|
+
caricamento.
|
|
127
|
+
|
|
128
|
+
`detail: { json?: string, data?: Array, cols?: Array, search?: object }`
|
|
129
|
+
|
|
130
|
+
Il campo `search` permette di combinare reload e filtro in un unico evento, evitando
|
|
131
|
+
race condition tra il fetch e la ricerca. Formati supportati:
|
|
132
|
+
|
|
133
|
+
- `{ term, columns? }` → ricerca semplice
|
|
134
|
+
- `{ queries: [{terms, columns?}] }` → ricerca multipla
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// Reload stesso URL
|
|
138
|
+
document.dispatchEvent(new CustomEvent('datatable:reload', { detail: {} }));
|
|
139
|
+
|
|
140
|
+
// Nuovo endpoint + nuove colonne
|
|
141
|
+
document.dispatchEvent(new CustomEvent('datatable:reload', {
|
|
142
|
+
detail: {
|
|
143
|
+
json: '/api/data-filtered.json',
|
|
144
|
+
cols: [{ _heading: 'Nome', _field: 'name' }]
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
|
|
148
|
+
// Dati inline (senza fetch)
|
|
149
|
+
document.dispatchEvent(new CustomEvent('datatable:reload', {
|
|
150
|
+
detail: {
|
|
151
|
+
data: [{ name: 'Mario', city: 'Roma' }],
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
// Reload + filtro contestuale applicato dopo il render
|
|
156
|
+
document.dispatchEvent(new CustomEvent('datatable:reload', {
|
|
157
|
+
detail: {
|
|
158
|
+
json: '/api/utenti?anno=2024',
|
|
159
|
+
search: { term: 'mario', columns: [0] }
|
|
160
|
+
}
|
|
161
|
+
}));
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Targeting – più istanze nella stessa pagina
|
|
165
|
+
|
|
166
|
+
Aggiungere `id` al componente e `target` nel detail. Se `target` è assente,
|
|
167
|
+
l'evento viene raccolto da **tutte** le istanze.
|
|
168
|
+
|
|
169
|
+
```html
|
|
170
|
+
<s-datatable id="table-utenti" ...></s-datatable>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
document.dispatchEvent(new CustomEvent('datatable:search', {
|
|
175
|
+
detail: { term: 'mario', target: 'table-utenti' }
|
|
176
|
+
}));
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Parametro `cols`
|
|
180
|
+
|
|
181
|
+
Array di oggetti di configurazione delle colonne. Le chiavi con prefisso `_` sono
|
|
182
|
+
proprietà del componente; le restanti vengono passate direttamente a simple-datatables
|
|
183
|
+
(es. `sortable`, `searchable`, `type`, `hidden`, …).
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
cols: [
|
|
187
|
+
{
|
|
188
|
+
// ── Proprietà del componente (prefisso _) ─────────────────────────────
|
|
189
|
+
|
|
190
|
+
_field, // Chiave del campo JSON. Obbligatorio.
|
|
191
|
+
// Supporta notazione punto per valori annidati: 'owner.cognome'
|
|
192
|
+
|
|
193
|
+
_heading, // Testo dell'intestazione di colonna (contenuto del <th>). Obbligatorio.
|
|
194
|
+
|
|
195
|
+
_title, // Testo del tooltip sull'intestazione (attributo `title` dell'<abbr>).
|
|
196
|
+
|
|
197
|
+
_render, // Template HTML per il contenuto della cella.
|
|
198
|
+
// Accetta:
|
|
199
|
+
// - stringa mustache-like: i segnaposto [[chiave]] vengono sostituiti
|
|
200
|
+
// con i valori della riga (notazione punto: [[owner.nome]]).
|
|
201
|
+
// I segnaposto null usano _renderNullAs; se il padre dell'oggetto è
|
|
202
|
+
// null (es. Symfony restituisce owner: null) non viene emesso alcun
|
|
203
|
+
// warning (il dato è semplicemente assente, non un errore di config).
|
|
204
|
+
// - funzione (row) => string
|
|
205
|
+
|
|
206
|
+
_renderMode, // Preset di visualizzazione predefiniti. Valori disponibili:
|
|
207
|
+
// 'id' – colonna numerica, non ricercabile, allineata a destra
|
|
208
|
+
// 'email' – va a capo prima e dopo la @
|
|
209
|
+
// 'boolean' – icona spunta/croce con colore semantico
|
|
210
|
+
// 'sf_datetime' – data/ora da oggetto Symfony ({ date, timezone })
|
|
211
|
+
|
|
212
|
+
_sortValue, // Percorso del campo da usare per l'ORDINAMENTO al posto del contenuto
|
|
213
|
+
// visualizzato. Utile quando la cella contiene HTML composto (_render).
|
|
214
|
+
// Supporta notazione punto.
|
|
215
|
+
// Implementato tramite l'attributo data-order sulla cella, letto
|
|
216
|
+
// nativamente da simple-datatables.
|
|
217
|
+
//
|
|
218
|
+
// Esempio: cella con '[[owner.nome]] [[owner.cognome]]'
|
|
219
|
+
// _sortValue: 'owner.cognome' → ordina per cognome
|
|
220
|
+
|
|
221
|
+
_searchValue, // Percorso/i del campo da usare per la RICERCA al posto del contenuto
|
|
222
|
+
// visualizzato. Accetta uno o più percorsi separati da spazio,
|
|
223
|
+
// oppure una funzione (row) => string|number|boolean.
|
|
224
|
+
// Supporta notazione punto.
|
|
225
|
+
// Implementato tramite searchMethod di colonna (API simple-datatables):
|
|
226
|
+
// il valore viene scritto in attributes['data-search'] della cella e
|
|
227
|
+
// la searchMethod lo legge dall'OGGETTO DATI INTERNO di
|
|
228
|
+
// simple-datatables (cellType: { data, text?, order?, attributes? }),
|
|
229
|
+
// NON dal nodo DOM <td> (la libreria passa alla callback
|
|
230
|
+
// `searchRow[index]`, non l'elemento renderizzato).
|
|
231
|
+
// Ordine di lettura: attributes['data-search'] → text → data.
|
|
232
|
+
// Il valore viene convertito con String(), quindi sono ammessi
|
|
233
|
+
// numeri e booleani (es. `_searchValue: row => row.id`).
|
|
234
|
+
// La ricerca è di tipo AND: tutti i termini digitati devono essere
|
|
235
|
+
// presenti nel valore del campo (o dei campi) indicati.
|
|
236
|
+
//
|
|
237
|
+
// Normalizzazione: simple-datatables normalizza solo i termini
|
|
238
|
+
// digitati (default `sensitivity: 'base'`, `ignorePunctuation: true`).
|
|
239
|
+
// La searchMethod applica la stessa catena anche al contenuto della
|
|
240
|
+
// cella (minuscole + NFD senza diacritici + rimozione punteggiatura),
|
|
241
|
+
// così "citta" trova "Città" e "spa" trova "S.p.A.".
|
|
242
|
+
// Se sulla colonna sono impostate `sensitivity` / `ignorePunctuation`,
|
|
243
|
+
// vengono rispettate.
|
|
244
|
+
//
|
|
245
|
+
// Esempio: _searchValue: 'owner.cognome owner.nome'
|
|
246
|
+
// cerca su entrambi i campi concatenati; "rossi mario" trova solo
|
|
247
|
+
// le righe che contengono entrambe le parole, in qualsiasi ordine.
|
|
248
|
+
|
|
249
|
+
_footerRender, // Callback o stringa per il contenuto della cella nel <tfoot>.
|
|
250
|
+
// Il <tfoot> (una sola riga) viene aggiunto automaticamente se
|
|
251
|
+
// almeno una colonna definisce questa proprietà; le colonne senza
|
|
252
|
+
// _footerRender producono una <td> vuota.
|
|
253
|
+
//
|
|
254
|
+
// Accetta:
|
|
255
|
+
// - funzione (data) => string
|
|
256
|
+
// `data` è l'array dei record da considerare per il calcolo.
|
|
257
|
+
// Il contenuto di `data` dipende dall'opzione `updateFooterOnPageChange`
|
|
258
|
+
// del componente (default false = dataset filtrato completo,
|
|
259
|
+
// indipendente dalla pagina corrente).
|
|
260
|
+
// - stringa statica (es. etichetta fissa "Totale:")
|
|
261
|
+
//
|
|
262
|
+
// La cella eredita le classi CSS di allineamento della colonna
|
|
263
|
+
// (cellClass / headerClass), in modo da risultare visivamente coerente.
|
|
264
|
+
//
|
|
265
|
+
// Aggiornamenti automatici:
|
|
266
|
+
// - sempre: al cambio di filtro (ricerca / multisearch)
|
|
267
|
+
// - solo se updateFooterOnPageChange: true: al cambio di pagina
|
|
268
|
+
//
|
|
269
|
+
// Esempio – somma di una colonna numerica:
|
|
270
|
+
// _footerRender: (data) => {
|
|
271
|
+
// const tot = data.reduce((s, r) => s + (r.importo ?? 0), 0);
|
|
272
|
+
// return `<strong>${tot.toLocaleString('it-IT')} €</strong>`;
|
|
273
|
+
// }
|
|
274
|
+
|
|
275
|
+
_collapseKey, // Percorso (notazione punto) del campo da usare come chiave di
|
|
276
|
+
// raggruppamento per collassare valori ripetuti su righe consecutive.
|
|
277
|
+
// `true` = riusa _field. Colonne diverse con la stessa _collapseKey
|
|
278
|
+
// collassano insieme come un unico gruppo logico (es. più colonne
|
|
279
|
+
// che riportano dati della stessa "commessa" su più righe/attività).
|
|
280
|
+
//
|
|
281
|
+
// Opera sulle righe effettivamente renderizzate (già filtrate/ordinate/
|
|
282
|
+
// paginate): la prima riga di ogni pagina mostra sempre il valore pieno,
|
|
283
|
+
// anche se continua un gruppo iniziato nella pagina precedente.
|
|
284
|
+
// Si ricalcola automaticamente ad ogni cambio pagina, ordinamento,
|
|
285
|
+
// ricerca o multisearch.
|
|
286
|
+
|
|
287
|
+
_collapsePlaceholder, // Testo da mostrare al posto del valore ripetuto. Accetta lo stesso
|
|
288
|
+
// ventaglio di _render/_cellTitle: stringa semplice, stringa mustache-like
|
|
289
|
+
// con segnaposto [[chiave]] (interpolati sulla riga corrente), oppure
|
|
290
|
+
// funzione (row) => string. Default null (anche se la funzione ritorna
|
|
291
|
+
// null/undefined) = il testo della cella resta invariato (utile insieme
|
|
292
|
+
// alla sola _collapseClass, quando basta l'effetto visivo a segnalare
|
|
293
|
+
// il collasso).
|
|
294
|
+
|
|
295
|
+
_collapseClass, // Classe CSS da assegnare alla cella collassata. Nessuna classe di
|
|
296
|
+
// default: il trattamento visivo (es. attenuazione cromatica) è a
|
|
297
|
+
// discrezione del consumer nel proprio CSS applicativo.
|
|
298
|
+
|
|
299
|
+
// ── Proprietà native simple-datatables (senza prefisso _) ────────────
|
|
300
|
+
|
|
301
|
+
type, // Tipo di dato: 'string' | 'number' | 'date' | 'boolean' | 'html' | 'other'
|
|
302
|
+
sortable, // false per disabilitare l'ordinamento sulla colonna
|
|
303
|
+
searchable, // false per escludere la colonna dalla ricerca globale
|
|
304
|
+
hidden, // true per nascondere la colonna (esclusa anche dalla ricerca)
|
|
305
|
+
sort, // 'asc' | 'desc' per ordinamento iniziale (solo su colonna singola)
|
|
306
|
+
// ... tutte le altre opzioni columns di simple-datatables
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Esempio completo con valori annidati
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
const cols = [
|
|
315
|
+
{
|
|
316
|
+
_field: 'commessa',
|
|
317
|
+
_heading: 'Commessa / fornitore',
|
|
318
|
+
_render: '[[commessa]]<br><small class="text-muted">[[fornitore]]</small>',
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
_field: 'owner.cognome',
|
|
322
|
+
_heading: 'Owner',
|
|
323
|
+
_render: '[[owner.nome]] [[owner.cognome]]',
|
|
324
|
+
_sortValue: 'owner.cognome', // ordina per cognome
|
|
325
|
+
_searchValue: 'owner.cognome owner.nome', // cerca su cognome e nome (AND)
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
_field: 'importi.0.importo', // valore annidato con indice array
|
|
329
|
+
_heading: 'Importo',
|
|
330
|
+
type: 'number',
|
|
331
|
+
}
|
|
332
|
+
];
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Esempio con tfoot – somma e conteggio
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
// updateFooterOnPageChange: false (default) – il footer mostra il totale del set filtrato,
|
|
339
|
+
// indipendente dalla pagina. Non si aggiorna al cambio pagina.
|
|
340
|
+
const el = document.querySelector('s-datatable');
|
|
341
|
+
el.init({
|
|
342
|
+
json: '/api/commesse.json',
|
|
343
|
+
cols: [
|
|
344
|
+
{
|
|
345
|
+
_field: 'commessa',
|
|
346
|
+
_heading: 'Commessa',
|
|
347
|
+
_footerRender: '<strong>Totale:</strong>', // etichetta fissa
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
_field: 'importo',
|
|
351
|
+
_heading: 'Importo',
|
|
352
|
+
type: 'number',
|
|
353
|
+
cellClass: 'text-end',
|
|
354
|
+
headerClass: 'text-end',
|
|
355
|
+
// data = tutti i record filtrati (intera ricerca, non solo la pagina corrente)
|
|
356
|
+
_footerRender: (data) => {
|
|
357
|
+
const tot = data.reduce((s, r) => s + (r.importo ?? 0), 0);
|
|
358
|
+
return `<strong>${tot.toLocaleString('it-IT', { style: 'currency', currency: 'EUR' })}</strong>`;
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
_field: 'stato',
|
|
363
|
+
_heading: 'Stato',
|
|
364
|
+
_footerRender: (data) => `${data.length} record`,
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// updateFooterOnPageChange: true – il footer mostra il subtotale della sola pagina corrente.
|
|
370
|
+
// Si aggiorna sia al cambio filtro sia al cambio pagina.
|
|
371
|
+
el.init({
|
|
372
|
+
json: '/api/commesse.json',
|
|
373
|
+
updateFooterOnPageChange: true,
|
|
374
|
+
cols: [ /* stesse colonne di sopra */ ],
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Il `<tfoot>` viene aggiunto solo se almeno una colonna definisce `_footerRender`.
|
|
379
|
+
|
|
380
|
+
Con `updateFooterOnPageChange: false` (default) la callback riceve l'intero set filtrato: i totali
|
|
381
|
+
non cambiano navigando tra le pagine e si aggiornano solo quando cambia la ricerca.
|
|
382
|
+
|
|
383
|
+
Con `updateFooterOnPageChange: true` la callback riceve i record della pagina corrente: utile per
|
|
384
|
+
subtotali di pagina; il footer si aggiorna sia al cambio filtro sia al cambio pagina.
|
|
385
|
+
|
|
386
|
+
### Esempio con collapse – valori ripetuti su righe consecutive
|
|
387
|
+
|
|
388
|
+
Utile quando ogni riga è un dettaglio (es. un'attività) ma alcune colonne riportano dati di un
|
|
389
|
+
gruppo più ampio (es. la commessa a cui l'attività appartiene), ripetuti identici su più righe
|
|
390
|
+
consecutive quando la tabella è ordinata per quel gruppo.
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
const cols = [
|
|
394
|
+
{
|
|
395
|
+
_field: 'commessaId',
|
|
396
|
+
_heading: 'Commessa',
|
|
397
|
+
_collapseKey: true, // riusa _field ('commessaId') come chiave di gruppo
|
|
398
|
+
_collapsePlaceholder: '»', // mostrato al posto del valore ripetuto
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
_field: 'budgetCommessa',
|
|
402
|
+
_heading: 'Budget',
|
|
403
|
+
type: 'number',
|
|
404
|
+
_collapseKey: 'commessaId', // stessa chiave: collassa insieme alla colonna precedente
|
|
405
|
+
_collapseClass: 'text-muted', // classe CSS a discrezione del consumer
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
_field: 'attivita',
|
|
409
|
+
_heading: 'Attività', // colonna di dettaglio, mai collassata
|
|
410
|
+
},
|
|
411
|
+
];
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
La prima riga di ogni pagina mostra sempre il valore pieno, anche se continua un gruppo iniziato
|
|
415
|
+
nella pagina precedente. Il calcolo si ricalcola automaticamente ad ogni cambio pagina, ordinamento,
|
|
416
|
+
ricerca o multisearch (opera sulle righe effettivamente renderizzate, non sui dati grezzi).
|
|
417
|
+
|
|
418
|
+
## Testo info
|
|
419
|
+
|
|
420
|
+
Il componente sovrascrive il testo dell'area informativa (in basso a sinistra) per includere
|
|
421
|
+
sempre il numero totale assoluto di record, distinto dal conteggio filtrato.
|
|
422
|
+
|
|
423
|
+
Senza ricerca attiva:
|
|
424
|
+
|
|
425
|
+
> Stai visualizzando le righe da 1 a 25, su un totale di 100.
|
|
426
|
+
|
|
427
|
+
Con ricerca attiva:
|
|
428
|
+
|
|
429
|
+
> Stai visualizzando le righe da 1 a 25 dei 50 record filtrati, su un totale di 100.
|
|
430
|
+
|
|
431
|
+
Ricerca senza risultati:
|
|
432
|
+
|
|
433
|
+
> Nessun risultato per la ricerca corrente (totale record: 100).
|
|
434
|
+
|
|
435
|
+
Il testo si aggiorna automaticamente ad ogni cambio pagina, ricerca e multisearch.
|
|
436
|
+
|
|
437
|
+
## Parametri `topSlot` e `showInfoAtTop`
|
|
438
|
+
|
|
439
|
+
Entrambi occupano l'area in alto a sinistra del datatable — lo spazio normalmente
|
|
440
|
+
riservato al selettore "righe per pagina" (qui disabilitato). I due parametri si
|
|
441
|
+
escludono: se `showInfoAtTop` è `true` (default), `topSlot` viene ignorato.
|
|
442
|
+
|
|
443
|
+
### `showInfoAtTop`
|
|
444
|
+
|
|
445
|
+
Replica nell'area superiore lo stesso testo dell'area `.info` in basso,
|
|
446
|
+
mantenuto in sincronia ad ogni cambio pagina o filtro. Il nodo viene nascosto
|
|
447
|
+
automaticamente quando non ci sono record o la ricerca non produce risultati
|
|
448
|
+
(evita di mostrare "Nessun risultato…" in due posti).
|
|
449
|
+
|
|
450
|
+
```javascript
|
|
451
|
+
el.init({
|
|
452
|
+
json: '/api/data.json',
|
|
453
|
+
cols: [...],
|
|
454
|
+
showInfoAtTop: true,
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### `topSlot`
|
|
459
|
+
|
|
460
|
+
Inserisce un elemento DOM o markup HTML arbitrario, avvolto in un
|
|
461
|
+
`<div class="datatable-top-slot">`. Accetta sia una stringa HTML che un `Element`
|
|
462
|
+
già costruito (utile per mantenere un riferimento al nodo e aggiungervi listener).
|
|
463
|
+
Non è leggibile da attributo HTML, solo via `init()`.
|
|
464
|
+
|
|
465
|
+
```javascript
|
|
466
|
+
// Stringa HTML
|
|
467
|
+
el.init({
|
|
468
|
+
json: '/api/data.json',
|
|
469
|
+
cols: [...],
|
|
470
|
+
topSlot: '<button class="btn btn-sm btn-primary">Esporta CSV</button>',
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Elemento DOM
|
|
474
|
+
const btn = document.createElement('button');
|
|
475
|
+
btn.className = 'btn btn-sm btn-outline-secondary';
|
|
476
|
+
btn.textContent = 'Nuova commessa';
|
|
477
|
+
btn.addEventListener('click', () => { /* … */ });
|
|
478
|
+
|
|
479
|
+
el.init({
|
|
480
|
+
json: '/api/commesse.json',
|
|
481
|
+
cols: [...],
|
|
482
|
+
topSlot: btn,
|
|
483
|
+
});
|
|
484
|
+
```
|