@massimo-cassandro/minimo 1.41.4 → 1.41.6
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/README.md +12 -12
- package/charts/src/node-helpers/create-svg-canvas-node.mjs +2 -2
- package/charts/src/node-helpers/svg-text-to-path.js +9 -9
- package/charts/src/rating-display/index.js +1 -1
- package/charts/src/rating-display/ratindDisplayAnimation.js +3 -3
- package/design-tokens/README.md +2 -2
- package/design-tokens/_src/_readme.md +26 -26
- package/dev-tools/README.md +16 -16
- package/dev-tools/create-favicons/README.md +31 -31
- package/dev-tools/create-favicons/src/create-ico.mjs +2 -2
- package/dev-tools/create-favicons/src/default-params.mjs +1 -1
- package/dev-tools/create-favicons/src/init.mjs +1 -1
- package/dev-tools/design-tokens-builder/README.md +102 -102
- package/dev-tools/design-tokens-builder/build-tokens-src/build-source-modes.mjs +85 -73
- package/dev-tools/design-tokens-builder/build-tokens-src/config.mjs +109 -85
- package/dev-tools/design-tokens-builder/build-tokens-src/formats/css.mjs +113 -90
- package/dev-tools/design-tokens-builder/build-tokens-src/formats/json.mjs +107 -95
- package/dev-tools/design-tokens-builder/build-tokens-src/legacy-tokens-parser.mjs +71 -64
- package/dev-tools/design-tokens-builder/build-tokens-src/light-dark.mjs +75 -65
- package/dev-tools/design-tokens-builder/build-tokens-src/merge-css.mjs +113 -102
- package/dev-tools/design-tokens-builder/build-tokens-src/platforms.mjs +36 -30
- package/dev-tools/design-tokens-builder/build-tokens-src/resolve-source-paths.mjs +15 -13
- package/dev-tools/design-tokens-builder/build-tokens-src/source-prefixes.mjs +44 -42
- package/dev-tools/design-tokens-builder/build-tokens-src/source-transforms.mjs +89 -83
- package/dev-tools/design-tokens-builder/build-tokens-src/token-rename-parser.mjs +99 -86
- package/dev-tools/design-tokens-builder/build-tokens-src/transforms.mjs +64 -48
- package/dev-tools/design-tokens-builder/build-tokens.mjs +80 -68
- package/dev-tools/design-tokens-builder/check-unresolved-custom-props.mjs +103 -77
- package/dev-tools/design-tokens-builder/tokens-config-sample.mjs +275 -238
- package/dev-tools/find-orphan-css-classes/find-orphan-css-classes.mjs +2 -2
- package/dev-tools/starter-kit/source_files/app/CLAUDE.md +4 -2
- package/dev-tools/starter-kit/source_files/app/{css/index.css → index.css} +2 -2
- package/dev-tools/starter-kit/source_files/app/index.js +1 -1
- package/dev-tools/starter-kit/source_files/root/_sf/controller/ImgViewerController.php +3 -3
- package/dev-tools/starter-kit/source_files/root/_sf/controller/ViewerController.php +2 -2
- package/dev-tools/starter-kit/source_files/root/stylelint.config.mjs +1 -1
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/css-rules.mjs +1 -1
- package/dev-tools/starter-kit/source_files/root/webpack.config.mjs +15 -1
- package/dev-tools/starter-kit/webpack-config-upg.md +12 -0
- package/dev-tools/stylelint-config/README.md +7 -7
- package/dev-tools/svg-icons-tools/README.md +32 -32
- package/dev-tools/svg-icons-tools/src/config-manager.mjs +1 -1
- package/dev-tools/svg-icons-tools/src/default-config.mjs +118 -118
- package/dev-tools/svg-icons-tools/src/jsx-icon-file-builder.mjs +24 -24
- package/dev-tools/svg-icons-tools/src/parse-svg-filename.mjs +3 -3
- package/dev-tools/svg-icons-tools/src/parse-svg-files.mjs +1 -1
- package/dev-tools/svg-icons-tools/src/parse-svg-markup.mjs +4 -4
- package/dev-tools/svg-icons-tools/src/svg-to-jsx.mjs +2 -2
- package/dev-tools/svg-icons-tools/src/svg-to-optimized.mjs +2 -2
- package/dev-tools/svg-icons-tools/src/svg-to-symbols.mjs +2 -2
- package/dev-tools/upd@m/README.md +1 -1
- package/dev-tools/upd@m/index.mjs +1 -1
- package/dev-tools/update-version/README.md +9 -9
- package/package.json +1 -1
- package/snippets/unsplash-browser-app/README.md +1 -1
- package/snippets/unsplash-browser-app/unsplash-utilities.js +9 -9
- package/src/components/autocomplete/README.md +3 -3
- package/src/components/autocomplete/autocomplete-engine.js +75 -63
- package/src/components/autocomplete/check-autocomplete.js +3 -3
- package/src/components/dismiss-alerts.js +3 -3
- package/src/components/form-reset-submit.js +6 -6
- package/src/components/inner-nav/inner-nav.js +2 -2
- package/src/components/modal-alert/modal-alert.js +67 -65
- package/src/components/modal-alert/modal-alert.minimo.tokens.mjs +1 -1
- package/src/components/modal-popup/modal-popup.js +56 -56
- package/src/components/overlay/overlay.js +3 -3
- package/src/components/sf-macro/readme.md +1 -1
- package/src/components/sf-macro/sf-macro.js +27 -23
- package/src/components/slide-up-down-toggle/slide-up-down-toggle.js +83 -79
- package/src/components/slide-up-down-toggle/slide-up-down-toggle.module.css +44 -44
- package/src/components/snackbar/snackbar.js +19 -19
- package/src/components/spinner/spinner.js +3 -3
- package/src/components/unsplash-page/README.md +8 -8
- package/src/components/unsplash-page/fetch-unsplash-data.js +3 -3
- package/src/components/unsplash-page/unsplash-page.js +33 -33
- package/src/components/unsplash-page/unsplash-page.module.css +1 -1
- package/src/css/alerts.css +2 -2
- package/src/css/forms/forms.css +4 -4
- package/src/css/forms/validation.css +1 -1
- package/src/css/margin-padding-media.css +4 -4
- package/src/css/margin-padding.css +4 -4
- package/src/css/reset.css +11 -11
- package/src/icons/read-me.md +1 -1
- package/src/minimo.css +3 -3
- package/src/utilities/better-text-with-places.js +22 -22
- package/src/utilities/better-text.js +42 -35
- package/src/utilities/classnames.js +8 -8
- package/src/utilities/cookies.js +6 -6
- package/src/utilities/create-id.js +3 -3
- package/src/utilities/disable-btns-on-submit.js +2 -2
- package/src/utilities/dom-builder/dom-builder.js +89 -86
- package/src/utilities/dom-builder/domBuilderBasicSetup.js +9 -9
- package/src/utilities/dom-builder/parseDomString.js +23 -23
- package/src/utilities/dom-builder-helpers/README.md +2 -2
- package/src/utilities/dom-builder-helpers/form-tags.js +100 -100
- package/src/utilities/dom-builder-helpers/picture-tag.js +39 -39
- package/src/utilities/enable-submit-btns.js +1 -1
- package/src/utilities/escape-html.js +2 -2
- package/src/utilities/generate-pwd.js +4 -4
- package/src/utilities/linked-dates.js +13 -13
- package/src/utilities/parse-file-size.js +6 -6
- package/src/utilities/random-id.js +3 -3
- package/src/utilities/relative-date.js +40 -40
- package/src/utilities/sentence-case.js +1 -1
- package/src/utilities/strip-tags.js +1 -1
- package/src/utilities/title-case.js +1 -1
- package/src/utilities/truncate-string.js +5 -5
- package/src/utilities/validation-error-class.js +12 -12
- package/src/web-components/json-table/json-table-component.js +123 -121
- package/src/web-components/json-table/json-table-component.module.css +6 -6
- package/src/web-components/json-table/json-table.minimo.tokens.mjs +6 -6
- package/src/web-components/json-table/jt-docs/parametri.md +124 -0
- package/src/web-components/json-table/jt-docs.md +622 -0
- package/src/web-components/json-table/readme.md +3 -763
- package/src/web-components/json-table/src/cell-content.js +25 -25
- package/src/web-components/json-table/src/content-utils.js +21 -21
- package/src/web-components/json-table/src/data-types.js +32 -32
- package/src/web-components/json-table/src/defaults.js +230 -191
- package/src/web-components/json-table/src/get-data.js +55 -38
- package/src/web-components/json-table/src/icons.js +17 -17
- package/src/web-components/json-table/src/info-section.js +18 -18
- package/src/web-components/json-table/src/main-builder.js +45 -45
- package/src/web-components/json-table/src/pagination.js +32 -32
- package/src/web-components/json-table/src/parse-cols.js +18 -18
- package/src/web-components/json-table/src/parse-rows.js +17 -17
- package/src/web-components/json-table/src/resolve-params.js +53 -41
- package/src/web-components/json-table/src/search.js +13 -13
- package/src/web-components/json-table/src/sorting.js +20 -20
- package/src/web-components/json-table/src/table-body.js +13 -13
- package/src/web-components/json-table/src/table-builder.js +34 -34
- package/src/web-components/json-table/src/table-tfoot.js +8 -8
- package/src/web-components/json-table/src/table-thead.js +21 -21
- package/src/web-components/json-table/src/update-info.js +11 -11
- package/types/classes.d.ts +2 -2
- package/types/global.d.ts +18 -12
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# `<json-table>` – datatable da JSON
|
|
2
|
+
|
|
3
|
+
* [Parametri (include `classes` e `labels`)](jt-docs/parametri.md)
|
|
4
|
+
|
|
5
|
+
**json-table** costruisce una tabella HTML da un array di dati JSON (inline o caricato via fetch), a partire da una definizione delle colonne (`cols`) e da un insieme di tipi di dato predefiniti ed estendibili, con ordinamento, ricerca e paginazione (lato client oppure, con `serverSide: true`, delegati al server). È un web component light DOM senza dipendenze esterne: può quindi essere stilizzato direttamente dal CSS del progetto in cui è utilizzato.
|
|
6
|
+
|
|
7
|
+
## Installazione
|
|
8
|
+
|
|
9
|
+
È sufficiente importare il file del componente (registra il custom element):
|
|
10
|
+
|
|
11
|
+
```javascriptcacheGroup `shared` (solo con `useSharedChunk: true`): escludere `unsplash-page` e `blurhash` dal chunk condiviso, in modo che js e css restino nella entry che li importa. In `webpack.config.mjs`, nella funzione `shared_chunk_paths`, prima del `return` aggiungere:
|
|
12
|
+
```js
|
|
13
|
+
const excluded_paths = [
|
|
14
|
+
'minimo/src/components/unsplash-page',
|
|
15
|
+
'node_modules/blurhash', // peer dep usata solo da unsplash-page
|
|
16
|
+
];
|
|
17
|
+
const excludedRegexp = new RegExp(
|
|
18
|
+
excluded_paths.map(p => `${sep}${p.replace(/\//g, sep)}${sep}`).join('|')
|
|
19
|
+
);
|
|
20
|
+
const modulePath = module.nameForCondition?.() ?? '';
|
|
21
|
+
```
|
|
22
|
+
e sostituire il `return` con `return !excludedRegexp.test(modulePath) && pathsRegexp.test(modulePath);`. Aggiungere anche al commento sopra il cacheGroup la nota sull'esclusione.
|
|
23
|
+
// solo registrazione del tag
|
|
24
|
+
import '@massimo-cassandro/minimo/json-table';
|
|
25
|
+
|
|
26
|
+
// oppure, se necessario eseguire metodi della classe (es. `setDefaults`):
|
|
27
|
+
import { JsonTable } from '@massimo-cassandro/minimo/json-table';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Il CSS (`json-table-component.module.css`) è importato dal componente stesso; le custom
|
|
31
|
+
properties `--jt-*` sono definite in `json-table.minimo.tokens.mjs` e compilate in
|
|
32
|
+
`custom-properties.css` da `build-tokens`. Le icone di default (booleani, ordinamento e
|
|
33
|
+
paginazione) sono SVG di minimo importati con il suffisso `?inline` (gestito dalla configurazione
|
|
34
|
+
webpack dello starter-kit).
|
|
35
|
+
|
|
36
|
+
### Tipi per gli script consumer
|
|
37
|
+
|
|
38
|
+
Il file `types/global.d.ts` di minimo dichiara il tipo globale `JsonTable` e registra il tag `json-table` in `HTMLElementTagNameMap`: aggiungendolo all'`include` del proprio `jsconfig.json`/`tsconfig.json` (`"node_modules/@massimo-cassandro/minimo/types/global.d.ts"`) si ottiene il completamento dei metodi senza cast né import di tipo:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const el = document.querySelector('json-table'); // → JsonTable
|
|
42
|
+
el.init({ ... });
|
|
43
|
+
|
|
44
|
+
/** @type {JsonTable} */
|
|
45
|
+
const other = document.createElement('json-table');
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Utilizzo da markup
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<json-table
|
|
52
|
+
jsonurl="/api/rows.json"
|
|
53
|
+
caption="Utenti"
|
|
54
|
+
perpage="10"
|
|
55
|
+
cols='[
|
|
56
|
+
{ "key": "id", "dataType": "id" },
|
|
57
|
+
{ "key": "name", "title": "Nome", "render": "<a href=\"/users/[[id]]\">[[name]]</a>" },
|
|
58
|
+
{ "key": "amount", "title": "Importo", "dataType": "euro", "tfootRender": "@sum" },
|
|
59
|
+
{ "key": "active", "title": "Attivo", "dataType": "bool" }
|
|
60
|
+
]'
|
|
61
|
+
tfoot="true"
|
|
62
|
+
serverSide="true"
|
|
63
|
+
></json-table>
|
|
64
|
+
|
|
65
|
+
<!-- dati inline (JSON serializzato) al posto di jsonurl -->
|
|
66
|
+
<json-table
|
|
67
|
+
data='[{"name":"Mario","city":"Roma"}, ...]'
|
|
68
|
+
cols='[{ "key": "name", "title": "Nome" }, { "key": "city", "title": "Città" }]'
|
|
69
|
+
search="false"
|
|
70
|
+
></json-table>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
I nomi degli attributi sono case-insensitive (`jsonurl` e `jsonUrl` sono equivalenti).
|
|
74
|
+
Gli attributi booleani accettano i formati HTML idiomatici: `search` (presente senza valore),
|
|
75
|
+
`search="true"`, `search="1"` → true; `search="false"`, `search="0"` → false. Gli attributi
|
|
76
|
+
numerici (`perpage`, `paginationdelta`, `searchdebounce`) vengono convertiti in numero.
|
|
77
|
+
I valori che iniziano con `[` o `{` vengono interpretati come JSON (un JSON malformato viene
|
|
78
|
+
segnalato in console e ignorato).
|
|
79
|
+
|
|
80
|
+
Da attributo non è possibile passare funzioni: per il rendering delle celle è disponibile la
|
|
81
|
+
sintassi mustache-like `[[key]]` (vedi [`render`](#render)), per il footer gli aggregati
|
|
82
|
+
predefiniti (`"@sum"`, ...).
|
|
83
|
+
|
|
84
|
+
## Utilizzo da script
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const el = document.querySelector('json-table');
|
|
88
|
+
// oppure: const el = document.createElement('json-table');
|
|
89
|
+
|
|
90
|
+
el.init({
|
|
91
|
+
jsonUrl: '/api/rows.json',
|
|
92
|
+
caption: 'Utenti',
|
|
93
|
+
cols: [
|
|
94
|
+
{ key: 'id', dataType: 'id' },
|
|
95
|
+
{ key: 'name', title: 'Nome', render: row => `<a href="/users/${row.id}">${row.name}</a>` },
|
|
96
|
+
{ key: 'amount', title: 'Importo', dataType: 'euro', tfootRender: '@sum' },
|
|
97
|
+
{ key: 'active', title: 'Attivo', dataType: 'bool' }
|
|
98
|
+
],
|
|
99
|
+
tfoot: true
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// dati inline al posto di jsonUrl (se presenti entrambi, `data` prevale)
|
|
103
|
+
el.init({
|
|
104
|
+
data: [{ name: 'Mario', city: 'Roma' }, ...],
|
|
105
|
+
cols: [...]
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// se appena creato, appendere dopo init():
|
|
109
|
+
document.body.appendChild(el);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Con `domBuilder`
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
domBuilder([
|
|
116
|
+
{
|
|
117
|
+
tag: 'json-table',
|
|
118
|
+
attrs: { caption: 'Utenti' },
|
|
119
|
+
callback: el => el.init({ jsonUrl: '/api/rows.json', cols: [...] })
|
|
120
|
+
}
|
|
121
|
+
], container);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Utilizzo misto – attributi HTML + script
|
|
125
|
+
|
|
126
|
+
I parametri non inclusi in `init()` vengono letti dall'attributo HTML corrispondente:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<json-table caption="Utenti" search="false" cols='[...]'></json-table>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
el.init({ jsonUrl: '/api/rows.json' }); // jsonUrl da script, caption, search e cols da attributo
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Ordine di precedenza:** `init()` > attributo HTML > `JsonTable.setDefaults()` > default interno.
|
|
137
|
+
|
|
138
|
+
Per ignorare esplicitamente un attributo e utilizzare il valore di default, utilizzare `null` nel config:
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
el.init({ jsonUrl: '/api/rows.json', search: null }); // search == default (true)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
I parametri oggetto `classes`, `labels`, `dataTypes` e `serverParams` vengono **uniti** ai default
|
|
145
|
+
(merge a un livello), quindi ogni sorgente deve indicare solo le chiavi da sovrascrivere:
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
el.init({ classes: { table: 'table' } }); // le altre classi restano quelle di default
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Default di progetto – `JsonTable.setDefaults()`
|
|
152
|
+
|
|
153
|
+
Imposta valori validi per tutte le istanze create successivamente, evitando di ripetere gli
|
|
154
|
+
stessi parametri. Da invocare una sola volta, in uno script condiviso eseguito prima degli altri.
|
|
155
|
+
I valori vengono uniti a quelli impostati in chiamate precedenti.
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
import { JsonTable } from '.../json-table-component.js';
|
|
159
|
+
|
|
160
|
+
JsonTable.setDefaults({
|
|
161
|
+
perPage: 50,
|
|
162
|
+
classes: { searchInput: 'form-control', table: 'table' },
|
|
163
|
+
labels: { searchPlaceholder: 'Cerca…', noRows: 'Nessun elemento' },
|
|
164
|
+
infoText: (start, end, totRec, filteredRec) => `${filteredRec} di ${totRec} record`
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
JsonTable.getDefaults(); // default interni + default di progetto
|
|
168
|
+
JsonTable.resetDefaults(); // azzera i default di progetto
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API dell'istanza
|
|
172
|
+
|
|
173
|
+
| Metodo | Descrizione |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `init(config)` | Avvia il componente (prima o dopo l'inserimento nel DOM) |
|
|
176
|
+
| `reload(overrides)` | Ricarica i dati e ricostruisce la struttura, con eventuali parametri sovrascritti (`Promise`); ricerca, ordinamento e pagina vengono azzerati |
|
|
177
|
+
| `destroy()` | Svuota il componente; `init()` può essere richiamato in seguito |
|
|
178
|
+
| `goToPage(page)` | Mostra la pagina indicata (limitata all'intervallo disponibile) |
|
|
179
|
+
| `setSort(key, dir)` | Ordina per la colonna `key` (`'asc'`, `'desc'`, oppure `null` per rimuovere l'ordinamento) e torna alla prima pagina |
|
|
180
|
+
| `setSearch(term)` | Filtra le righe con il termine indicato (stringa vuota = nessun filtro) e torna alla prima pagina; l'input di ricerca viene aggiornato |
|
|
181
|
+
|
|
182
|
+
In modalità server-side `goToPage`, `setSort` e `setSearch` generano una nuova richiesta.
|
|
183
|
+
|
|
184
|
+
Proprietà disponibili dopo il caricamento:
|
|
185
|
+
|
|
186
|
+
| Proprietà | Descrizione |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `params` | parametri risolti |
|
|
189
|
+
| `data` | righe grezze (array di oggetti; in modalità server-side solo quelle della pagina corrente) |
|
|
190
|
+
| `cols` | colonne dopo il parsing (solo quelle visibili, con classi e tipo di dato risolti) |
|
|
191
|
+
| `dataTypes` | mappa dei tipi di dato (predefiniti + personalizzati) |
|
|
192
|
+
| `state` | stato del rendering: `totRec`, `filteredRec`, `rows`, `filtered`, `pageRows`, `searchTerm`, `sort` (`{ key, dir }` o `null`), `page`, `totPages` |
|
|
193
|
+
| `elements` | elementi generati: `wrapper`, `infoOuter`, `info`, `resultInfo`, `searchInput`, `tableWrapper`, `table`, `thead`, `tbody`, `tfoot`, `tableFooter`, `caption`, `pagination` |
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
await el.reload({ jsonUrl: '/api/rows.json?anno=2025' });
|
|
197
|
+
await el.reload({ data: [...] });
|
|
198
|
+
el.setSort('name', 'desc');
|
|
199
|
+
el.goToPage(3);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Eventi
|
|
203
|
+
|
|
204
|
+
Entrambi vengono emessi sull'elemento con `bubbles: true`; `event.detail.jsonTable` è l'istanza.
|
|
205
|
+
|
|
206
|
+
| Evento | Quando | `event.detail` |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `jt:ready` | al termine della costruzione e del primo rendering (nel microtask successivo, quindi un listener registrato subito dopo `init()` lo intercetta comunque) | `{ jsonTable }` |
|
|
209
|
+
| `jt:update` | dopo ogni cambio pagina, ordinamento o ricerca (anche via API) | `{ jsonTable, reason }` con `reason` = `'page'`, `'sort'` o `'search'` |
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
el.addEventListener('jt:ready', e => console.log(e.detail.jsonTable.data));
|
|
213
|
+
el.addEventListener('jt:update', e => console.log(e.detail.reason, e.detail.jsonTable.state.page));
|
|
214
|
+
el.init({ jsonUrl: '/api/rows.json', cols: [...] });
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
## Colonne – `cols`
|
|
220
|
+
|
|
221
|
+
Parametro **obbligatorio**: array di oggetti, uno per colonna:
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
cols: [
|
|
225
|
+
{
|
|
226
|
+
key, // string – OBBLIGATORIO: chiave dell'oggetto riga (ammessa la notazione con
|
|
227
|
+
// punto per valori annidati, es. 'owner.name')
|
|
228
|
+
title, // contenuto del <th>: testo, HTML, Node, array domBuilder o funzione.
|
|
229
|
+
// Nelle colonne ordinabili è il testo del pulsante. Default: la chiave
|
|
230
|
+
dataType, // string – tipo di dato, una delle chiavi di `dataTypes`; `type` è un alias
|
|
231
|
+
// equivalente. Default: 'string'
|
|
232
|
+
render, // (row, tr, td) => contenuto | stringa mustache-like – vedi sotto. Default: null
|
|
233
|
+
tfootRender, // (rows, td) => contenuto | aggregato '@…' | stringa statica – contenuto della
|
|
234
|
+
// cella del tfoot (solo se `tfoot` è true). null = cella vuota. Default: null
|
|
235
|
+
rowHeading, // boolean – se true la cella è un'intestazione di riga (th[scope=row]).
|
|
236
|
+
// Default: false
|
|
237
|
+
searchable, // boolean – abilita la ricerca sulla colonna. Default: true
|
|
238
|
+
sortable, // boolean – abilita l'ordinamento sulla colonna. Default: true
|
|
239
|
+
sortValue, // valore | row => valore – valore usato per l'ordinamento; prevale su quello
|
|
240
|
+
// del tipo di dato. Default: non impostato (tipo di dato / valore grezzo)
|
|
241
|
+
searchValue, // idem, per la ricerca. Default: non impostato
|
|
242
|
+
condition, // boolean | params => boolean – se false la colonna non viene renderizzata.
|
|
243
|
+
// Default: true
|
|
244
|
+
headerClass, // string – classi del <th>, in sostituzione di quelle del tipo di dato
|
|
245
|
+
cellClass // string – classi delle celle body/tfoot, in sostituzione di quelle del tipo.
|
|
246
|
+
// Se solo una tra `headerClass` e `cellClass` è presente, l'altra assume lo
|
|
247
|
+
// stesso valore. Default: null (classi del tipo di dato)
|
|
248
|
+
},
|
|
249
|
+
...
|
|
250
|
+
]
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### `render`
|
|
254
|
+
|
|
255
|
+
Funzione con argomenti posizionali, tutti facoltativi:
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
render: (row, tr, td) => contenuto
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
- `row` – l'oggetto dati della riga
|
|
262
|
+
- `tr` – l'elemento `<tr>` della riga
|
|
263
|
+
- `td` – l'elemento della cella (`<td>` o `<th scope="row">`)
|
|
264
|
+
|
|
265
|
+
Il valore restituito è il contenuto della cella: testo, stringa HTML, numero, Node o array
|
|
266
|
+
domBuilder. Casi particolari:
|
|
267
|
+
|
|
268
|
+
- `undefined` (nessun `return`): la cella viene renderizzata dal tipo di dato, come se `render`
|
|
269
|
+
non fosse definito. Utile per decorare soltanto `td`/`tr` (classi, attributi, `title`):
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
render: (row, tr, td) => { if (row.amount < 0) td.classList.add('text-danger'); }
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
- `null`: viene mostrato `renderNullAs`
|
|
276
|
+
|
|
277
|
+
Da attributo HTML (o da script, per i casi semplici) `render` può essere una stringa
|
|
278
|
+
mustache-like: ogni segnaposto `[[key]]` viene sostituito con il valore corrispondente della
|
|
279
|
+
riga (chiavi annidate ammesse; i valori null diventano `renderNullAs`):
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
render: '<a href="/users/[[id]]">[[owner.name]]</a>'
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Le stringhe HTML vengono inserite tramite la Sanitizer API (`Element.setHTML`, dove supportata):
|
|
286
|
+
script, attributi handler di eventi e URL `javascript:` vengono rimossi, mentre `class`, `id`,
|
|
287
|
+
`style` e `data-*` sono conservati.
|
|
288
|
+
|
|
289
|
+
### `tfootRender`
|
|
290
|
+
|
|
291
|
+
Con `tfoot: true` ogni colonna produce una cella nel footer, il cui contenuto è dato da
|
|
292
|
+
`tfootRender`:
|
|
293
|
+
|
|
294
|
+
- funzione `(rows, td) => contenuto`, dove `rows` è l'intero set filtrato (oppure i soli record
|
|
295
|
+
della pagina corrente se `updateFooterOnPageChange` è true)
|
|
296
|
+
- uno degli aggregati predefiniti (vedi sotto)
|
|
297
|
+
- qualsiasi altra stringa: contenuto statico (es. l'etichetta `'Totale'`)
|
|
298
|
+
- `null`: cella vuota
|
|
299
|
+
|
|
300
|
+
Aggregati predefiniti, calcolati sui valori **numerici** della colonna (i valori non numerici
|
|
301
|
+
vengono ignorati) e, tranne `@count`, formattati dal tipo di dato della colonna (es. come valuta
|
|
302
|
+
per `euro`, con `%` per `perc`):
|
|
303
|
+
|
|
304
|
+
| Aggregato | Valore |
|
|
305
|
+
|---|---|
|
|
306
|
+
| `'@sum'` | somma |
|
|
307
|
+
| `'@avg'` | media aritmetica |
|
|
308
|
+
| `'@min'` | minimo |
|
|
309
|
+
| `'@max'` | massimo |
|
|
310
|
+
| `'@count'` | numero di valori numerici (formattato con `locale`, senza il tipo di dato) |
|
|
311
|
+
|
|
312
|
+
Un aggregato non riconosciuto viene segnalato in console e produce una cella vuota. Le celle del
|
|
313
|
+
footer ricevono le stesse classi (`cellClass`) delle celle del body.
|
|
314
|
+
|
|
315
|
+
Il `tfoot` non è disponibile in modalità server-side (il componente riceve solo la pagina
|
|
316
|
+
corrente, gli aggregati sarebbero parziali): se richiesto viene ignorato con un avviso in console.
|
|
317
|
+
|
|
318
|
+
## Tipi di dato – `dataTypes`
|
|
319
|
+
|
|
320
|
+
Ogni colonna ha un tipo di dato (`dataType`, default `string`) che ne definisce classi, rendering
|
|
321
|
+
e valori di ordinamento/ricerca. Tipi predefiniti:
|
|
322
|
+
|
|
323
|
+
| Tipo | Descrizione | Classi delle celle |
|
|
324
|
+
|---|---|---|
|
|
325
|
+
| `string` | valore così com'è | – |
|
|
326
|
+
| `num` | numero formattato con `numbersLocaleOpts` | `textEnd numeric nowrap` |
|
|
327
|
+
| `id` | id numerico: valore grezzo, allineato a destra, non ricercabile | `textEnd numeric` |
|
|
328
|
+
| `perc` | percentuale in scala 0–100, `currPercLocaleOpts` + `%` | `textEnd numeric nowrap` |
|
|
329
|
+
| `percDecimal` | percentuale in scala 0–1 (moltiplicata per 100) | `textEnd numeric nowrap` |
|
|
330
|
+
| `currency` | formato valuta `Intl` con `currency` | `textEnd numeric nowrap` |
|
|
331
|
+
| `euro` | come `currency`, EUR forzato | `textEnd numeric nowrap` |
|
|
332
|
+
| `date` | elemento `<time>`, `datesLocaleOpts`; accetta stringhe ISO, timestamp, `Date` e oggetti data Symfony | `textEnd nowrap` |
|
|
333
|
+
| `datetime` | come `date`, con la parte ora (`timesLocaleOpts`): `<time><span class="[nowrap]">data</span> <small>ora</small></time>` | `textEnd nowrap` |
|
|
334
|
+
| `bool` | icone `boolTrueIcon`/`boolFalseIcon` (accetta anche `1`/`0`, `'true'`/`'false'`); `null` → `renderNullAs`; non ordinabile né ricercabile | stili interni delle icone |
|
|
335
|
+
| `email` | andate a capo facoltative attorno alla `@` | – |
|
|
336
|
+
|
|
337
|
+
I tipi numerici applicano `renderNaNAs` ai valori non numerici e `renderZeroAs` (se impostato)
|
|
338
|
+
agli zeri. Per tutti i tipi i valori `null`/`undefined` vengono resi come `renderNullAs`.
|
|
339
|
+
|
|
340
|
+
### Tipi personalizzati
|
|
341
|
+
|
|
342
|
+
Il parametro `dataTypes` è unito ai tipi predefiniti: una chiave già esistente sovrascrive solo
|
|
343
|
+
le proprietà indicate, una chiave nuova può estendere un tipo esistente con `inheritsFrom`.
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
dataTypes: {
|
|
347
|
+
// modifica solo le classi del tipo predefinito `num`
|
|
348
|
+
num: { cellClass: 'text-end fw-bold' },
|
|
349
|
+
|
|
350
|
+
// nuovo tipo basato su `num` (stesse classi, ordinamento, ricerca e gestione NaN/zero)
|
|
351
|
+
km: {
|
|
352
|
+
inheritsFrom: 'num',
|
|
353
|
+
render: (value, row, params) => `${value.toLocaleString(params.locale)} km`
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Proprietà di un tipo di dato (tutte facoltative):
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
{
|
|
362
|
+
headerClass, // string – classi di default del <th>
|
|
363
|
+
cellClass, // string – classi di default delle celle
|
|
364
|
+
internalCellClass, // (value, row, params) => string – classi aggiunte sempre alle celle,
|
|
365
|
+
// indipendentemente da `cellClass` (usato dal tipo `bool` per le icone)
|
|
366
|
+
render, // (value, row, params) => contenuto – non invocato per i valori null
|
|
367
|
+
sortValue, // (value, row, params) => valore per l'ordinamento
|
|
368
|
+
searchValue, // (value, row, params) => stringa per la ricerca
|
|
369
|
+
colDefaults, // Object – default forzati per le colonne di questo tipo
|
|
370
|
+
// (es. { sortable: false }), comunque sovrascrivibili nella colonna
|
|
371
|
+
inheritsFrom // string – solo tipi nuovi: tipo predefinito da estendere
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
A differenza del `render` di colonna (`(row, tr, td)`), il `render` del tipo di dato riceve per
|
|
376
|
+
primo il **valore** della cella, perché il tipo è generico rispetto al campo.
|
|
377
|
+
|
|
378
|
+
## Ordinamento
|
|
379
|
+
|
|
380
|
+
Ogni colonna con `sortable: true` (default; i tipi `bool` lo disattivano) ha un pulsante nel
|
|
381
|
+
`<th>` che cicla tra nessun ordinamento → ascendente → discendente → nessuno; l'attributo
|
|
382
|
+
`aria-sort` del `<th>` e l'icona riflettono lo stato, `title`/`aria-label` del pulsante
|
|
383
|
+
descrivono l'azione del click successivo (`labels.sortAsc/sortDesc/sortNone`). È attivo un solo
|
|
384
|
+
ordinamento per volta; il cambio di ordinamento riporta alla prima pagina.
|
|
385
|
+
|
|
386
|
+
Lato client il confronto usa i valori precalcolati per ogni riga (`sortValue` di colonna, poi del
|
|
387
|
+
tipo di dato, poi il valore grezzo): i valori vuoti (`null`, `''`, `NaN`) vanno sempre in coda,
|
|
388
|
+
i numeri sono confrontati numericamente, il resto con `localeCompare` (`locale`, collazione
|
|
389
|
+
numerica, insensibile a maiuscole e accenti). L'ordinamento è stabile.
|
|
390
|
+
|
|
391
|
+
`initialSort: { key, dir }` applica un ordinamento al caricamento; `setSort(key, dir)` lo cambia
|
|
392
|
+
via script.
|
|
393
|
+
|
|
394
|
+
## Ricerca
|
|
395
|
+
|
|
396
|
+
L'input di ricerca (`search: true`) filtra le righe dopo `searchDebounce` ms dall'ultimo tasto:
|
|
397
|
+
ogni parola del termine (separatore: spazi) deve essere contenuta nel testo di ricerca della riga,
|
|
398
|
+
composto dai valori delle colonne `searchable` (`searchValue` di colonna, poi del tipo di dato,
|
|
399
|
+
poi il valore grezzo), senza distinzione tra maiuscole e minuscole. La ricerca riporta alla prima
|
|
400
|
+
pagina; `setSearch(term)` la imposta via script.
|
|
401
|
+
|
|
402
|
+
## Paginazione
|
|
403
|
+
|
|
404
|
+
Con `perPage` > 0 (default 25) le righe vengono suddivise in pagine e sotto la tabella compare la
|
|
405
|
+
navigazione: pulsanti precedente/successivo (icone `paginationPrevIcon`/`paginationNextIcon`),
|
|
406
|
+
la prima e l'ultima pagina e `paginationDelta` pagine per lato della corrente, con puntini di
|
|
407
|
+
sospensione per le pagine omesse. La navigazione è nascosta quando c'è una sola pagina.
|
|
408
|
+
`perPage: 0` disattiva la paginazione (tutte le righe in un'unica pagina).
|
|
409
|
+
|
|
410
|
+
`goToPage(page)` cambia pagina via script; `state.page`/`state.totPages` riportano lo stato.
|
|
411
|
+
|
|
412
|
+
### Caption e paginazione
|
|
413
|
+
|
|
414
|
+
La caption è mostrata **sotto la tabella**, nella barra `tableFooter`, allineata a sinistra; a
|
|
415
|
+
destra, nella stessa barra, la navigazione delle pagine. La caption non è un elemento
|
|
416
|
+
`<caption>` (che dovrebbe stare dentro la tabella e non potrebbe ospitare la navigazione senza
|
|
417
|
+
farla leggere come nome della tabella) ma un contenitore con id, collegato alla tabella tramite
|
|
418
|
+
`aria-labelledby`: il nome accessibile della tabella è quindi lo stesso di una `<caption>`.
|
|
419
|
+
Posizioni diverse si ottengono con gli slot `caption` e `pagination` del [template](#template).
|
|
420
|
+
|
|
421
|
+
## Modalità server-side
|
|
422
|
+
|
|
423
|
+
Con `serverSide: true` il componente non elabora i dati in locale: a ogni cambio pagina,
|
|
424
|
+
ordinamento o ricerca invia una nuova richiesta GET a `jsonUrl`, aggiungendo alla query string i
|
|
425
|
+
parametri definiti in `serverParams` (quelli già presenti in `jsonUrl` vengono conservati):
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
serverParams: {
|
|
429
|
+
page: 'page', // pagina richiesta (da 1)
|
|
430
|
+
start: 'start', // indice (da 0) del primo record richiesto = (page - 1) * perPage,
|
|
431
|
+
// comodo per `LIMIT start, perPage`
|
|
432
|
+
perPage: 'perPage', // record per pagina
|
|
433
|
+
sort: 'sort', // chiave della colonna ordinata (omesso senza ordinamento)
|
|
434
|
+
dir: 'dir', // 'asc' | 'desc' (omesso senza ordinamento)
|
|
435
|
+
search: 'search' // termine di ricerca (omesso se vuoto)
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Un valore `null` omette il parametro (es. `serverParams: { start: null }` per inviare solo `page`).
|
|
440
|
+
Richiesta di esempio, seconda pagina ordinata per nome con ricerca attiva:
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
GET /api/rows.json?page=2&start=25&perPage=25&sort=name&dir=desc&search=mar
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Il JSON di risposta deve contenere le **sole righe della pagina richiesta** in `jsonDataField`,
|
|
447
|
+
il totale dei record in `totRecField` e il numero di record che soddisfano la ricerca in
|
|
448
|
+
`filteredRecField` (se assente vale il totale):
|
|
449
|
+
|
|
450
|
+
```json
|
|
451
|
+
{ "data": [ ...25 righe... ], "totRec": 1500, "filteredRec": 40 }
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Durante la richiesta il contenitore principale ha `aria-busy="true"` (la tabella viene resa
|
|
455
|
+
semitrasparente); le risposte superate da una richiesta più recente vengono ignorate. Se la
|
|
456
|
+
pagina richiesta non esiste più (il set di dati si è ridotto), viene richiesta l'ultima
|
|
457
|
+
disponibile. In questa modalità `data`, `filtered` e `pageRows` dello stato contengono solo la
|
|
458
|
+
pagina corrente e il `tfoot` non è disponibile. `serverSide` richiede `jsonUrl`: con `data` inline
|
|
459
|
+
viene ignorato con un avviso in console.
|
|
460
|
+
|
|
461
|
+
### Modalità jQuery DataTables
|
|
462
|
+
|
|
463
|
+
Con `jqDatatableMode: true` (o l'attributo `jqdatatablemode="true"`) i default dei parametri server-side diventano quelli del formato di jQuery DataTables, utile per riutilizzare endpoint già esistenti:
|
|
464
|
+
|
|
465
|
+
| Parametro | Default con `jqDatatableMode` |
|
|
466
|
+
|---|---|
|
|
467
|
+
| `jsonDataField` | `'data'` |
|
|
468
|
+
| `totRecField` | `'recordsTotal'` |
|
|
469
|
+
| `filteredRecField` | `'recordsFiltered'` |
|
|
470
|
+
| `serverSide` | `true` |
|
|
471
|
+
| `serverParams` | `{ page: null, start: 'start', perPage: 'length', sort: 'order[0][column]', dir: 'order[0][dir]', search: 'search[value]' }` |
|
|
472
|
+
|
|
473
|
+
Precedenza: `init()` > attributo HTML > `JsonTable.setDefaults()` > modalità jQuery DataTables > default predefiniti. Ogni parametro può quindi essere sovrascritto singolarmente, e `serverParams` viene unito chiave per chiave.
|
|
474
|
+
|
|
475
|
+
Oltre ai `serverParams`, in questa modalità vengono inviati anche `draw=1` e, per ogni colonna visualizzata, `columns[i][name]` (la `key`), `columns[i][searchable]` e `columns[i][orderable]`. `order[0][column]` riceve l'**indice** della colonna ordinata (tra quelle visualizzate), non la chiave. È previsto un solo criterio di ordinamento (`order[0]`).
|
|
476
|
+
|
|
477
|
+
```html
|
|
478
|
+
<json-table jsonurl="/api/rows" jqdatatablemode="true" cols='[{"key":"id"},{"key":"name"}]'></json-table>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
GET /api/rows?draw=1&columns[0][name]=id&columns[0][searchable]=true&columns[0][orderable]=true&columns[1][name]=name&…&start=25&length=25&order[0][column]=1&order[0][dir]=desc&search[value]=mar
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Template
|
|
486
|
+
|
|
487
|
+
`template` definisce il contenuto del contenitore principale come array domBuilder, in cui gli
|
|
488
|
+
elementi `{ slot: 'nome' }` vengono sostituiti dalle parti predefinite:
|
|
489
|
+
|
|
490
|
+
| Slot | Contenuto |
|
|
491
|
+
|---|---|
|
|
492
|
+
| `infoSection` | sezione info completa (testo info + input di ricerca, in riga) |
|
|
493
|
+
| `resultInfo` | solo il testo info |
|
|
494
|
+
| `search` | solo l'input di ricerca (se `search` è true) |
|
|
495
|
+
| `table` | wrapper + tabella (thead, tbody, tfoot) e barra `tableFooter` con caption e paginazione |
|
|
496
|
+
| `caption` | solo la caption (se `caption` è impostata) |
|
|
497
|
+
| `pagination` | solo la navigazione delle pagine (se `perPage` > 0) |
|
|
498
|
+
|
|
499
|
+
`caption` e `pagination` fanno parte della barra sotto la tabella dello slot `table`; se il
|
|
500
|
+
template li colloca esplicitamente altrove, vengono rimossi dalla barra (che sparisce se resta
|
|
501
|
+
vuota).
|
|
502
|
+
|
|
503
|
+
Il default `[{ slot: 'infoSection' }, { slot: 'table' }]` corrisponde alla struttura descritta
|
|
504
|
+
in [Struttura generata](#struttura-generata). Esempio con ricerca e paginazione sopra la tabella
|
|
505
|
+
e info sotto:
|
|
506
|
+
|
|
507
|
+
```javascript
|
|
508
|
+
template: [
|
|
509
|
+
{ className: 'flex gap-2 mbe-sm', children: [{ slot: 'search' }, { slot: 'pagination' }] },
|
|
510
|
+
{ slot: 'table' }, // barra sotto la tabella con la sola caption
|
|
511
|
+
{ slot: 'resultInfo' }
|
|
512
|
+
]
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Da `init()` è accettata anche una funzione `(parts, params) => array`, dove `parts` contiene le
|
|
516
|
+
parti predefinite (oggetti domBuilder) con le stesse chiavi degli slot. Gli slot non
|
|
517
|
+
riconosciuti vengono segnalati in console e ignorati.
|
|
518
|
+
|
|
519
|
+
## Struttura generata
|
|
520
|
+
|
|
521
|
+
```html
|
|
522
|
+
<json-table>
|
|
523
|
+
<section class="[wrapper] [classes.wrapper]">
|
|
524
|
+
<div class="[infoOuter] [classes.infoOuter]">
|
|
525
|
+
<div class="[info] [classes.info]">
|
|
526
|
+
<div class="[resultInfo] [classes.resultInfo]" aria-live="polite">Stai visualizzando le righe da 1 a 25, ...</div>
|
|
527
|
+
<div class="[search] [classes.search]"> <!-- solo se search: true -->
|
|
528
|
+
<input type="search" class="form-control form-control-sm" ...>
|
|
529
|
+
</div>
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
<div class="[tableOuter]">
|
|
533
|
+
<div class="[tableWrapper] table-responsive"> <!-- classes.tableWrapper -->
|
|
534
|
+
<table class="[table] table table-bordered" aria-labelledby="jt-1-caption"> <!-- classes.table; aria-labelledby solo se caption -->
|
|
535
|
+
<thead>
|
|
536
|
+
<tr>
|
|
537
|
+
<th scope="col" data-key="name" data-sortable="true" class="[sortable] ..." aria-sort="ascending">
|
|
538
|
+
<button type="button" class="[sortBtn] btn-reset" title="Ordina ..." aria-label="Nome: Ordina ...">
|
|
539
|
+
<span class="[sortTitle]">Nome</span>
|
|
540
|
+
<span class="[sortIcon]" aria-hidden="true"><svg ...></svg></span>
|
|
541
|
+
</button>
|
|
542
|
+
</th>
|
|
543
|
+
<th scope="col" data-key="active" class="text-center">Attivo</th> <!-- non ordinabile -->
|
|
544
|
+
</tr>
|
|
545
|
+
</thead>
|
|
546
|
+
<tbody>
|
|
547
|
+
<tr data-jt-idx="0">
|
|
548
|
+
<th scope="row" data-key="id" class="text-end text-numeric">1</th> <!-- rowHeading -->
|
|
549
|
+
<td data-key="name">Mario</td>
|
|
550
|
+
<td data-key="active" class="[boolCell] [boolTrue]"><svg ...></svg></td>
|
|
551
|
+
</tr>
|
|
552
|
+
<!-- oppure, senza righe: -->
|
|
553
|
+
<tr><td colspan="3" class="[empty] [classes.empty]">Nessun record trovato</td></tr>
|
|
554
|
+
</tbody>
|
|
555
|
+
<tfoot> <!-- solo se tfoot: true -->
|
|
556
|
+
<tr><td data-key="id"></td><td data-key="name">Totale</td>...</tr>
|
|
557
|
+
</tfoot>
|
|
558
|
+
</table>
|
|
559
|
+
</div>
|
|
560
|
+
<div class="[tableFooter] [classes.tableFooter]"> <!-- solo se caption o perPage > 0 -->
|
|
561
|
+
<div class="[caption] [classes.caption]" id="jt-1-caption">Utenti</div> <!-- solo se caption -->
|
|
562
|
+
<nav class="[pagination] [classes.pagination]" aria-label="Navigazione pagine"> <!-- solo se perPage > 0; hidden con una sola pagina -->
|
|
563
|
+
<ul class="[paginationList]">
|
|
564
|
+
<li class="[paginationItem]"><button type="button" class="[paginationBtn] btn-reset [paginationArrow]" data-page="prev" aria-label="Pagina precedente" disabled><svg ...></svg></button></li>
|
|
565
|
+
<li class="[paginationItem]"><button type="button" class="[paginationBtn] btn-reset" data-page="1" aria-current="page" aria-label="Pagina 1, corrente">1</button></li>
|
|
566
|
+
<li class="[paginationItem]"><button type="button" class="[paginationBtn] btn-reset" data-page="2" aria-label="Vai a pagina 2">2</button></li>
|
|
567
|
+
<li class="[paginationItem]"><span class="[paginationEllipsis]" aria-hidden="true">…</span></li>
|
|
568
|
+
<li class="[paginationItem]"><button type="button" class="[paginationBtn] btn-reset" data-page="10" aria-label="Vai a pagina 10">10</button></li>
|
|
569
|
+
<li class="[paginationItem]"><button type="button" class="[paginationBtn] btn-reset [paginationArrow]" data-page="next" aria-label="Pagina successiva"><svg ...></svg></button></li>
|
|
570
|
+
</ul>
|
|
571
|
+
</nav>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
</section>
|
|
575
|
+
</json-table>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
Le classi tra parentesi quadre sono quelle interne del CSS module. Nei `<th>` ordinabili
|
|
579
|
+
l'attributo `aria-sort` (`ascending`/`descending`) è presente solo sulla colonna ordinata;
|
|
580
|
+
`title` e `aria-label` del pulsante descrivono l'azione del click successivo.
|
|
581
|
+
|
|
582
|
+
## Custom properties CSS
|
|
583
|
+
|
|
584
|
+
Definite in `json-table.minimo.tokens.mjs` (namespace `--jt-*`):
|
|
585
|
+
|
|
586
|
+
| Proprietà | Default (token minimo) |
|
|
587
|
+
|---|---|
|
|
588
|
+
| `--jt-wrapper-margin-block` | `{size.md}` |
|
|
589
|
+
| `--jt-busy-opacity` | `.5` |
|
|
590
|
+
| `--jt-info-gap` | `{size.sm}` |
|
|
591
|
+
| `--jt-info-font-size` | `{font.size.sm}` |
|
|
592
|
+
| `--jt-info-color` | `{text.muted}` |
|
|
593
|
+
| `--jt-info-outer-padding-block-end` | `{size.xs}` |
|
|
594
|
+
| `--jt-search-max-width` | `20rem` |
|
|
595
|
+
| `--jt-thead-hover-background-color` | `color-mix(in srgb, {table.thead.background.color} 90%, {accent})` |
|
|
596
|
+
| `--jt-sort-btn-padding-block` | `{table.cell.padding.block}` |
|
|
597
|
+
| `--jt-sort-btn-padding-inline` | `{table.cell.padding.inline}` |
|
|
598
|
+
| `--jt-sort-btn-line-height` | `{table.cell.line-height}` |
|
|
599
|
+
| `--jt-sort-icon-size` | `1em` |
|
|
600
|
+
| `--jt-sort-icon-gap` | `.4em` |
|
|
601
|
+
| `--jt-sort-icon-none-opacity` | `.4` |
|
|
602
|
+
| `--jt-table-footer-padding-block-start` | `{size.xs}` |
|
|
603
|
+
| `--jt-caption-font-size` | `{font.size.sm}` |
|
|
604
|
+
| `--jt-caption-color` | `{text.muted}` |
|
|
605
|
+
| `--jt-pagination-border-width` | `1px` |
|
|
606
|
+
| `--jt-pagination-border-color` | `{table.border.color}` |
|
|
607
|
+
| `--jt-pagination-radius` | `{radius.xxs}` |
|
|
608
|
+
| `--jt-pagination-font-size` | `{font.size.sm}` |
|
|
609
|
+
| `--jt-pagination-line-height` | `{table.cell.line-height}` |
|
|
610
|
+
| `--jt-pagination-btn-min-width` | `2.2em` |
|
|
611
|
+
| `--jt-pagination-btn-padding-block` | `{table.cell.padding.block}` |
|
|
612
|
+
| `--jt-pagination-btn-padding-inline` | `{table.cell.padding.inline}` |
|
|
613
|
+
| `--jt-pagination-icon-size` | `1em` |
|
|
614
|
+
| `--jt-pagination-current-background-color` | `{table.thead.background.color}` |
|
|
615
|
+
| `--jt-pagination-current-font-weight` | `{font.weight.semibold}` |
|
|
616
|
+
| `--jt-pagination-hover-background-color` | `{table.body.tr.hover-bg-color}` |
|
|
617
|
+
| `--jt-pagination-disabled-opacity` | `.5` |
|
|
618
|
+
| `--jt-bool-icon-size` | `1.1em` |
|
|
619
|
+
| `--jt-bool-true-color` | `{status.success.color}` |
|
|
620
|
+
| `--jt-bool-false-color` | `{status.danger.color}` |
|
|
621
|
+
| `--jt-empty-color` | `{text.muted}` |
|
|
622
|
+
| `--jt-empty-padding-block` | `{size.sm}` |
|