@massimo-cassandro/minimo 1.32.4 → 1.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- 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/{CLAUDE-frontend.md → app/CLAUDE.md} +2 -1
- package/dev-tools/starter-kit/source_files/app/assets/api_htaccess +20 -0
- package/dev-tools/starter-kit/source_files/{layout-critical.css → app/css/layout.critical.css} +1 -1
- package/dev-tools/starter-kit/source_files/app/css/layout.css +0 -0
- package/dev-tools/starter-kit/source_files/app/index.js +1 -0
- package/dev-tools/starter-kit/{package-tpl.json → source_files/root/package-tpl.json} +1 -1
- package/dev-tools/starter-kit/source_files/{stylelint.config.mjs → root/stylelint.config.mjs} +1 -1
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/inline-critical-css.mjs +41 -0
- package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/purgecss-setup.mjs +1 -1
- package/dev-tools/starter-kit/{webpack.config.mjs → source_files/root/webpack.config.mjs} +56 -38
- package/dev-tools/starter-kit/starter-install.sh +233 -81
- package/dev-tools/stylelint-config/README.md +45 -0
- package/dev-tools/stylelint-config/index.js +184 -0
- package/dev-tools/stylelint-config/test/test.module.scss +9 -0
- package/package.json +11 -2
- package/snippets/php/iviewer.php +488 -0
- package/snippets/twig-symfony/ViewerController.php +549 -0
- package/src/custom-properties.css +36 -0
- package/src/minimo.css +3 -0
- package/src/utilities/dom-builder/dom-builder.js +9 -5
- package/src/web-components/json-table/json-table-component.js +632 -0
- package/src/web-components/json-table/json-table-component.module.css +247 -0
- package/src/web-components/json-table/json-table.minimo.tokens.mjs +243 -0
- package/src/web-components/json-table/readme.md +764 -0
- package/src/web-components/json-table/src/cell-content.js +136 -0
- package/src/web-components/json-table/src/content-utils.js +103 -0
- package/src/web-components/json-table/src/data-types.js +280 -0
- package/src/web-components/json-table/src/defaults.js +424 -0
- package/src/web-components/json-table/src/get-data.js +152 -0
- package/src/web-components/json-table/src/icons.js +80 -0
- package/src/web-components/json-table/src/info-section.js +107 -0
- package/src/web-components/json-table/src/main-builder.js +218 -0
- package/src/web-components/json-table/src/pagination.js +253 -0
- package/src/web-components/json-table/src/parse-cols.js +113 -0
- package/src/web-components/json-table/src/parse-rows.js +81 -0
- package/src/web-components/json-table/src/resolve-params.js +166 -0
- package/src/web-components/json-table/src/search.js +71 -0
- package/src/web-components/json-table/src/sorting.js +111 -0
- package/src/web-components/json-table/src/table-body.js +109 -0
- package/src/web-components/json-table/src/table-builder.js +152 -0
- package/src/web-components/json-table/src/table-tfoot.js +56 -0
- package/src/web-components/json-table/src/table-thead.js +183 -0
- package/src/web-components/json-table/src/update-info.js +67 -0
- package/src/web-components/s-datatable-component/{docs.md → readme.md} +67 -3
- package/src/web-components/s-datatable-component/s-datatable-component.js +1 -1
- package/types/global.d.ts +17 -3
- package/dev-tools/starter-kit/source_files/app_index.js +0 -1
- package/snippets/config-templates/_htaccess +0 -69
- package/snippets/config-templates/robots.txt +0 -5
- package/snippets/config-templates/rollup.config.mjs +0 -115
- /package/dev-tools/starter-kit/source_files/{_root_robots.txt → app/assets/robots.txt} +0 -0
- /package/dev-tools/starter-kit/source_files/{_root_htaccess → app/assets/root_htaccess} +0 -0
- /package/{src → dev-tools/starter-kit/source_files/app/css}/fonts.css +0 -0
- /package/dev-tools/starter-kit/source_files/{favicons-readme.md → app/favicons/favicons-readme.md} +0 -0
- /package/dev-tools/starter-kit/{webpack-template.ejs → source_files/app/tpl/index.ejs} +0 -0
- /package/dev-tools/starter-kit/source_files/{__project__.code-workspace → root/__project__.code-workspace} +0 -0
- /package/dev-tools/starter-kit/source_files/{_browserslistrc → root/_browserslistrc} +0 -0
- /package/dev-tools/starter-kit/source_files/{_editorconfig → root/_editorconfig} +0 -0
- /package/dev-tools/starter-kit/source_files/{_gitignore → root/_gitignore} +0 -0
- /package/dev-tools/starter-kit/source_files/{_prettierrc → root/_prettierrc} +0 -0
- /package/dev-tools/starter-kit/source_files/{eslint.config.mjs → root/eslint.config.mjs} +0 -0
- /package/dev-tools/starter-kit/source_files/{jsconfig.json → root/jsconfig.json} +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/css-rules.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/get-jsConfig-aliases.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/mini-svg-data-uri-loader.cjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/postcss.config.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/purgecss-variables-safelist.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/svg-rules.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/svgo.config.mjs +0 -0
- /package/snippets/{config-templates/_github → _github}/dependabot.yml +0 -0
- /package/snippets/{error-pages/error-pages.js → error-pages.js} +0 -0
- /package/snippets/{config-templates/react → react}/offcanvas/offcanvas.jsx +0 -0
- /package/snippets/{config-templates/react → react}/offcanvas/offcanvas.module.scss +0 -0
- /package/snippets/{config-templates → root}/_hintrc.cjs +0 -0
- /package/snippets/{config-templates → root}/_npmignore +0 -0
- /package/snippets/{config-templates → root}/_npmrc +0 -0
- /package/snippets/{config-templates → root}/_stylelintignore +0 -0
- /package/snippets/{config-templates → root}/babel.config--react.json +0 -0
- /package/snippets/{config-templates → root}/babel.config--solidJS.json +0 -0
- /package/snippets/{ImgViewerController.php → twig-symfony/ImgViewerController.php} +0 -0
- /package/snippets/{UnsplashController.php → twig-symfony/UnsplashController.php} +0 -0
- /package/snippets/{config-templates → twig-symfony}/_env.local +0 -0
- /package/snippets/{error-pages → twig-symfony}/bundles/TwigBundle/Exception/error.html.twig +0 -0
- /package/snippets/{flash-messages.html.twig → twig-symfony/flash-messages.html.twig} +0 -0
- /package/snippets/{minimo-form-layout.html.twig → twig-symfony/minimo-form-layout.html.twig} +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*! minimo - json-table: tfoot rendering */
|
|
2
|
+
|
|
3
|
+
import { domBuilder } from '../../../utilities/dom-builder/dom-builder.js';
|
|
4
|
+
import { classnames } from '../../../utilities/classnames.js';
|
|
5
|
+
import { setContent } from './content-utils.js';
|
|
6
|
+
import { tfootContent } from './cell-content.js';
|
|
7
|
+
|
|
8
|
+
/** @typedef {import('../json-table-component.js').JsonTable} JsonTable */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Renders the footer row inside `elements.tfoot` (present only when `params.tfoot` is true),
|
|
12
|
+
* replacing the previous content. Every column produces a cell whose content comes from
|
|
13
|
+
* `tfootContent()` (column `tfootRender`); the footer is emptied when there are no rows.
|
|
14
|
+
*
|
|
15
|
+
* The rows passed to `tfootRender` are the whole filtered set, or the current page only when
|
|
16
|
+
* `updateFooterOnPageChange` is true.
|
|
17
|
+
*
|
|
18
|
+
* Generated structure: `tr > td[data-key][cellClass]…`
|
|
19
|
+
*
|
|
20
|
+
* @param {JsonTable} jt - The component instance
|
|
21
|
+
* @returns {void}
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* renderTfoot(jt); // after `jt.state` has been updated
|
|
25
|
+
*/
|
|
26
|
+
export function renderTfoot(jt) {
|
|
27
|
+
|
|
28
|
+
const { params, elements, state, cols } = jt;
|
|
29
|
+
const tfoot = elements.tfoot;
|
|
30
|
+
|
|
31
|
+
if (!tfoot) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!state.filtered.length) {
|
|
36
|
+
tfoot.replaceChildren();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const rows = (params.updateFooterOnPageChange ? state.pageRows : state.filtered).map(parsed => parsed.row);
|
|
41
|
+
|
|
42
|
+
domBuilder([
|
|
43
|
+
{
|
|
44
|
+
tag: 'tr',
|
|
45
|
+
children: cols.map(col => ({
|
|
46
|
+
tag: 'td',
|
|
47
|
+
className: classnames(col.cellClass),
|
|
48
|
+
attrs: { 'data-key': col.key },
|
|
49
|
+
callback: el => {
|
|
50
|
+
const td = /** @type {HTMLTableCellElement} */ (el);
|
|
51
|
+
setContent(td, tfootContent(col, rows, params, td));
|
|
52
|
+
}
|
|
53
|
+
}))
|
|
54
|
+
}
|
|
55
|
+
], tfoot, { emptyParent: true });
|
|
56
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/*! minimo - json-table: thead */
|
|
2
|
+
|
|
3
|
+
import * as styles from '../json-table-component.module.css';
|
|
4
|
+
import { classnames } from '../../../utilities/classnames.js';
|
|
5
|
+
import { setContent } from './content-utils.js';
|
|
6
|
+
import { iconContent } from './icons.js';
|
|
7
|
+
import { nextSortDir } from './sorting.js';
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('./defaults.js').JsonTableParams} JsonTableParams */
|
|
10
|
+
/** @typedef {import('../json-table-component.js').JsonTable} JsonTable */
|
|
11
|
+
/** @typedef {import('./main-builder.js').JsonTableElements} JsonTableElements */
|
|
12
|
+
|
|
13
|
+
/** @typedef {'none'|'ascending'|'descending'} SortState */
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Updates the sort state of a sortable `<th>`: `aria-sort` attribute (removed when `none`),
|
|
17
|
+
* icon and `title`/`aria-label` of the sort button (both describing the action of the next
|
|
18
|
+
* click, e.g. "sort descending" when the ascending sort is active).
|
|
19
|
+
*
|
|
20
|
+
* @param {HTMLTableCellElement} th - The `<th>` generated by `theadPart()`
|
|
21
|
+
* @param {SortState} state - Sort state to apply
|
|
22
|
+
* @param {JsonTableParams} params - Resolved params (icons and labels)
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* setSortState(th, 'ascending', params);
|
|
27
|
+
* // th[aria-sort=ascending], icon → params.sortAscArrowIcon, button title → params.labels.sortDesc
|
|
28
|
+
*/
|
|
29
|
+
export function setSortState(th, state, params) {
|
|
30
|
+
|
|
31
|
+
const btn = th.querySelector('button');
|
|
32
|
+
const icon = th.querySelector('[data-jt-sort-icon]');
|
|
33
|
+
|
|
34
|
+
if (state === 'none') {
|
|
35
|
+
th.removeAttribute('aria-sort');
|
|
36
|
+
} else {
|
|
37
|
+
th.setAttribute('aria-sort', state);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const nextLabel = state === 'none'
|
|
41
|
+
? params.labels.sortAsc
|
|
42
|
+
: (state === 'ascending' ? params.labels.sortDesc : params.labels.sortNone);
|
|
43
|
+
|
|
44
|
+
if (btn) {
|
|
45
|
+
const titleText = (th.querySelector('[data-jt-sort-title]')?.textContent ?? '').trim();
|
|
46
|
+
btn.title = nextLabel ?? '';
|
|
47
|
+
btn.setAttribute('aria-label', titleText ? `${titleText}: ${nextLabel}` : (nextLabel ?? ''));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (icon) {
|
|
51
|
+
const iconDef = state === 'none'
|
|
52
|
+
? params.sortNoneArrowIcon
|
|
53
|
+
: (state === 'ascending' ? params.sortAscArrowIcon : params.sortDescArrowIcon);
|
|
54
|
+
setContent(/** @type {HTMLElement} */ (icon), iconContent(iconDef));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Applies the current sort (`jt.state.sort`) to every sortable `<th>` of the thead: the sorted
|
|
61
|
+
* column gets `aria-sort` and the matching icon, the others are reset to `none`.
|
|
62
|
+
*
|
|
63
|
+
* @param {JsonTable} jt - The component instance
|
|
64
|
+
* @returns {void}
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* jt.state.sort = { key: 'name', dir: 'desc' };
|
|
68
|
+
* applySortState(jt); // th[data-key=name][aria-sort=descending], the other th without aria-sort
|
|
69
|
+
*/
|
|
70
|
+
export function applySortState(jt) {
|
|
71
|
+
|
|
72
|
+
const { elements, params, state } = jt;
|
|
73
|
+
|
|
74
|
+
if (!elements.thead) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
elements.thead.querySelectorAll('th[data-sortable]').forEach(el => {
|
|
79
|
+
const th = /** @type {HTMLTableCellElement} */ (el);
|
|
80
|
+
const isSorted = state.sort != null && th.dataset.key === state.sort.key;
|
|
81
|
+
setSortState(th, isSorted ? (state.sort?.dir === 'desc' ? 'descending' : 'ascending') : 'none', params);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Attaches the delegated `click` listener to the thead: a click on a sort button cycles the
|
|
88
|
+
* column sort (none → asc → desc → none, see `nextSortDir`) through `jt.setSort()`.
|
|
89
|
+
* The thead is not rebuilt on sort, so the focus stays on the clicked button.
|
|
90
|
+
*
|
|
91
|
+
* @param {JsonTable} jt - The component instance
|
|
92
|
+
* @returns {void}
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* setSortListener(jt); // after mainBuilder(jt)
|
|
96
|
+
*/
|
|
97
|
+
export function setSortListener(jt) {
|
|
98
|
+
|
|
99
|
+
const thead = jt.elements.thead;
|
|
100
|
+
|
|
101
|
+
if (!thead) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
thead.addEventListener('click', e => {
|
|
106
|
+
const btn = /** @type {HTMLElement|null} */ (
|
|
107
|
+
/** @type {HTMLElement} */ (e.target).closest('button[data-key]')
|
|
108
|
+
);
|
|
109
|
+
if (!btn) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const key = btn.dataset.key ?? '';
|
|
113
|
+
const current = jt.state.sort?.key === key ? jt.state.sort.dir : null;
|
|
114
|
+
jt.setSort(key, nextSortDir(current));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* domBuilder item of the `<thead>`.
|
|
121
|
+
*
|
|
122
|
+
* Generated structure:
|
|
123
|
+
* ```
|
|
124
|
+
* thead > tr
|
|
125
|
+
* th[scope=col][data-key][headerClass] ← not sortable: content = col.title
|
|
126
|
+
* th.sortable[scope=col][data-key][data-sortable=true][headerClass] ← sortable
|
|
127
|
+
* button.sortBtn[classes.sortBtn][type=button][data-key][title][aria-label]
|
|
128
|
+
* span.sortTitle[data-jt-sort-title] ← col.title
|
|
129
|
+
* span.sortIcon[data-jt-sort-icon] ← sort icon (see `setSortState`)
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* The `<thead>` is stored in `elements.thead`; the sort listener is attached by `setSortListener()`.
|
|
133
|
+
*
|
|
134
|
+
* @param {JsonTable} jt - The component instance (`params`, `cols`)
|
|
135
|
+
* @param {JsonTableElements} elements - Object collecting the generated elements (mutated)
|
|
136
|
+
* @returns {DomBuilderItem}
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* domBuilder([{ tag: 'table', children: [theadPart(jt, elements)] }], host);
|
|
140
|
+
* elements.thead; // → HTMLTableSectionElement
|
|
141
|
+
*/
|
|
142
|
+
export function theadPart(jt, elements) {
|
|
143
|
+
|
|
144
|
+
const { params, cols } = jt;
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
tag: 'thead',
|
|
148
|
+
callback: el => { elements.thead = /** @type {HTMLTableSectionElement} */ (el); },
|
|
149
|
+
children: [
|
|
150
|
+
{
|
|
151
|
+
tag: 'tr',
|
|
152
|
+
children: cols.map(col => ({
|
|
153
|
+
tag: 'th',
|
|
154
|
+
className: classnames(col.headerClass, col.sortable && styles.sortable),
|
|
155
|
+
attrs: {
|
|
156
|
+
scope: 'col',
|
|
157
|
+
'data-key': col.key,
|
|
158
|
+
'data-sortable': col.sortable ? 'true' : null
|
|
159
|
+
},
|
|
160
|
+
content: col.sortable ? null : col.title,
|
|
161
|
+
children: col.sortable
|
|
162
|
+
? [
|
|
163
|
+
{
|
|
164
|
+
tag: 'button',
|
|
165
|
+
className: classnames(styles.sortBtn, params.classes.sortBtn),
|
|
166
|
+
attrs: { type: 'button', 'data-key': col.key },
|
|
167
|
+
children: [
|
|
168
|
+
{ tag: 'span', className: styles.sortTitle, attrs: { 'data-jt-sort-title': '' }, content: col.title },
|
|
169
|
+
{ tag: 'span', className: styles.sortIcon, attrs: { 'data-jt-sort-icon': '', 'aria-hidden': 'true' } }
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
: [],
|
|
174
|
+
callback: el => {
|
|
175
|
+
if (col.sortable) {
|
|
176
|
+
setSortState(/** @type {HTMLTableCellElement} */ (el), 'none', params);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}))
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*! minimo - json-table: info text update */
|
|
2
|
+
|
|
3
|
+
import { setContent } from './content-utils.js';
|
|
4
|
+
|
|
5
|
+
/** @typedef {import('../json-table-component.js').JsonTable} JsonTable */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Updates the info text (`elements.resultInfo`) from the current state.
|
|
9
|
+
*
|
|
10
|
+
* Content resolution:
|
|
11
|
+
* - `params.infoText` function → `infoText(start, end, totRec, filteredRec, page, totPages)`
|
|
12
|
+
* - no rows → `labels.noResults` (search active) or `labels.noRows`
|
|
13
|
+
* - otherwise the mustache-like template `params.infoText` (string) or `labels.info`, with the
|
|
14
|
+
* placeholders `{start}`, `{end}`, `{totRec}`, `{filteredRec}`, `{page}`, `{totPages}` replaced
|
|
15
|
+
* by the locale-formatted numbers
|
|
16
|
+
*
|
|
17
|
+
* `start`/`end` are the 1-based indexes of the first/last row of the current page within the
|
|
18
|
+
* filtered set (0 when there are no rows).
|
|
19
|
+
*
|
|
20
|
+
* @param {JsonTable} jt - The component instance
|
|
21
|
+
* @returns {void}
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* updateInfo(jt);
|
|
25
|
+
* // default `labels.info`, page 2 of 25 rows per page, 60 rows → "Stai visualizzando le righe da 26 a 50, su un totale di 60 record trovati"
|
|
26
|
+
*/
|
|
27
|
+
export function updateInfo(jt) {
|
|
28
|
+
|
|
29
|
+
const { params, elements, state } = jt;
|
|
30
|
+
const target = elements.resultInfo;
|
|
31
|
+
|
|
32
|
+
if (!target) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const shown = state.pageRows.length;
|
|
37
|
+
const perPage = params.perPage > 0 ? params.perPage : shown;
|
|
38
|
+
const start = shown ? (state.page - 1) * perPage + 1 : 0;
|
|
39
|
+
|
|
40
|
+
/** @type {Record<string, number>} */
|
|
41
|
+
const values = {
|
|
42
|
+
start,
|
|
43
|
+
end: shown ? start + shown - 1 : 0,
|
|
44
|
+
totRec: state.totRec,
|
|
45
|
+
filteredRec: state.filteredRec,
|
|
46
|
+
page: state.page,
|
|
47
|
+
totPages: state.totPages
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let content;
|
|
51
|
+
|
|
52
|
+
if (typeof params.infoText === 'function') {
|
|
53
|
+
content = params.infoText(values.start, values.end, values.totRec, values.filteredRec, values.page, values.totPages);
|
|
54
|
+
|
|
55
|
+
} else if (values.filteredRec === 0) {
|
|
56
|
+
content = state.searchTerm ? params.labels.noResults : params.labels.noRows;
|
|
57
|
+
|
|
58
|
+
} else {
|
|
59
|
+
const tpl = params.infoText ?? params.labels.info ?? '';
|
|
60
|
+
content = String(tpl).replace(
|
|
61
|
+
/\{(start|end|totRec|filteredRec|page|totPages)\}/g,
|
|
62
|
+
(_, key) => values[key].toLocaleString(params.locale)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setContent(target, content);
|
|
67
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# `s-datatable` web-component per simple-datatables
|
|
1
|
+
# `s-datatable` web-component per [simple-datatables](https://fiduswriter.github.io/simple-datatables/documentation/)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
>**NB: deprecato, utilizzare [json-table](../json-table/readme.md)**
|
|
2
5
|
|
|
3
|
-
<https://fiduswriter.github.io/simple-datatables/documentation/>
|
|
4
6
|
|
|
5
7
|
## Installazione
|
|
6
8
|
|
|
@@ -176,6 +178,68 @@ document.dispatchEvent(new CustomEvent('datatable:search', {
|
|
|
176
178
|
}));
|
|
177
179
|
```
|
|
178
180
|
|
|
181
|
+
## Parametri/attributi
|
|
182
|
+
|
|
183
|
+
Tutti i parametri sono leggibili sia da attributo HTML che da script (`init()`).
|
|
184
|
+
Ordine di precedenza: config (script) > attributo HTML > default interno.
|
|
185
|
+
|
|
186
|
+
### Parametri del componente
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
{
|
|
190
|
+
json, // URL da cui recuperare i dati (struttura attesa: { data: [...] }).
|
|
191
|
+
// Ignorato se `data` è presente.
|
|
192
|
+
|
|
193
|
+
data, // Dati inline, array di oggetti plain. Ha precedenza su `json`.
|
|
194
|
+
// Utile in contesti SSR (Twig, Blade, ...) dove il JSON è già
|
|
195
|
+
// disponibile server-side; per grandi dataset preferire `json`.
|
|
196
|
+
|
|
197
|
+
cols, // Definizione delle colonne. Vedi "Parametro cols" più sotto.
|
|
198
|
+
// Default: []
|
|
199
|
+
|
|
200
|
+
tableClass, // Classi CSS del tag <table>, in sostituzione di 'table table-bordered'.
|
|
201
|
+
// La classe interna styles.table (necessaria al layout) viene
|
|
202
|
+
// comunque sempre applicata.
|
|
203
|
+
// Default: 'table table-bordered'
|
|
204
|
+
|
|
205
|
+
renderNullAs, // Stringa globale da mostrare al posto di valori null/undefined,
|
|
206
|
+
// sovrascrivibile per singola colonna con `_renderNullAs`.
|
|
207
|
+
// Default: '—' (em dash)
|
|
208
|
+
|
|
209
|
+
updateFooterOnPageChange, // Se true, `_footerRender` riceve solo i record della
|
|
210
|
+
// pagina corrente (subtotale). Se false, l'intero set filtrato
|
|
211
|
+
// (il footer si aggiorna solo al cambio filtro, non pagina).
|
|
212
|
+
// Default: false
|
|
213
|
+
|
|
214
|
+
refs, // string[] di percorsi URL da cui, se si proviene, la pagina
|
|
215
|
+
// corrente viene ripristinata dal cookie di sessione 'sd-pag'.
|
|
216
|
+
// Confronto su pathname (query string/hash ignorati): es. ['/utenti']
|
|
217
|
+
// intercetta anche '/utenti/1234'.
|
|
218
|
+
|
|
219
|
+
topSlot, // Elemento DOM o markup HTML nell'area in alto a sinistra.
|
|
220
|
+
// Solo via init() (non leggibile da attributo HTML). Ignorato
|
|
221
|
+
// se showInfoAtTop è true. Vedi sezione dedicata più sotto.
|
|
222
|
+
|
|
223
|
+
showInfoAtTop, // Replica il testo dell'area info anche in alto a sinistra.
|
|
224
|
+
// Ha priorità su topSlot se true. Vedi sezione dedicata più sotto.
|
|
225
|
+
// Default: true
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Parametri nativi di simple-datatables
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
{
|
|
233
|
+
perPage, // Numero di righe per pagina, passato direttamente come opzione
|
|
234
|
+
// nativa `perPage` del costruttore DataTable.
|
|
235
|
+
// Default: 25
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Tutte le altre opzioni native di simple-datatables (`searchable`, `sortable`, `paging`,
|
|
240
|
+
`pagerDelta`, `perPageSelect`, `locale`, `labels`, `classes`, `template`, ...) sono
|
|
241
|
+
impostate internamente dal componente e **non** sono configurabili da attributo/script.
|
|
242
|
+
|
|
179
243
|
## Parametro `cols`
|
|
180
244
|
|
|
181
245
|
Array di oggetti di configurazione delle colonne. Le chiavi con prefisso `_` sono
|
|
@@ -217,7 +281,7 @@ cols: [
|
|
|
217
281
|
// 'sf_datetime' – data e ora da oggetto Symfony ({ date, timezone }),
|
|
218
282
|
// pre-elaborato in _load
|
|
219
283
|
// 'sf_date' – come 'sf_datetime', ma mostra solo la data
|
|
220
|
-
// 'datetime' – data e ora da stringa/valore
|
|
284
|
+
// 'datetime' – data e ora da stringa/valore elaborabile da `new Date()`
|
|
221
285
|
// 'date' – come 'datetime', ma mostra solo la data
|
|
222
286
|
// (date/datetime: formato it-IT, allineamento a destra,
|
|
223
287
|
// data-order impostato per l'ordinamento; null → '—')
|
|
@@ -502,7 +502,7 @@ class SimpleDatatableAdapter extends HTMLElement {
|
|
|
502
502
|
* Se presente e !== undefined sovrascrive sempre l'attributo.
|
|
503
503
|
* Passare esplicitamente `null` per ignorare l'attributo e
|
|
504
504
|
* ricevere il defaultValue.
|
|
505
|
-
* 2. Attributo HTML – se il valore inizia con [ o { viene auto-
|
|
505
|
+
* 2. Attributo HTML – se il valore inizia con [ o { viene auto-elaborato
|
|
506
506
|
* da JSON (utile per cols, che può stare nell'attributo).
|
|
507
507
|
* 3. defaultValue – fallback finale.
|
|
508
508
|
*
|
package/types/global.d.ts
CHANGED
|
@@ -3,9 +3,23 @@
|
|
|
3
3
|
// The canonical definition lives in dom-builder.js — this only re-exports it.
|
|
4
4
|
type DomBuilderItem = import('../src/utilities/dom-builder/dom-builder.js').DomBuilderItem;
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// Global alias for the `<json-table>` web component class, so consumer scripts can write
|
|
7
|
+
// `/** @type {JsonTable} */` without importing the type. The tag name map augmentation makes
|
|
8
|
+
// `document.querySelector('json-table')` / `document.createElement('json-table')` return a
|
|
9
|
+
// `JsonTable` instance without casts. Consumers get both by adding this file to their
|
|
10
|
+
// jsconfig/tsconfig `include` (e.g. "node_modules/@massimo-cassandro/minimo/types/global.d.ts").
|
|
11
|
+
type JsonTable = import('../src/web-components/json-table/json-table-component.js').JsonTable;
|
|
12
|
+
|
|
13
|
+
interface HTMLElementTagNameMap {
|
|
14
|
+
'json-table': JsonTable;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Sanitizer API (Element.setHTML / getHTML). Declared as required members (not optional) so
|
|
18
|
+
// the augmentation stays compatible with the lib.dom versions that already include them
|
|
19
|
+
// (overload signatures must all be optional or all required); the runtime code still checks
|
|
20
|
+
// `typeof el.setHTML === 'function'` before using it.
|
|
7
21
|
// https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML
|
|
8
22
|
interface Element {
|
|
9
|
-
setHTML
|
|
10
|
-
getHTML
|
|
23
|
+
setHTML(input: string, options?: { sanitizer?: unknown }): void;
|
|
24
|
+
getHTML(options?: { serializableShadowRoots?: boolean; shadowRoots?: ShadowRoot[] }): string;
|
|
11
25
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import './index.css';
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# no cache
|
|
3
|
-
# <IfModule mod_headers.c>
|
|
4
|
-
# Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
5
|
-
# Header set Pragma "no-cache"
|
|
6
|
-
# Header set Expires 0
|
|
7
|
-
# </IfModule>
|
|
8
|
-
|
|
9
|
-
# blocco iframe
|
|
10
|
-
# "deny" = tutti siti
|
|
11
|
-
# "sameorigin" tutti tranne quelli che vengono dalla stessa origine
|
|
12
|
-
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
|
13
|
-
Header set X-Frame-Options "sameorigin"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<IfModule mod_expires.c>
|
|
17
|
-
ExpiresActive On
|
|
18
|
-
AddType application/vnd.ms-fontobject .eot
|
|
19
|
-
AddType application/x-font-ttf .ttf
|
|
20
|
-
AddType application/x-font-opentype .otf
|
|
21
|
-
AddType application/x-font-woff .woff
|
|
22
|
-
AddType application/x-font-woff2 .woff2
|
|
23
|
-
AddType application/font-sfnt otf ttf
|
|
24
|
-
AddType application/font-woff woff
|
|
25
|
-
AddType application/font-woff2 woff2
|
|
26
|
-
AddType image/svg+xml .svg
|
|
27
|
-
|
|
28
|
-
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
|
|
29
|
-
ExpiresByType application/x-font-ttf "access plus 1 year"
|
|
30
|
-
ExpiresByType application/x-font-opentype "access plus 1 year"
|
|
31
|
-
ExpiresByType application/x-font-woff "access plus 1 year"
|
|
32
|
-
ExpiresByType application/x-font-woff2 "access plus 1 year"
|
|
33
|
-
ExpiresByType application/font-sfnt "access plus 1 year"
|
|
34
|
-
ExpiresByType application/font-woff "access plus 1 year"
|
|
35
|
-
ExpiresByType application/font-woff2 "access plus 1 year"
|
|
36
|
-
|
|
37
|
-
ExpiresByType text/html "access plus 1 hour"
|
|
38
|
-
ExpiresByType text/css "access plus 1 year"
|
|
39
|
-
ExpiresByType text/x-javascript "access plus 1 year"
|
|
40
|
-
ExpiresByType application/javascript "access plus 1 year"
|
|
41
|
-
ExpiresByType application/x-javascript "access plus 1 year"
|
|
42
|
-
|
|
43
|
-
ExpiresByType image/svg+xml "access plus 1 year"
|
|
44
|
-
ExpiresByType image/gif "access plus 1 year"
|
|
45
|
-
ExpiresByType image/png "access plus 1 year"
|
|
46
|
-
ExpiresByType image/jpeg "access plus 1 year"
|
|
47
|
-
ExpiresByType image/pjpeg "access plus 1 year"
|
|
48
|
-
ExpiresByType image/webp "access plus 1 year"
|
|
49
|
-
ExpiresByType image/avif "access plus 1 year"
|
|
50
|
-
|
|
51
|
-
ExpiresByType application/pdf "access plus 1 year"
|
|
52
|
-
|
|
53
|
-
ExpiresByType video/mp4 "access plus 1 year"
|
|
54
|
-
ExpiresByType video/webm "access plus 1 year"
|
|
55
|
-
ExpiresByType video/x-m4v "access plus 1 year"
|
|
56
|
-
ExpiresByType video/quicktime "access plus 1 year"
|
|
57
|
-
ExpiresByType audio/mpeg "access plus 1 year"
|
|
58
|
-
ExpiresByType audio/x-wav "access plus 1 year"
|
|
59
|
-
|
|
60
|
-
ExpiresByType image/x-icon "access plus 1 year"
|
|
61
|
-
ExpiresDefault "access plus 1 year"
|
|
62
|
-
</IfModule>
|
|
63
|
-
|
|
64
|
-
<IfModule !mod_expires.c>
|
|
65
|
-
<IfModule mod_headers.c>
|
|
66
|
-
# 1 year
|
|
67
|
-
Header set Cache-Control 'public, max-age 31536000'
|
|
68
|
-
</IfModule>
|
|
69
|
-
</IfModule>
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import terser from '@rollup/plugin-terser';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import node_resolve from '@rollup/plugin-node-resolve';
|
|
4
|
-
import commonjs from '@rollup/plugin-commonjs';
|
|
5
|
-
|
|
6
|
-
// https://github.com/hyhappy/rollup-plugin-string-html
|
|
7
|
-
// import htmlString from 'rollup-plugin-string-html';
|
|
8
|
-
|
|
9
|
-
// https://github.com/exuanbo/rollup-plugin-minify-html-template-literals
|
|
10
|
-
// https://github.com/asyncLiz/minify-html-literals
|
|
11
|
-
// https://github.com/kangax/html-minifier#options-quick-reference
|
|
12
|
-
// import minifyHTML from 'rollup-plugin-minify-html-template-literals';
|
|
13
|
-
|
|
14
|
-
import p from '../package.json'; // assert { type: 'json' };
|
|
15
|
-
|
|
16
|
-
const terserOptions = {
|
|
17
|
-
compress: {
|
|
18
|
-
passes: 2
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
anno = new Date().getFullYear(),
|
|
22
|
-
dirs = [
|
|
23
|
-
{source_dir: './front-end/js', output_dir: './AppBundle/Resources/public/js'}
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
let config = [];
|
|
27
|
-
|
|
28
|
-
// lettura subdir apps e aggiunta a `dirs`
|
|
29
|
-
fs.readdirSync('./front-end/apps').forEach(item => {
|
|
30
|
-
let stats = fs.statSync(`./front-end/apps/${item}`); // stats.isFile() / stats.isDirectory()
|
|
31
|
-
if(stats.isDirectory()) {
|
|
32
|
-
dirs.push({
|
|
33
|
-
source_dir: `./front-end/apps/${item}`,
|
|
34
|
-
output_dir: `./AppBundle/Resources/public/apps/${item}`
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
dirs.forEach(dir => {
|
|
40
|
-
|
|
41
|
-
fs.readdirSync(dir.source_dir)
|
|
42
|
-
.filter(f => /\.js$/.test(f))
|
|
43
|
-
.filter(f => /^[^_]/.test(f)) // ignore files starting with _
|
|
44
|
-
.forEach(file => {
|
|
45
|
-
|
|
46
|
-
let format = 'iife',
|
|
47
|
-
name = null;
|
|
48
|
-
|
|
49
|
-
if(/(-umd\.js)$/.test(file)) {
|
|
50
|
-
format = 'umd';
|
|
51
|
-
name = file.replace('-umd.js', '').replace(/-/g, '_');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
config.push(
|
|
55
|
-
{
|
|
56
|
-
// preserveEntrySignatures: false,
|
|
57
|
-
input: `${dir.source_dir}/${file}`,
|
|
58
|
-
plugins: [
|
|
59
|
-
// deve essere il primo
|
|
60
|
-
// minifyHTML({
|
|
61
|
-
// options: {
|
|
62
|
-
// minifyOptions: {
|
|
63
|
-
// html5: true,
|
|
64
|
-
// collapseWhitespace: true,
|
|
65
|
-
// collapseInlineTagWhitespace: true,
|
|
66
|
-
// conservativeCollapse: true,
|
|
67
|
-
// decodeEntities: true
|
|
68
|
-
// },
|
|
69
|
-
// shouldMinify: () => true
|
|
70
|
-
// },
|
|
71
|
-
// }),
|
|
72
|
-
node_resolve(),
|
|
73
|
-
commonjs(),
|
|
74
|
-
terser(terserOptions),
|
|
75
|
-
],
|
|
76
|
-
output: [{
|
|
77
|
-
file: `${dir.output_dir}/${file.replace('.js', '-min.js')}`,
|
|
78
|
-
format: format,
|
|
79
|
-
sourcemap: true,
|
|
80
|
-
name: name,
|
|
81
|
-
banner: `/*! xxxx v.${p.version} - Massimo Cassandro ${anno} */`,
|
|
82
|
-
// footer: `//! Released on ${new Date().toLocaleString('it-IT', { year: 'numeric', month: 'short', day: '2-digit', hour12: false, hour:'2-digit', minute:'2-digit' })}`
|
|
83
|
-
}]
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
export default config;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// versione statica
|
|
94
|
-
|
|
95
|
-
// export default [
|
|
96
|
-
// {
|
|
97
|
-
// input: 'input.js',
|
|
98
|
-
// plugins: [
|
|
99
|
-
// node_resolve(),
|
|
100
|
-
// // commonjs(),
|
|
101
|
-
// terser({ compress: { passes: 2 } }),
|
|
102
|
-
// ],
|
|
103
|
-
// output: [
|
|
104
|
-
// {
|
|
105
|
-
// file: 'output.min.js',
|
|
106
|
-
// format: 'iife',
|
|
107
|
-
// sourcemap: true,
|
|
108
|
-
|
|
109
|
-
// banner: `/*! xxxx v.${p.version} - Massimo Cassandro ${anno} */`,
|
|
110
|
-
// // footer: `//! Released on ${new Date().toLocaleString('it-IT', { year: 'numeric', month: 'short', day: '2-digit', hour12: false, hour:'2-digit', minute:'2-digit' })}`
|
|
111
|
-
// }
|
|
112
|
-
// ]
|
|
113
|
-
// },
|
|
114
|
-
// ...
|
|
115
|
-
// ];
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dev-tools/starter-kit/source_files/{favicons-readme.md → app/favicons/favicons-readme.md}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|