@massimo-cassandro/minimo 1.41.7 → 1.41.9

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.
@@ -5,14 +5,14 @@ export default {
5
5
  $type: 'dimension'
6
6
  }
7
7
  },
8
- grid: {
9
- cols: {
10
- $value: 3,
11
- $type: 'number'
12
- },
13
- 'min-col-width': {
14
- $value: '6rem',
15
- $type: 'dimension'
16
- }
17
- }
8
+ // grid: {
9
+ // cols: {
10
+ // $value: 3,
11
+ // $type: 'number'
12
+ // },
13
+ // 'min-col-width': {
14
+ // $value: '6rem',
15
+ // $type: 'dimension'
16
+ // }
17
+ // }
18
18
  };
@@ -1 +1,7 @@
1
1
  import './index.css';
2
+
3
+
4
+ import { innerNav, dismissAlerts } from '@massimo-cassandro/minimo';
5
+
6
+ innerNav();
7
+ dismissAlerts();
@@ -0,0 +1,9 @@
1
+ # CLAUDE.md
2
+
3
+ ## Contesto di dominio
4
+
5
+ ...descrizione...
6
+
7
+ Backend in symfony, frontend nella dir `/app`
8
+
9
+ vedi anche `app/CLAUDE.md`
@@ -1,5 +1,5 @@
1
1
  // webpack.config.mjs __project_name__
2
- // v.3.1
2
+ // v.3.2
3
3
  import path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import webpack from 'webpack';
@@ -30,6 +30,20 @@ import { svgRules } from './webpack-config-modules/svg-rules.mjs';
30
30
  // import { InlineCriticalCssPlugin } from './webpack-config-modules/inline-critical-css.mjs';
31
31
 
32
32
 
33
+ // =>> entries
34
+ // NB: percorsi dalla root del progetto
35
+ const entries = {
36
+ 'index': './app/index.js',
37
+
38
+ 'error-pages': './app/error-pages/error-pages.js',
39
+
40
+ // css critici da includere inline nei template html: il suffisso `.critical` nel nome
41
+ // della entry attiva l'istanza PurgeCSS dedicata con purge stretto
42
+ // (vedi purgecss-setup.mjs) e la regola dedicata in css-rules.mjs
43
+ 'layout.critical': './app/css/layout.critical.css'
44
+
45
+ };
46
+
33
47
  // --- config ---
34
48
  const __filename = fileURLToPath(import.meta.url)
35
49
  ,__dirname = path.dirname(__filename);
@@ -132,7 +146,7 @@ const isDevelopment = process.env.NODE_ENV === 'development'
132
146
  // PurgeCSS e plugin custom properties lavorano sugli asset css finali:
133
147
  // in dev, con `false`, sono entrambi disattivati (build più veloce, css
134
148
  // iniettato con style-loader; se `useCustomPropsPlugin` è true le definizioni
135
- // vengono aggiunte per intero alle entry, vedi `entries`).
149
+ // vengono aggiunte per intero alle entry, vedi `parsedEntries`).
136
150
  // Con `true` (a scopo di test) sono attivi come in produzione (secondo i
137
151
  // rispettivi flag `usePurgeCss` / `useCustomPropsPlugin`) e `inlineCssInDevMode`
138
152
  // viene forzato a false
