@massimo-cassandro/minimo 1.32.3 → 1.33.0
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/dev-tools/starter-kit/package-tpl.json +1 -1
- package/dev-tools/starter-kit/source_files/CLAUDE-frontend.md +1 -0
- package/dev-tools/starter-kit/source_files/_private/pwd.md +16 -0
- package/dev-tools/starter-kit/source_files/_private/sf.md +12 -0
- package/dev-tools/starter-kit/source_files/stylelint.config.mjs +1 -1
- package/dev-tools/starter-kit/starter-install.sh +102 -8
- package/dev-tools/starter-kit/webpack.config.mjs +6 -14
- package/dev-tools/stylelint-config/README.md +45 -0
- package/dev-tools/stylelint-config/index.js +184 -0
- package/package.json +11 -2
- package/src/custom-properties.css +7 -0
- package/src/web-components/json-table/json-table-component.js +283 -0
- package/src/web-components/json-table/json-table-component.module.css +57 -0
- package/src/web-components/json-table/json-table.minimo.tokens.mjs +48 -0
- package/src/web-components/json-table/readme.md +243 -0
- package/src/web-components/json-table/src/defaults.js +105 -0
- package/src/web-components/json-table/src/get-data.js +57 -0
- package/src/web-components/json-table/src/info-section.js +72 -0
- package/src/web-components/json-table/src/main-builder.js +95 -0
- package/src/web-components/json-table/src/resolve-params.js +96 -0
- package/src/web-components/json-table/src/update-info.js +48 -0
- package/src/web-components/s-datatable-component/docs.md +62 -0
- package/src/web-components/s-datatable-component/s-datatable-component.js +9 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*! minimo - json-table: defaults */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Function returning the text of the info area.
|
|
5
|
+
* @callback InfoTextFn
|
|
6
|
+
* @param {number} shown - Number of rows currently displayed
|
|
7
|
+
* @param {number} total - Total number of rows in the data set
|
|
8
|
+
* @returns {string|Node} Plain text, an HTML string or a DOM node
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `<json-table>` parameters.
|
|
13
|
+
*
|
|
14
|
+
* Every parameter can be set either as an HTML attribute of the `<json-table>` element
|
|
15
|
+
* (attribute names are case-insensitive, so `jsonurl="…"` and `jsonUrl="…"` are equivalent)
|
|
16
|
+
* or as a property of the object passed to `init()`. Values that are functions
|
|
17
|
+
* (e.g. `caption`, `infoText`) can only be set via `init()`.
|
|
18
|
+
*
|
|
19
|
+
* Precedence: `init()` > HTML attribute > `JsonTable.setDefaults()` > built-in default.
|
|
20
|
+
*
|
|
21
|
+
* TODO parametri `cols`, data-types, locale, icone, ordinamento, paginazione: da aggiungere negli step successivi
|
|
22
|
+
*
|
|
23
|
+
* @typedef {Object} JsonTableParams
|
|
24
|
+
* @property {boolean} [debug] - Logs resolved params and generated elements to the console (default: false)
|
|
25
|
+
* @property {string|null} [jsonUrl] - URL of the JSON to fetch. Ignored when `data` is set (default: null)
|
|
26
|
+
* @property {string|null} [jsonDataField] - Key of the fetched JSON holding the rows array (e.g. `{ data: [...] }`);
|
|
27
|
+
* `null` or an empty string means the JSON root is the rows array itself (default: 'data')
|
|
28
|
+
* @property {Array<Object>|null} [data] - Inline rows (array of plain objects); takes precedence over `jsonUrl`.
|
|
29
|
+
* As an HTML attribute it must be a JSON string, which is always treated as the rows array itself
|
|
30
|
+
* (`jsonDataField` is ignored) (default: null)
|
|
31
|
+
* @property {string|Function|null} [caption] - Table caption: a string (plain text or HTML) or, via `init()` only,
|
|
32
|
+
* a function returning a string or a Node (default: null)
|
|
33
|
+
* @property {boolean} [search] - Whether to render the search input (default: true)
|
|
34
|
+
* @property {string|null} [searchInputClass] - Class(es) of the search input, replacing the default one (default: 'form-control')
|
|
35
|
+
* @property {string} [searchInputTitle] - `title` attribute of the search input (default: 'Filtra record: inserisci un termine per eseguire la ricerca')
|
|
36
|
+
* @property {string} [searchInputPlaceholder] - Placeholder of the search input (default: 'Inserisci il termine da cercare')
|
|
37
|
+
* @property {string} [searchInputAriaLabel] - `aria-label` of the search input (default: 'Filtra risultati')
|
|
38
|
+
* @property {string|null} [tableId] - `id` attribute of the `<table>` element (default: null)
|
|
39
|
+
* @property {string|null} [tableWrapperClass] - Class(es) of the div wrapping the table, replacing the default one (default: 'table-responsive')
|
|
40
|
+
* @property {string|null} [tableClass] - Class(es) of the `<table>` element, replacing the default ones (default: 'table table-bordered')
|
|
41
|
+
* @property {string|null} [mainWrapperExtraClass] - Extra class(es) added to the main wrapper (info section + table) (default: null)
|
|
42
|
+
* @property {string|null} [outerInfoExtraClass] - Extra class(es) added to the outer info container (default: null)
|
|
43
|
+
* @property {string|null} [infoExtraClass] - Extra class(es) added to the info container (default: null)
|
|
44
|
+
* @property {InfoTextFn} [infoText] - Function returning the info text: plain text, an HTML string or a Node
|
|
45
|
+
* (default: Italian text "Visualizzate N righe su M" / "Nessun record")
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Built-in defaults.
|
|
50
|
+
*
|
|
51
|
+
* @type {Required<JsonTableParams>}
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* {
|
|
55
|
+
* debug: false,
|
|
56
|
+
* jsonUrl: null,
|
|
57
|
+
* jsonDataField: 'data',
|
|
58
|
+
* data: null,
|
|
59
|
+
* caption: null,
|
|
60
|
+
* search: true,
|
|
61
|
+
* searchInputClass: 'form-control',
|
|
62
|
+
* searchInputTitle: 'Filtra record: inserisci un termine per eseguire la ricerca',
|
|
63
|
+
* searchInputPlaceholder: 'Inserisci il termine da cercare',
|
|
64
|
+
* searchInputAriaLabel: 'Filtra risultati',
|
|
65
|
+
* tableId: null,
|
|
66
|
+
* tableWrapperClass: 'table-responsive',
|
|
67
|
+
* tableClass: 'table table-bordered',
|
|
68
|
+
* mainWrapperExtraClass: null,
|
|
69
|
+
* outerInfoExtraClass: null,
|
|
70
|
+
* infoExtraClass: null,
|
|
71
|
+
* infoText: (shown, total) => '…'
|
|
72
|
+
* }
|
|
73
|
+
*/
|
|
74
|
+
export const defaults = {
|
|
75
|
+
debug: false,
|
|
76
|
+
|
|
77
|
+
jsonUrl: null,
|
|
78
|
+
jsonDataField: 'data',
|
|
79
|
+
data: null,
|
|
80
|
+
|
|
81
|
+
caption: null,
|
|
82
|
+
|
|
83
|
+
search: true,
|
|
84
|
+
searchInputClass: 'form-control',
|
|
85
|
+
searchInputTitle: 'Filtra record: inserisci un termine per eseguire la ricerca',
|
|
86
|
+
searchInputPlaceholder: 'Inserisci il termine da cercare',
|
|
87
|
+
searchInputAriaLabel: 'Filtra risultati',
|
|
88
|
+
|
|
89
|
+
tableId: null,
|
|
90
|
+
tableWrapperClass: 'table-responsive',
|
|
91
|
+
tableClass: 'table table-bordered',
|
|
92
|
+
|
|
93
|
+
mainWrapperExtraClass: null,
|
|
94
|
+
outerInfoExtraClass: null,
|
|
95
|
+
infoExtraClass: null,
|
|
96
|
+
|
|
97
|
+
infoText: (shown, total) => {
|
|
98
|
+
if (total === 0) {
|
|
99
|
+
return 'Nessun record';
|
|
100
|
+
}
|
|
101
|
+
return shown === 1
|
|
102
|
+
? `Visualizzata <strong>${shown}</strong> riga su <strong>${total}</strong>`
|
|
103
|
+
: `Visualizzate <strong>${shown}</strong> righe su <strong>${total}</strong>`;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*! minimo - json-table: data acquisition */
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('./defaults.js').JsonTableParams} JsonTableParams */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Retrieves the rows array from the configured source.
|
|
7
|
+
*
|
|
8
|
+
* `data` takes precedence over `jsonUrl`. With `jsonUrl` the JSON is fetched and the rows are
|
|
9
|
+
* read from the `jsonDataField` key (`null`/`''` = the JSON root is the array itself).
|
|
10
|
+
* Returns `null` when no source is configured.
|
|
11
|
+
*
|
|
12
|
+
* @param {JsonTableParams} params - Resolved params (see `resolve-params.js`)
|
|
13
|
+
* @returns {Promise<Array<Object>|null>} The rows array, or `null` when neither `data` nor `jsonUrl` is set
|
|
14
|
+
* @throws {Error} When `data` is not an array, on HTTP/network errors, or when the fetched JSON
|
|
15
|
+
* does not contain an array at `jsonDataField`
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // inline data (jsonUrl ignored)
|
|
19
|
+
* await getData({ ...params, data: [{ id: 1 }], jsonUrl: '/ignored.json' }); // → [{ id: 1 }]
|
|
20
|
+
*
|
|
21
|
+
* // fetched JSON: { data: [...] } (jsonDataField default: 'data')
|
|
22
|
+
* await getData({ ...params, jsonUrl: '/api/rows.json' });
|
|
23
|
+
*
|
|
24
|
+
* // fetched JSON whose root is the array itself
|
|
25
|
+
* await getData({ ...params, jsonUrl: '/api/rows.json', jsonDataField: null });
|
|
26
|
+
*/
|
|
27
|
+
export async function getData(params) {
|
|
28
|
+
|
|
29
|
+
if (params.data != null) {
|
|
30
|
+
if (!Array.isArray(params.data)) {
|
|
31
|
+
throw new Error('[json-table] `data` deve essere un array di oggetti');
|
|
32
|
+
}
|
|
33
|
+
return params.data;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (params.jsonUrl) {
|
|
37
|
+
const response = await fetch(params.jsonUrl);
|
|
38
|
+
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`[json-table] ${params.jsonUrl}: risposta HTTP ${response.status}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const json = await response.json();
|
|
44
|
+
const field = params.jsonDataField;
|
|
45
|
+
const rows = (field == null || field === '') ? json : json?.[field];
|
|
46
|
+
|
|
47
|
+
if (!Array.isArray(rows)) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`[json-table] ${params.jsonUrl}: ` +
|
|
50
|
+
(field ? `il campo \`${field}\` non contiene un array` : 'la risposta non è un array')
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return rows;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*! minimo - json-table: info section */
|
|
2
|
+
|
|
3
|
+
import * as styles from '../json-table-component.module.css';
|
|
4
|
+
import { classnames } from '../../../utilities/classnames.js';
|
|
5
|
+
|
|
6
|
+
/** @typedef {import('./defaults.js').JsonTableParams} JsonTableParams */
|
|
7
|
+
/** @typedef {import('./main-builder.js').JsonTableElements} JsonTableElements */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builds the domBuilder configuration of the info section (info text + search input).
|
|
11
|
+
*
|
|
12
|
+
* Generated structure:
|
|
13
|
+
* ```
|
|
14
|
+
* div.infoOuter[outerInfoExtraClass]
|
|
15
|
+
* div.info[infoExtraClass]
|
|
16
|
+
* div.resultInfo ← info text (see `update-info.js`)
|
|
17
|
+
* div.search > input[type=search] ← only if `params.search` is true
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* References to the generated elements are stored in `elements`
|
|
21
|
+
* (`infoOuter`, `info`, `resultInfo`, `searchInput`).
|
|
22
|
+
*
|
|
23
|
+
* TODO listener sull'input di ricerca: da impostare quando verrà implementata la ricerca
|
|
24
|
+
*
|
|
25
|
+
* @param {JsonTableParams} params - Resolved params
|
|
26
|
+
* @param {JsonTableElements} elements - Object collecting the generated elements (mutated)
|
|
27
|
+
* @returns {DomBuilderItem[]} domBuilder items to be used as children of the main wrapper
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const elements = {};
|
|
31
|
+
* domBuilder([{ tag: 'section', children: [...infoSection(params, elements)] }], host);
|
|
32
|
+
* elements.searchInput; // → HTMLInputElement (when params.search is true)
|
|
33
|
+
*/
|
|
34
|
+
export function infoSection(params, elements) {
|
|
35
|
+
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
className: classnames(styles.infoOuter, params.outerInfoExtraClass),
|
|
39
|
+
callback: el => { elements.infoOuter = /** @type {HTMLElement} */ (el); },
|
|
40
|
+
children: [
|
|
41
|
+
{
|
|
42
|
+
className: classnames(styles.info, params.infoExtraClass),
|
|
43
|
+
callback: el => { elements.info = /** @type {HTMLElement} */ (el); },
|
|
44
|
+
children: [
|
|
45
|
+
{
|
|
46
|
+
className: styles.resultInfo,
|
|
47
|
+
attrs: { 'aria-live': 'polite' },
|
|
48
|
+
callback: el => { elements.resultInfo = /** @type {HTMLElement} */ (el); }
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
condition: params.search,
|
|
52
|
+
className: styles.search,
|
|
53
|
+
children: [
|
|
54
|
+
{
|
|
55
|
+
tag: 'input',
|
|
56
|
+
className: classnames(params.searchInputClass),
|
|
57
|
+
attrs: {
|
|
58
|
+
type: 'search',
|
|
59
|
+
title: params.searchInputTitle,
|
|
60
|
+
placeholder: params.searchInputPlaceholder,
|
|
61
|
+
'aria-label': params.searchInputAriaLabel
|
|
62
|
+
},
|
|
63
|
+
callback: el => { elements.searchInput = /** @type {HTMLInputElement} */ (el); }
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
];
|
|
72
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*! minimo - json-table: main structure builder */
|
|
2
|
+
|
|
3
|
+
import * as styles from '../json-table-component.module.css';
|
|
4
|
+
import { domBuilder } from '../../../utilities/dom-builder/dom-builder.js';
|
|
5
|
+
import { classnames } from '../../../utilities/classnames.js';
|
|
6
|
+
import { infoSection } from './info-section.js';
|
|
7
|
+
|
|
8
|
+
/** @typedef {import('./defaults.js').JsonTableParams} JsonTableParams */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Elements generated by the component, collected during the build.
|
|
12
|
+
* @typedef {Object} JsonTableElements
|
|
13
|
+
* @property {HTMLElement} [wrapper] - Main wrapper (`section`), contains info section and table
|
|
14
|
+
* @property {HTMLElement} [infoOuter] - Outer info container
|
|
15
|
+
* @property {HTMLElement} [info] - Info container (flex row: info text + search)
|
|
16
|
+
* @property {HTMLElement} [resultInfo] - Info text container
|
|
17
|
+
* @property {HTMLInputElement} [searchInput] - Search input (only when `search` is true)
|
|
18
|
+
* @property {HTMLElement} [tableWrapper] - Div wrapping the table (`table-responsive` by default)
|
|
19
|
+
* @property {HTMLTableElement} [table] - The `<table>` element
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Builds the whole component structure inside `host` (emptied first) via domBuilder.
|
|
24
|
+
*
|
|
25
|
+
* Generated structure:
|
|
26
|
+
* ```
|
|
27
|
+
* section.wrapper[mainWrapperExtraClass]
|
|
28
|
+
* div.infoOuter[outerInfoExtraClass]
|
|
29
|
+
* div.info[infoExtraClass]
|
|
30
|
+
* div.resultInfo
|
|
31
|
+
* div.search > input[type=search] (if `search`)
|
|
32
|
+
* div[tableWrapperClass]
|
|
33
|
+
* table#tableId[tableClass]
|
|
34
|
+
* caption (if `caption`)
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* TODO thead / tbody / tfoot: step successivo (parametro `cols` e rendering delle righe)
|
|
38
|
+
*
|
|
39
|
+
* @param {HTMLElement} host - The `<json-table>` element
|
|
40
|
+
* @param {JsonTableParams} params - Resolved params
|
|
41
|
+
* @returns {JsonTableElements} References to the generated elements
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const elements = mainBuilder(el, {
|
|
45
|
+
* ...defaults,
|
|
46
|
+
* caption: 'Utenti', // default: null
|
|
47
|
+
* search: true, // default: true
|
|
48
|
+
* tableId: 'users-table', // default: null
|
|
49
|
+
* tableWrapperClass: 'table-responsive', // default: 'table-responsive'
|
|
50
|
+
* tableClass: 'table table-bordered', // default: 'table table-bordered'
|
|
51
|
+
* mainWrapperExtraClass: null, // default: null
|
|
52
|
+
* outerInfoExtraClass: null, // default: null
|
|
53
|
+
* infoExtraClass: null // default: null
|
|
54
|
+
* });
|
|
55
|
+
* elements.table; // → HTMLTableElement
|
|
56
|
+
*/
|
|
57
|
+
export function mainBuilder(host, params) {
|
|
58
|
+
|
|
59
|
+
/** @type {JsonTableElements} */
|
|
60
|
+
const elements = {};
|
|
61
|
+
|
|
62
|
+
domBuilder([
|
|
63
|
+
{
|
|
64
|
+
tag: 'section',
|
|
65
|
+
className: classnames(styles.wrapper, params.mainWrapperExtraClass),
|
|
66
|
+
callback: el => { elements.wrapper = /** @type {HTMLElement} */ (el); },
|
|
67
|
+
children: [
|
|
68
|
+
|
|
69
|
+
...infoSection(params, elements),
|
|
70
|
+
|
|
71
|
+
{
|
|
72
|
+
className: classnames(params.tableWrapperClass),
|
|
73
|
+
callback: el => { elements.tableWrapper = /** @type {HTMLElement} */ (el); },
|
|
74
|
+
children: [
|
|
75
|
+
{
|
|
76
|
+
tag: 'table',
|
|
77
|
+
id: params.tableId,
|
|
78
|
+
className: classnames(styles.table, params.tableClass),
|
|
79
|
+
callback: el => { elements.table = /** @type {HTMLTableElement} */ (el); },
|
|
80
|
+
children: [
|
|
81
|
+
{
|
|
82
|
+
tag: 'caption',
|
|
83
|
+
condition: params.caption != null,
|
|
84
|
+
content: params.caption
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
], host, { emptyParent: true });
|
|
93
|
+
|
|
94
|
+
return elements;
|
|
95
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*! minimo - json-table: params resolution */
|
|
2
|
+
|
|
3
|
+
import { defaults } from './defaults.js';
|
|
4
|
+
|
|
5
|
+
/** @typedef {import('./defaults.js').JsonTableParams} JsonTableParams */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a single parameter, merging the four possible sources.
|
|
9
|
+
*
|
|
10
|
+
* Precedence (highest first):
|
|
11
|
+
* 1. `config` – value passed via `init()`/`reload()`. Present and `!== undefined` always wins;
|
|
12
|
+
* pass `null` explicitly to skip the HTML attribute and fall back to the defaults.
|
|
13
|
+
* 2. HTML attribute – booleans are auto-parsed when the built-in default is a boolean
|
|
14
|
+
* (`search`, `search="true"`, `search="1"` → true; `search="false"`, `search="0"` → false);
|
|
15
|
+
* values starting with `[` or `{` are parsed as JSON (invalid JSON is reported in the
|
|
16
|
+
* console and ignored, falling back to the defaults).
|
|
17
|
+
* 3. `projectDefaults` – values set via `JsonTable.setDefaults()`.
|
|
18
|
+
* 4. Built-in default (see `defaults.js`).
|
|
19
|
+
*
|
|
20
|
+
* @param {HTMLElement} el - The `<json-table>` element (source of the HTML attributes)
|
|
21
|
+
* @param {Partial<JsonTableParams>|null} config - Config passed via `init()` (default: null)
|
|
22
|
+
* @param {Partial<JsonTableParams>} projectDefaults - Project-wide defaults (default: {})
|
|
23
|
+
* @param {keyof JsonTableParams} name - Parameter name
|
|
24
|
+
* @returns {*} The resolved value
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // <json-table jsonurl="/api/rows.json" search="false"></json-table>
|
|
28
|
+
* getParam(el, { jsonUrl: '/api/other.json' }, {}, 'jsonUrl'); // → '/api/other.json' (config)
|
|
29
|
+
* getParam(el, null, {}, 'search'); // → false (attribute)
|
|
30
|
+
* getParam(el, null, { tableClass: 'table' }, 'tableClass'); // → 'table' (project defaults)
|
|
31
|
+
* getParam(el, null, {}, 'tableWrapperClass'); // → 'table-responsive' (built-in default)
|
|
32
|
+
*/
|
|
33
|
+
export function getParam(el, config = null, projectDefaults = {}, name) {
|
|
34
|
+
|
|
35
|
+
// 1. config from script (undefined = key not present → go down one level)
|
|
36
|
+
if (config && config[name] !== undefined) {
|
|
37
|
+
return config[name];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. HTML attribute
|
|
41
|
+
const attr = el.getAttribute(name);
|
|
42
|
+
if (attr !== null) {
|
|
43
|
+
const trimmed = attr.trim();
|
|
44
|
+
|
|
45
|
+
if (typeof defaults[name] === 'boolean') {
|
|
46
|
+
const lower = trimmed.toLowerCase();
|
|
47
|
+
// empty string = attribute present without value → true (standard HTML)
|
|
48
|
+
return lower !== 'false' && lower !== '0';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(trimmed);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.error(`[json-table] attributo \`${name}\`: JSON non valido, valore ignorato (viene usato il default)`, err);
|
|
57
|
+
// fall through to the defaults
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
return attr;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 3. project defaults
|
|
65
|
+
if (projectDefaults[name] !== undefined) {
|
|
66
|
+
return projectDefaults[name];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 4. built-in default
|
|
70
|
+
return defaults[name];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolves every parameter defined in `defaults.js` (see `getParam` for the precedence rules).
|
|
76
|
+
*
|
|
77
|
+
* @param {HTMLElement} el - The `<json-table>` element
|
|
78
|
+
* @param {Partial<JsonTableParams>|null} [config=null] - Config passed via `init()` (default: null)
|
|
79
|
+
* @param {Partial<JsonTableParams>} [projectDefaults={}] - Project-wide defaults (default: {})
|
|
80
|
+
* @returns {JsonTableParams} The complete params object
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const params = resolveParams(el, { jsonUrl: '/api/rows.json', debug: true }, {});
|
|
84
|
+
* // → { debug: true, jsonUrl: '/api/rows.json', jsonDataField: 'data', data: null, ... }
|
|
85
|
+
*/
|
|
86
|
+
export function resolveParams(el, config = null, projectDefaults = {}) {
|
|
87
|
+
|
|
88
|
+
const params = Object.fromEntries(
|
|
89
|
+
Object.keys(defaults).map(name => [
|
|
90
|
+
name,
|
|
91
|
+
getParam(el, config, projectDefaults, /** @type {keyof JsonTableParams} */ (name))
|
|
92
|
+
])
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return /** @type {JsonTableParams} */ (params);
|
|
96
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*! minimo - json-table: info text update */
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('./defaults.js').JsonTableParams} JsonTableParams */
|
|
4
|
+
/** @typedef {import('./main-builder.js').JsonTableElements} JsonTableElements */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Updates the info text (`elements.resultInfo`) using `params.infoText(shown, total)`.
|
|
8
|
+
*
|
|
9
|
+
* The returned value can be a Node (appended as-is), a plain string (set as `textContent`)
|
|
10
|
+
* or an HTML string (sanitized via `Element.setHTML` where supported, `innerHTML` otherwise,
|
|
11
|
+
* consistently with domBuilder).
|
|
12
|
+
*
|
|
13
|
+
* @param {JsonTableElements} elements - Generated elements (see `main-builder.js`)
|
|
14
|
+
* @param {JsonTableParams} params - Resolved params
|
|
15
|
+
* @param {number} shown - Number of rows currently displayed
|
|
16
|
+
* @param {number} total - Total number of rows in the data set
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* updateInfo(elements, params, 25, 100);
|
|
21
|
+
* // with the default `infoText` → "Visualizzate <strong>25</strong> righe su <strong>100</strong>"
|
|
22
|
+
*/
|
|
23
|
+
export function updateInfo(elements, params, shown, total) {
|
|
24
|
+
|
|
25
|
+
const target = elements.resultInfo;
|
|
26
|
+
if (!target) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const content = params.infoText(shown, total);
|
|
31
|
+
|
|
32
|
+
if (content instanceof Node) {
|
|
33
|
+
target.replaceChildren(content);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const text = String(content ?? '');
|
|
38
|
+
|
|
39
|
+
if (!text.includes('<')) {
|
|
40
|
+
target.textContent = text;
|
|
41
|
+
|
|
42
|
+
} else if (typeof target.setHTML === 'function') {
|
|
43
|
+
target.setHTML(text);
|
|
44
|
+
|
|
45
|
+
} else {
|
|
46
|
+
target.innerHTML = text;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -176,6 +176,68 @@ document.dispatchEvent(new CustomEvent('datatable:search', {
|
|
|
176
176
|
}));
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
+
## Parametri/attributi
|
|
180
|
+
|
|
181
|
+
Tutti i parametri sono leggibili sia da attributo HTML che da script (`init()`).
|
|
182
|
+
Ordine di precedenza: config (script) > attributo HTML > default interno.
|
|
183
|
+
|
|
184
|
+
### Parametri del componente
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
{
|
|
188
|
+
json, // URL da cui recuperare i dati (struttura attesa: { data: [...] }).
|
|
189
|
+
// Ignorato se `data` è presente.
|
|
190
|
+
|
|
191
|
+
data, // Dati inline, array di oggetti plain. Ha precedenza su `json`.
|
|
192
|
+
// Utile in contesti SSR (Twig, Blade, ...) dove il JSON è già
|
|
193
|
+
// disponibile server-side; per grandi dataset preferire `json`.
|
|
194
|
+
|
|
195
|
+
cols, // Definizione delle colonne. Vedi "Parametro cols" più sotto.
|
|
196
|
+
// Default: []
|
|
197
|
+
|
|
198
|
+
tableClass, // Classi CSS del tag <table>, in sostituzione di 'table table-bordered'.
|
|
199
|
+
// La classe interna styles.table (necessaria al layout) viene
|
|
200
|
+
// comunque sempre applicata.
|
|
201
|
+
// Default: 'table table-bordered'
|
|
202
|
+
|
|
203
|
+
renderNullAs, // Stringa globale da mostrare al posto di valori null/undefined,
|
|
204
|
+
// sovrascrivibile per singola colonna con `_renderNullAs`.
|
|
205
|
+
// Default: '—' (em dash)
|
|
206
|
+
|
|
207
|
+
updateFooterOnPageChange, // Se true, `_footerRender` riceve solo i record della
|
|
208
|
+
// pagina corrente (subtotale). Se false, l'intero set filtrato
|
|
209
|
+
// (il footer si aggiorna solo al cambio filtro, non pagina).
|
|
210
|
+
// Default: false
|
|
211
|
+
|
|
212
|
+
refs, // string[] di percorsi URL da cui, se si proviene, la pagina
|
|
213
|
+
// corrente viene ripristinata dal cookie di sessione 'sd-pag'.
|
|
214
|
+
// Confronto su pathname (query string/hash ignorati): es. ['/utenti']
|
|
215
|
+
// intercetta anche '/utenti/1234'.
|
|
216
|
+
|
|
217
|
+
topSlot, // Elemento DOM o markup HTML nell'area in alto a sinistra.
|
|
218
|
+
// Solo via init() (non leggibile da attributo HTML). Ignorato
|
|
219
|
+
// se showInfoAtTop è true. Vedi sezione dedicata più sotto.
|
|
220
|
+
|
|
221
|
+
showInfoAtTop, // Replica il testo dell'area info anche in alto a sinistra.
|
|
222
|
+
// Ha priorità su topSlot se true. Vedi sezione dedicata più sotto.
|
|
223
|
+
// Default: true
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Parametri nativi di simple-datatables
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
{
|
|
231
|
+
perPage, // Numero di righe per pagina, passato direttamente come opzione
|
|
232
|
+
// nativa `perPage` del costruttore DataTable.
|
|
233
|
+
// Default: 25
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Tutte le altre opzioni native di simple-datatables (`searchable`, `sortable`, `paging`,
|
|
238
|
+
`pagerDelta`, `perPageSelect`, `locale`, `labels`, `classes`, `template`, ...) sono
|
|
239
|
+
impostate internamente dal componente e **non** sono configurabili da attributo/script.
|
|
240
|
+
|
|
179
241
|
## Parametro `cols`
|
|
180
242
|
|
|
181
243
|
Array di oggetti di configurazione delle colonne. Le chiavi con prefisso `_` sono
|
|
@@ -119,6 +119,13 @@ import caretRightIcon from '../../icons/caret-right.svg?inline';
|
|
|
119
119
|
* @param {number} perPage Numero di righe per pagina.
|
|
120
120
|
* Default: `25` (default: 25)
|
|
121
121
|
*
|
|
122
|
+
* @param {string} tableClass Classi CSS del tag `<table>`, in sostituzione delle
|
|
123
|
+
* classi di default `table table-bordered`. La classe
|
|
124
|
+
* interna `styles.table` (necessaria al layout del
|
|
125
|
+
* componente) viene sempre applicata, a prescindere da
|
|
126
|
+
* questo parametro.
|
|
127
|
+
* Default: `'table table-bordered'` (default: 'table table-bordered')
|
|
128
|
+
*
|
|
122
129
|
* @param {string} renderNullAs Stringa globale da mostrare al posto di valori null/undefined
|
|
123
130
|
* in tutte le colonne. Può essere sovrascritto per singola
|
|
124
131
|
* colonna con `_renderNullAs`.
|
|
@@ -803,6 +810,7 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
803
810
|
this.appendChild(table);
|
|
804
811
|
|
|
805
812
|
const perPage = +this._getParam('perPage', 25);
|
|
813
|
+
const tableClass = this._getParam('tableClass', 'table table-bordered');
|
|
806
814
|
|
|
807
815
|
this._dt = new DataTable(table, {
|
|
808
816
|
locale: 'it',
|
|
@@ -853,7 +861,7 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
853
861
|
empty : styles.empty,
|
|
854
862
|
|
|
855
863
|
// table
|
|
856
|
-
table :
|
|
864
|
+
table : `${styles.table} ${tableClass}`,
|
|
857
865
|
// buttons in thead per il sort
|
|
858
866
|
sorter : `btn-reset ${styles.sortBtn}`,
|
|
859
867
|
ascending : styles.sortAscending,
|