@@ -256,31 +270,18 @@ const CopyWebpackPluginPatterns = [
256
270
  /******************************/
257
271
  /** CONFIG **/
258
272
 
259
- // =>> entries
260
- // NB: percorsi dalla root del progetto
261
- const projectEntries = {
262
- 'index': './app/index.js',
263
-
264
- 'error-pages': './app/error-pages/error-pages.js',
265
-
266
- // css critici da includere inline nei template html: il suffisso `.critical` nel nome
267
- // della entry attiva l'istanza PurgeCSS dedicata con purge stretto
268
- // (vedi purgecss-setup.mjs) e la regola dedicata in css-rules.mjs
269
- 'layout.critical': './app/css/layout.critical.css'
270
-
271
- };
272
273
 
273
274
  // dev senza plugin custom properties (`purgeCssInDev: false`, css iniettato con
274
275
  // style-loader, quindi nessun asset css in cui il plugin possa inserire le
275
276
  // definizioni): il file master con TUTTE le custom properties viene aggiunto come
276
277
  // primo modulo di ogni entry (escluse le `.critical`). Non serve nessuna
277
278
  // modifica ai template
278
- const entries = (useCustomPropsPlugin && isDevelopment && !purgeCssInDev)
279
- ? Object.fromEntries(Object.entries(projectEntries).map(([name, entry]) => [
279
+ const parsedEntries = (useCustomPropsPlugin && isDevelopment && !purgeCssInDev)
280
+ ? Object.fromEntries(Object.entries(entries).map(([name, entry]) => [
280
281
  name,
281
282
  /\.critical/.test(name) ? entry : [customPropsFile, ...[entry].flat()]
282
283
  ]))
283
- : projectEntries;
284
+ : entries;
284
285
 
285
286
 
286
287
  // regexp per gli asset css compilati di una entry: corrisponde a `nome.css` (dev con
@@ -291,9 +292,9 @@ const cssAssetRegexp = (entryName) =>
291
292
  new RegExp(`^${entryName.replace(/[.+?^${}()|[\]\\]/g, '\\$&')}(\\.[^./]+)?\\.css$`);
292
293
 
293
294
  // set del CustomPropsPurgeCssPlugin (vedi `sets` nei `plugins`). L'elenco delle entry
294
- // css è derivato da `projectEntries`: aggiungere una entry lì è sufficiente
295
- const cssEntries = Object.keys(projectEntries).filter(name => !/\.critical/.test(name))
296
- ,criticalEntries = Object.keys(projectEntries).filter(name => /\.critical/.test(name))
295
+ // css è derivato da `entries`: aggiungere una entry lì è sufficiente
296
+ const cssEntries = Object.keys(entries).filter(name => !/\.critical/.test(name))
297
+ ,criticalEntries = Object.keys(entries).filter(name => /\.critical/.test(name))
297
298
  ,customPropsSets = [
298
299
 
299
300
  // i critical css sono inline nei template: restano autosufficienti
@@ -343,7 +344,7 @@ const config = {
343
344
 
344
345
  devtool: isDevelopment ? 'inline-source-map' : false,
345
346
 
346
- entry: entries,
347
+ entry: parsedEntries,
347
348
 
348
349
  output: {
349
350
  path: output_dir,
@@ -687,7 +688,7 @@ const config = {
687
688
  // NB: evitare i wildcard `*`: 'index.*.css' non corrisponde a 'index.css'
688
689
  // (dev con WEBPACK_SERVE) e corrisponde invece a 'index.critical.<hash>.css'.
689
690
  // I set sono costruiti in `customPropsSets` (sopra) a partire da
690
- // `projectEntries`, in base a `useSharedChunk`: aggiungendo una entry
691
+ // `entries`, in base a `useSharedChunk`: aggiungendo una entry
691
692
  // non serve modificare nulla qui
692
693
  sets: customPropsSets,
693
694
 
@@ -10,18 +10,6 @@ Le versioni precedenti sono conservate in `archived/webpack-config-v2/` del repo
10
10
 
11
11
  ## Fix e upg da verificare e applicare a tutte le versioni
12
12
  * WebpackManifestPlugin: impostare `removeKeyHash: /\?.*$/, // /([a-f0-9]{32}\.?)/gi, // /(\?as_asset)$/,`, sostituendo eventuali impostazione `removeKeyHash: true` se presente
13
- * cacheGroup `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:
14
- ```js
15
- const excluded_paths = [
16
- 'minimo/src/components/unsplash-page',
17
- 'node_modules/blurhash', // peer dep usata solo da unsplash-page
18
- ];
19
- const excludedRegexp = new RegExp(
20
- excluded_paths.map(p => `${sep}${p.replace(/\//g, sep)}${sep}`).join('|')
21
- );
22
- const modulePath = module.nameForCondition?.() ?? '';
23
- ```
24
- e sostituire il `return` con `return !excludedRegexp.test(modulePath) && pathsRegexp.test(modulePath);`. Aggiungere anche al commento sopra il cacheGroup la nota sull'esclusione.
25
13
 
26
14
 
27
15
  ## Da v2 a v3
@@ -255,3 +243,37 @@ Con `useSharedChunk = false` i template **non** devono referenziare `shared.css`
255
243
  1. `NODE_ENV=production npx webpack --config ./webpack.config.mjs --stats minimal`: nessun errore o warning del plugin.
256
244
  2. Nel css con le definizioni non compaiono props di web components non importati (es. `--jt-*`, `--sdt-*`); per un componente importato con `?raw` le sue props sono presenti.
257
245
  3. Con `useSharedChunk = false`: in `build/` non esistono `shared.*` né `runtime.*` e le definizioni sono nel solo css di `mainEntry`.
246
+
247
+ ### 6. Fix da applicare (solo con `useSharedChunk: true`)
248
+
249
+ * cacheGroup `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:
250
+ ```js
251
+ const excluded_paths = [
252
+ 'minimo/src/components/unsplash-page',
253
+ 'node_modules/blurhash', // peer dep usata solo da unsplash-page
254
+ ];
255
+ const excludedRegexp = new RegExp(
256
+ excluded_paths.map(p => `${sep}${p.replace(/\//g, sep)}${sep}`).join('|')
257
+ );
258
+ const modulePath = module.nameForCondition?.() ?? '';
259
+ ```
260
+ e sostituire il `return` con `return !excludedRegexp.test(modulePath) && pathsRegexp.test(modulePath);`. Aggiungere anche al commento sopra il cacheGroup la nota sull'esclusione.
261
+
262
+
263
+ ## Da v3.1 a v3.2
264
+
265
+ Novità della v3.2: nessuna modifica di comportamento, solo riorganizzazione per rendere più agevole l'uso del config. Il blocco delle entry del progetto è spostato in cima al file (subito dopo gli import, prima di `// --- config ---`) e rinominato da `projectEntries` a `entries`; la costante `entries` che conteneva le entry elaborate per il dev (con il file master delle custom properties) è rinominata `parsedEntries`. Così le entry, che sono la parte da personalizzare in ogni progetto, si trovano subito e non vanno cercate a metà file. I moduli in `webpack-config-modules/` non cambiano.
266
+
267
+ Prompt per aggiornare un progetto esistente:
268
+
269
+ ```text
270
+ Aggiorna il mio webpack.config.mjs dalla v.3.1 alla v.3.2 con queste modifiche, senza toccare altro:
271
+
272
+ 1. Riga 2: `// v.3.1` → `// v.3.2`.
273
+ 2. Rinomina l'attuale costante `entries` (quella con la logica `useCustomPropsPlugin && isDevelopment && !purgeCssInDev`, che aggiunge `customPropsFile` come primo modulo delle entry non `.critical`) in `parsedEntries`. Aggiorna i riferimenti: `entry: entries` → `entry: parsedEntries` nella config e i commenti che citano `entries` in questo senso (es. nel commento sopra `purgeCssInDev`: "vedi `entries`" → "vedi `parsedEntries`").
274
+ 3. Rinomina `projectEntries` in `entries` in tutto il file (definizione, uso in `parsedEntries`, `cssEntries`, `criticalEntries` e commenti).
275
+ 4. Sposta l'intero blocco `// =>> entries` + `const entries = { ... };` (con le entry già presenti nel progetto, che vanno mantenute così come sono) in cima al file, subito dopo gli import e prima di `// --- config ---`, lasciando due righe vuote prima del blocco e una dopo.
276
+ 5. Verifica con `grep -n "projectEntries" webpack.config.mjs` (nessun risultato) e con `NODE_ENV=production npx webpack --config ./webpack.config.mjs --stats minimal` (nessun errore).
277
+
278
+ Mostrami il diff prima di salvare.
279
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "1.41.7",
3
+ "version": "1.41.9",
4
4
  "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/css/grid.css CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  .grid {
4
4
  --_cols: 1;
5
- /* --_min-col-width: calc((100% - calc(var(--_cols) * var(--flex-grid-gap))) / var(--grid-cols)); */
5
+ /* --_min-col-width: calc((100% - calc(var(--_cols) * var(--flex-grid-gap))) / var(--grid-cols)); NB custom prop rimossa */
6
6
 
7
7
  display: grid;
8
- /* grid-template-columns: repeat(var(--_cols), minmax(var(--grid-min-col-width), 1fr)); */
8
+ /* grid-template-columns: repeat(var(--_cols), minmax(var(--grid-min-col-width), 1fr)); NB custom prop rimossa */
9
9
  grid-template-columns: repeat(var(--_cols), minmax(0, 1fr));
10
10
  grid-auto-flow: row;
11
11
  gap: var(--flex-grid-gap);
@@ -1,6 +1,7 @@
1
1
 
2
2
  h1, h2, h3, .h3, h4, .h4 {
3
- line-height: 1.2;
3
+ font-family: var(--headings-font-family);
4
+ line-height: var(--headings-font-weight);
4
5
  color: var(--headings-color);
5
6
  text-align: var(--headings-text-align);
6
7
  }
package/src/css/text.css CHANGED
@@ -29,6 +29,12 @@ em, .italic {
29
29
  text-overflow: ellipsis;
30
30
  white-space: nowrap;
31
31
  }
32
+ .text-upper {
33
+ text-transform: uppercase;
34
+ }
35
+ .text-lower {
36
+ text-transform: lowercase;
37
+ }
32
38
 
33
39
  /* .text-nowrap-dskt {
34
40
  @media (--media-large-viewport-up) {
@@ -21,7 +21,7 @@ tablet alt:
21
21
  53.75rem : 860px medium (tablet)
22
22
  */
23
23
 
24
- /* @custom-media --media-xxlarge-viewport-up (width >= 90rem); */
24
+ @custom-media --media-xxlarge-viewport-up (width >= 90rem);
25
25
  @custom-media --media-xlarge-viewport-up (width >= 80rem);
26
26
  @custom-media --media-large-viewport-up (width >= 64rem);
27
27
  @custom-media --media-medium-viewport-up (width >= 47.5rem);
@@ -37,4 +37,4 @@ tablet alt:
37
37
  @custom-media --media-xlarge-viewport-down (max-width: 80rem);
38
38
  @custom-media --media-large-viewport-down (max-width: 64rem);
39
39
  @custom-media --media-medium-viewport-down (max-width: 47.5rem);
40
- /* @custom-media --media-small-viewport-down (max-width: 25rem); */
40
+ @custom-media --media-small-viewport-down (max-width: 25rem);
@@ -205,9 +205,6 @@
205
205
  --form-validation-outline-offset: -3px;
206
206
  --form-validation-outline-width: 3px;
207
207
 
208
- --grid-cols: 3;
209
- --grid-min-col-width: 6rem;
210
-
211
208
  --headings-color: var(--text-color);
212
209
  --headings-font-family: var(--font-family);
213
210
  --headings-font-weight: var(--font-weight-semibold);
@@ -0,0 +1,100 @@
1
+
2
+
3
+ # Parametri Colonne – `cols`
4
+
5
+ Parametro **obbligatorio**: array di oggetti, uno per colonna:
6
+
7
+ ```javascript
8
+ cols: [
9
+ {
10
+ key, // string – OBBLIGATORIO: chiave dell'oggetto riga (ammessa la notazione con
11
+ // punto per valori annidati, es. 'owner.name')
12
+ title, // contenuto del <th>: testo, HTML, Node, array domBuilder o funzione.
13
+ // Nelle colonne ordinabili è il testo del pulsante. Default: la chiave
14
+ dataType, // string – tipo di dato, una delle chiavi di `dataTypes`; `type` è un alias
15
+ // equivalente. Default: 'string'
16
+ render, // (row, tr, td) => contenuto | stringa mustache-like – vedi sotto. Default: null
17
+ tfootRender, // (rows, td) => contenuto | aggregato '@…' | stringa statica – contenuto della
18
+ // cella del tfoot (solo se `tfoot` è true). null = cella vuota. Default: null
19
+ rowHeading, // boolean – se true la cella è un'intestazione di riga (th[scope=row]).
20
+ // Default: false
21
+ searchable, // boolean – abilita la ricerca sulla colonna. Default: true
22
+ sortable, // boolean – abilita l'ordinamento sulla colonna. Default: true
23
+ sortValue, // valore | row => valore – valore usato per l'ordinamento; prevale su quello
24
+ // del tipo di dato. Default: non impostato (tipo di dato / valore grezzo)
25
+ searchValue, // idem, per la ricerca. Default: non impostato
26
+ condition, // boolean | params => boolean – se false la colonna non viene renderizzata.
27
+ // Default: true
28
+ headerClass, // string – classi del <th>, in sostituzione di quelle del tipo di dato
29
+ cellClass // string – classi delle celle body/tfoot, in sostituzione di quelle del tipo.
30
+ // Se solo una tra `headerClass` e `cellClass` è presente, l'altra assume lo
31
+ // stesso valore. Default: null (classi del tipo di dato)
32
+ },
33
+ ...
34
+ ]
35
+ ```
36
+
37
+ ## `render`
38
+
39
+ Funzione con argomenti posizionali, tutti facoltativi:
40
+
41
+ ```javascript
42
+ render: (row, tr, td) => contenuto
43
+ ```
44
+
45
+ - `row` – l'oggetto dati della riga
46
+ - `tr` – l'elemento `<tr>` della riga
47
+ - `td` – l'elemento della cella (`<td>` o `<th scope="row">`)
48
+
49
+ Il valore restituito è il contenuto della cella: testo, stringa HTML, numero, Node o array
50
+ domBuilder. Casi particolari:
51
+
52
+ - `undefined` (nessun `return`): la cella viene renderizzata dal tipo di dato, come se `render`
53
+ non fosse definito. Utile per decorare soltanto `td`/`tr` (classi, attributi, `title`):
54
+
55
+ ```javascript
56
+ render: (row, tr, td) => { if (row.amount < 0) td.classList.add('text-danger'); }
57
+ ```
58
+
59
+ - `null`: viene mostrato `renderNullAs`
60
+
61
+ Da attributo HTML (o da script, per i casi semplici) `render` può essere una stringa
62
+ mustache-like: ogni segnaposto `[[key]]` viene sostituito con il valore corrispondente della
63
+ riga (chiavi annidate ammesse; i valori null diventano `renderNullAs`):
64
+
65
+ ```javascript
66
+ render: '<a href="/users/[[id]]">[[owner.name]]</a>'
67
+ ```
68
+
69
+ Le stringhe HTML vengono inserite tramite la Sanitizer API (`Element.setHTML`, dove supportata):
70
+ script, attributi handler di eventi e URL `javascript:` vengono rimossi, mentre `class`, `id`,
71
+ `style` e `data-*` sono conservati.
72
+
73
+ ## `tfootRender`
74
+
75
+ Con `tfoot: true` ogni colonna produce una cella nel footer, il cui contenuto è dato da
76
+ `tfootRender`:
77
+
78
+ - funzione `(rows, td) => contenuto`, dove `rows` è l'intero set filtrato (oppure i soli record
79
+ della pagina corrente se `updateFooterOnPageChange` è true)
80
+ - uno degli aggregati predefiniti (vedi sotto)
81
+ - qualsiasi altra stringa: contenuto statico (es. l'etichetta `'Totale'`)
82
+ - `null`: cella vuota
83
+
84
+ Aggregati predefiniti, calcolati sui valori **numerici** della colonna (i valori non numerici
85
+ vengono ignorati) e, tranne `@count`, formattati dal tipo di dato della colonna (es. come valuta
86
+ per `euro`, con `%` per `perc`):
87
+
88
+ | Aggregato | Valore |
89
+ |---|---|
90
+ | `'@sum'` | somma |
91
+ | `'@avg'` | media aritmetica |
92
+ | `'@min'` | minimo |
93
+ | `'@max'` | massimo |
94
+ | `'@count'` | numero di valori numerici (formattato con `locale`, senza il tipo di dato) |
95
+
96
+ Un aggregato non riconosciuto viene segnalato in console e produce una cella vuota. Le celle del
97
+ footer ricevono le stesse classi (`cellClass`) delle celle del body.
98
+
99
+ Il `tfoot` non è disponibile in modalità server-side (il componente riceve solo la pagina
100
+ corrente, gli aggregati sarebbero parziali): se richiesto viene ignorato con un avviso in console.
@@ -1,6 +1,9 @@
1
1
  # `<json-table>` – datatable da JSON
2
2
 
3
3
  * [Parametri (include `classes` e `labels`)](jt-docs/parametri.md)
4
+ * [Parametri colonne](jt-docs/parametri-colonne.md)
5
+
6
+ ---
4
7
 
5
8
  **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
9
 
@@ -216,105 +219,6 @@ el.init({ jsonUrl: '/api/rows.json', cols: [...] });
216
219
 
217
220
 
218
221
 
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
222
  ## Tipi di dato – `dataTypes`
319
223
 
320
224
  Ogni colonna ha un tipo di dato (`dataType`, default `string`) che ne definisce classi, rendering
@@ -863,6 +863,7 @@ export type MinimoClass =
863
863
  | 'text-danger'
864
864
  | 'text-end'
865
865
  | 'text-info'
866
+ | 'text-lower'
866
867
  | 'text-md'
867
868
  | 'text-monospace'
868
869
  | 'text-muted'
@@ -874,6 +875,7 @@ export type MinimoClass =
874
875
  | 'text-sm'
875
876
  | 'text-success'
876
877
  | 'text-truncate'
878
+ | 'text-upper'
877
879
  | 'text-warning'
878
880
  | 'text-xs'
879
881
  | 'valign-middle'