@massimo-cassandro/minimo 0.1.3 → 0.1.4

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.
Files changed (68) hide show
  1. package/index.js +0 -0
  2. package/package.json +5 -6
  3. package/src/components/auto-datatable/auto-datatable.css +355 -0
  4. package/src/components/auto-datatable/auto-datatable.js +154 -0
  5. package/src/components/init.js +7 -0
  6. package/src/components/modal-alert/flash-alerts.js +32 -0
  7. package/src/components/modal-alert/modal-alert.css +47 -0
  8. package/src/components/modal-alert/modal-alert.js +85 -0
  9. package/src/components/modal-alert/svg/check-circle-duotone.svg +1 -0
  10. package/src/components/modal-alert/svg/info-duotone.svg +1 -0
  11. package/src/components/modal-alert/svg/question-duotone.svg +1 -0
  12. package/src/components/modal-alert/svg/warning-duotone.svg +1 -0
  13. package/src/components/modal-alert/svg/x-circle-duotone.svg +1 -0
  14. package/src/components/modal-content/modal-content.js +152 -0
  15. package/src/components/modal-content/modal-content.module.css +142 -0
  16. package/src/components/recaptcha/recaptcha.css +74 -0
  17. package/src/components/sf-macro/sf-macro.css +104 -0
  18. package/src/components/sf-macro/sf-macro.js +44 -0
  19. package/src/components/vanilla-cookie-consent/cookie-consent.css +20 -0
  20. package/src/components/vanilla-cookie-consent/cookie-consent.js +76 -0
  21. package/src/components/vanilla-cookie-consent/render-cookie-list.js +32 -0
  22. package/src/components/vanilla-cookie-consent/src/cookie-list.js +85 -0
  23. package/src/components/vanilla-cookie-consent/src/it-translation.js +38 -0
  24. package/src/components/vanilla-cookie-consent/src/run-analytics.js +54 -0
  25. package/src/components/vanilla-cookie-consent/src/run-recaptcha.js +71 -0
  26. package/src/css/alerts.css +95 -0
  27. package/src/css/anchors.css +40 -0
  28. package/src/css/buttons/btn-close.css +41 -0
  29. package/src/css/buttons/buttons.css +187 -0
  30. package/src/css/buttons/status-buttons.css +107 -0
  31. package/src/css/container.css +15 -0
  32. package/src/css/data-formats.css +30 -0
  33. package/src/css/details.css +19 -0
  34. package/src/css/dialog-content.css +72 -0
  35. package/src/css/flex.css +30 -0
  36. package/src/css/forms/form-edit-info.css +17 -0
  37. package/src/css/forms/forms.css +315 -0
  38. package/src/css/forms/select-indicator.svg +3 -0
  39. package/src/css/grid.css +63 -0
  40. package/src/css/headings.css +34 -0
  41. package/src/css/icons.css +114 -0
  42. package/src/css/inner-nav.css +11 -0
  43. package/src/css/layout-base.css +11 -0
  44. package/src/css/overlay.css +11 -0
  45. package/src/css/reset.css +59 -0
  46. package/src/css/spinners/TODO spinner-circle.css +54 -0
  47. package/src/css/spinners/TODO spinner-three-dots.css +14 -0
  48. package/src/css/spinners/readme.md +1 -0
  49. package/src/css/spinners/spinner-circle-basic.css +15 -0
  50. package/src/css/spinners/spinner-trailing-dots.css +51 -0
  51. package/src/css/spinners/spinner-wrapper.css +20 -0
  52. package/src/css/svg.css +3 -0
  53. package/src/css/table.css +66 -0
  54. package/src/css/text.css +114 -0
  55. package/src/css/utilities.css +128 -0
  56. package/src/custom-media-default.css +16 -0
  57. package/src/custom-properties-default.css +273 -0
  58. package/src/index-template.css +49 -0
  59. package/src/js/alert-autoclose.js +9 -0
  60. package/src/js/create-blurhash-canvas.js +47 -0
  61. package/src/js/dialog-content.js +82 -0
  62. package/src/js/dismiss-alerts.js +6 -0
  63. package/src/js/forms.js +36 -0
  64. package/src/js/img-viewer-dom-builder.js +196 -0
  65. package/src/js/inner-nav.js +11 -0
  66. package/src/js/overlay.js +12 -0
  67. package/src/js/print-icon.js +51 -0
  68. package/src/js/spinner.js +30 -0
@@ -0,0 +1,49 @@
1
+ /*
2
+ CSS globali
3
+ i fogli stile delle singole parti sono importate direttamente nel file js relativo
4
+ o (se non necessario), direttamente nel file js principale
5
+ */
6
+
7
+ @import '@src/config/fonts.css';
8
+ /* @import '../node_modules/open-props/open-props.min.css'; */
9
+ /* @import '@src/config/custom-properties-figma.css'; */
10
+ @import '@src/config/custom-media.css';
11
+ @import '@src/config/custom-properties.css';
12
+
13
+ @import '@minimo/reset.css';
14
+ @import '@minimo/layout-base.css';
15
+ @import '@minimo/container.css';
16
+
17
+ /* @import '@minimo/forms/forms.css'; */
18
+ /* @import '@minimo/forms/form-edit-info.css'; */
19
+ @import '@minimo/buttons/buttons.css';
20
+ /* @import '@minimo/buttons/status-buttons.css'; */
21
+ /* @import '@minimo/buttons/btn-close.css'; */
22
+ @import '@minimo/text.css';
23
+ @import '@minimo/anchors.css';
24
+ /* @import '@minimo/headings.css'; */
25
+ /* @import '@minimo/alerts.css'; */
26
+ /* @import '@minimo/icons.css'; */
27
+ @import '@minimo/svg.css';
28
+ @import '@minimo/overlay.css';
29
+ /* @import '@minimo/inner-nav.css'; */
30
+ /* @import '@minimo/table.css'; */
31
+ /* @import '@minimo/flex.css'; */
32
+ /* @import '@minimo/grid.css'; */
33
+ /* @import '@minimo/details.css'; */
34
+
35
+ /* spinner attivarne uno solo + spinner-wrapper */
36
+ @import '@minimo/spinners/spinner-wrapper.css';
37
+ /* @import '@minimo/spinners/spinner-trailing-dots.css'; */
38
+ /* @import '@minimo/spinners/spinner-three-dots.css'; */
39
+ /* @import '@minimo/spinners/spinner-circle.css'; */
40
+ @import '@minimo/spinners/spinner-circle-basic.css';
41
+
42
+ /* @import '@minimo/utilities.css'; */
43
+
44
+
45
+ /* componenti */
46
+ /* @import '@src/components/modal-alert/modal-alert.css'; */
47
+
48
+ /* file progetto */
49
+ /* @import '@src/layout/layout.css'; */
@@ -0,0 +1,9 @@
1
+ // alert autoclose
2
+ let flash_alert = document.querySelector('.alert-success.autoclose');
3
+ if (flash_alert) {
4
+ window.setTimeout(function () {
5
+ slideUp(flash_alert, 500, () => {
6
+ flash_alert.remove();
7
+ });
8
+ }, 4000);
9
+ }
@@ -0,0 +1,47 @@
1
+ import { decodeBlurHash } from 'fast-blurhash';
2
+
3
+ // https://github.com/mad-gooze/fast-blurhash
4
+ // https://github.com/woltapp/blurhash/tree/master/TypeScript
5
+ // https://blurha.sh/
6
+
7
+ /**
8
+ * restituisce un elemento canvas della stringa blurhash indicata
9
+ *
10
+ * @param {string} str - blurhash string
11
+ * @param {number} width
12
+ * @param {number} height
13
+ * @returns HTMLElement
14
+ */
15
+
16
+ export function createBlurhashCanvas({str, width, height, className = null}) {
17
+
18
+ try {
19
+
20
+ if(str && width && height) {
21
+ // decode blurHash image
22
+ const pixels = decodeBlurHash(str, width, height),
23
+
24
+ // draw it on canvas
25
+ canvas = document.createElement('canvas'),
26
+ ctx = canvas.getContext('2d'),
27
+ imageData = ctx.createImageData(width, height);
28
+
29
+ if(className) {
30
+ canvas.className = className;
31
+ }
32
+
33
+ canvas.width = width;
34
+ canvas.height = height;
35
+ imageData.data.set(pixels);
36
+ ctx.putImageData(imageData, 0, 0);
37
+
38
+ return canvas;
39
+ }
40
+
41
+ } catch(e) {
42
+ /* eslint-disable no-console */
43
+ console.error( e );
44
+ /* eslint-enable no-console */
45
+ }
46
+
47
+ }
@@ -0,0 +1,82 @@
1
+ import './dialog-content.css';
2
+ import x_icon from '@icone/x.svg?inline';
3
+ import { spinner } from './spinner';
4
+ /**
5
+ `indicator_class`: classe da aggiungere al wrapper per indicare in modo univoco il dialog
6
+ */
7
+ export function content_dialog_markup(indicator_class) {
8
+
9
+ return `<dialog class="${indicator_class} content-dialog">` +
10
+ '<div class="content-dialog-inner">' +
11
+ '<div class="content-dialog-close">' +
12
+ `<button type="button" class="btn-reset content-dialog-close-btn" aria-label="Chiudi finestra">${x_icon}</button>` +
13
+ '</div>' +
14
+ '<div class="content-dialog-body"></div>' +
15
+ '</div>'+
16
+ '</dialog>';
17
+ }
18
+
19
+
20
+ /**
21
+ `content_url` : url del file contenente il contenuto da caricare via ajax
22
+ `indicator_class`: classe da aggiungere al wrapper per indicare in modo univoco il dialog
23
+ `once_callback` : callback da invocare solo la prima volta, alla creazione del dialog
24
+ */
25
+ let dialog_list = {};
26
+ export async function show_ajax_content_dialog({content_url, class_identifier, once_callback = null}){
27
+
28
+ const close_dialog = (class_identifier) => {
29
+ dialog_list[class_identifier].close();
30
+ document.body.classList.remove('overflow-hidden');
31
+ }
32
+ ,open_dialog = (class_identifier) => {
33
+ dialog_list[class_identifier].showModal();
34
+ document.body.classList.add('overflow-hidden');
35
+ }
36
+ ;
37
+
38
+
39
+ if(!Object.keys(dialog_list).includes(class_identifier)) {
40
+ document.body.insertAdjacentHTML('beforeend',
41
+ content_dialog_markup(class_identifier)
42
+ );
43
+
44
+ const dialog = document.querySelector(`.content-dialog.${class_identifier}`);
45
+ dialog_list[class_identifier] = dialog;
46
+ dialog.querySelector('.content-dialog-close-btn').addEventListener('click', () => {
47
+ close_dialog(class_identifier);
48
+ }, false);
49
+
50
+
51
+ const dialog_body = dialog.querySelector('.content-dialog-body');
52
+ dialog_body.innerHTML = spinner();
53
+ open_dialog(class_identifier);
54
+
55
+ const url = content_url;
56
+ (async () => {
57
+ const response = await fetch(url),
58
+ content = await response.text();
59
+ return content;
60
+ })()
61
+ .then(content => {
62
+ dialog_body.innerHTML = content;
63
+
64
+ if(once_callback && typeof once_callback === 'function') {
65
+ once_callback(dialog_body);
66
+ }
67
+ })
68
+ .catch(err => {
69
+ /* eslint-disable no-console */
70
+ console.error(url);
71
+ console.error(err);
72
+ /* eslint-enable no-console */
73
+
74
+ alert('Si è verificato un errore.\n'+
75
+ 'Ricarica la pagina e ripeti l’operazione');
76
+ });
77
+
78
+ } else {
79
+ open_dialog(class_identifier);
80
+
81
+ }
82
+ }
@@ -0,0 +1,6 @@
1
+ // dismiss alerts
2
+ document.querySelectorAll('[data-dismiss]').forEach(item => {
3
+ item.addEventListener('click', () => {
4
+ item.closest('.' + item.dataset.dismiss).remove();
5
+ }, false);
6
+ });
@@ -0,0 +1,36 @@
1
+ // import './form.css'; //incorporato nel css principale
2
+
3
+ import { enableSubmitBtns } from '@massimo-cassandro/js-utilities';
4
+
5
+ document.querySelectorAll('form').forEach( form => {
6
+
7
+ // form.querySelectorAll('[type="submit"]').forEach(btn =>{
8
+ // btn.addEventListener('click', () => {
9
+ // form.classList.add('was-validated');
10
+ // }, false);
11
+ // });
12
+
13
+ form.querySelectorAll('.is-invalid, .is-valid').forEach(item => {
14
+ item.classList.remove('is-invalid', 'is-valid');
15
+ });
16
+
17
+ form.addEventListener('submit', e => {
18
+
19
+ if(!form.hasAttribute('data-no-was-validated')) {
20
+ form.classList.add('was-validated');
21
+ }
22
+
23
+ if(!form.checkValidity()) {
24
+ e.preventDefault();
25
+ enableSubmitBtns();
26
+
27
+ } else {
28
+ if(!form.hasAttribute('data-no-disabling') && !form.hasAttribute('data-disable-submit')) { /* 'data-disable-submit' per compatibilitò con verioni precedenti */
29
+ form.querySelectorAll('[type="submit"]').forEach(btn => {
30
+ btn.disabled = true;
31
+ });
32
+ }
33
+ }
34
+ }, false);
35
+ });
36
+
@@ -0,0 +1,196 @@
1
+ /**
2
+ * minimo: img-viewer-dom-builder
3
+ * Produce un array da utilizzare con domBuilder per generare un tag `picture`
4
+ *
5
+ * @returns Array
6
+ */
7
+
8
+ // TODO implementare getImgInfo (da ada)
9
+ // TODO detect se amabiente local per uso avif
10
+
11
+ /*
12
+ img_viewer_dom_builder({
13
+ viewer_url: 'iviewer',
14
+ sources: [
15
+ {
16
+ src: 'xxx',
17
+ crop: null, // 'x,y,w,h',
18
+ w: 100,
19
+ h: 100,
20
+ imgWidths: [100, 200, 300],
21
+ mq: null, // '(max-width: xxx)'
22
+ },
23
+ ...
24
+ ],
25
+ imgClassName: null,
26
+ // condition: true | false,
27
+ add2XWidths: false, // true,
28
+ alt_text: '',
29
+ useAvif: false | true,
30
+ // loadingMode: null | 'lazy' | 'eager'
31
+ })
32
+ */
33
+ export function img_viewer_dom_builder({
34
+ /** url del viewer */
35
+ viewer_url,
36
+
37
+ /**
38
+ array dei sorgenti
39
+ deve contenere almeno un oggetto configurato come `source_default`
40
+ l'ultimo oggetto della lista è quello sulla base el quale si costruirà il tag img
41
+ In mancanza di uno degli elementi richiesti (fatta eccezione per `mq` e `crop`),
42
+ il sorgente viene ignorato
43
+ */
44
+ sources = [], // array dei sorgenti
45
+
46
+ /** se false, il markup non viene generato */
47
+ condition = true,
48
+
49
+ /** se true, alle dimensioni fornite, viene aggiunta la versione 2x */
50
+ add2XWidths = false,
51
+
52
+ /** testo alt */
53
+ alt_text = null,
54
+
55
+ /** classe del tag img */
56
+ imgClassName = null,
57
+
58
+ /** se true viene usato anche il formato avif, oltre a webp e jpg */
59
+ useAvif = false,
60
+
61
+ /** valore dell'attributo loading: eager o lazy */
62
+ loadingMode = null
63
+ }) {
64
+
65
+ if(!viewer_url || !condition) {
66
+ return null;
67
+ }
68
+
69
+ const originalSourcesLength = (sources?? []).length,
70
+ source_default = {
71
+ id: null, // id per immagini da db
72
+ src: null, // percorso dalla root dell'immagine sorgente
73
+ crop: null, // eventuali coordinate per il crop dell'immagine sorgente
74
+ w: null, // larghezza e altezza dell'immagine da restituire
75
+ h: null,
76
+ imgWidths: [], // dimensioni dell'immagine
77
+ mq: null // media query, necessaria solo per gli oggetti prima dell'ultimo
78
+ };
79
+
80
+ sources = sources.map(item => {
81
+ item = {...source_default, ...item?? {}};
82
+
83
+ if((!item.src && !item.id) || !item.w || !item.h || !item.imgWidths?.length) {
84
+ return null;
85
+ }
86
+
87
+ return item;
88
+
89
+ }).filter(result => result != null);
90
+
91
+ if(!sources.length || sources.length !== originalSourcesLength) {
92
+ // eslint-disable-next-line no-console
93
+ console.error('`sources` array error');
94
+ return [];
95
+ }
96
+
97
+ // array per gli elementi <source>
98
+ const sourceTagsArray = [],
99
+ srcset_builder = (format, srcsetsArray) => {
100
+ return srcsetsArray.map(src => `${src[0]}${format? `&f=${format}` : ''} ${src[1]}`).join(',');
101
+ },
102
+ formats = (useAvif? ['avif','webp'] : ['webp']);
103
+
104
+ // sono definite fuori dal ciclo forEach perché saranno utilizzate anche dal tag img finale
105
+ let srcsetsArray, sizes;
106
+
107
+ // costruzione array `sourceTagsArray`
108
+ sources.forEach((item, sourceIdx) => {
109
+
110
+ item.imgWidths = item.imgWidths.map(w => add2XWidths? [w, w * 2] : w).flat().toSorted((a,b) => b - a);
111
+
112
+ const isLastSource = sourceIdx === sources.length - 1,
113
+ baseSrc = viewer_url + (item.src? `?src=${encodeURIComponent(item.src)}&` : `/${item.id}?`);
114
+
115
+ srcsetsArray = item.imgWidths.map(w =>{
116
+ const bbHeight = w / (item.w / item.h);
117
+ return [`${baseSrc}bb=${w}x${bbHeight}`, `${w}w`];
118
+ });
119
+
120
+ sizes = item.imgWidths.map((w,idx) => {
121
+
122
+ // la prima dimensione va ignorata (sarà utilizzata nell'iterazione successiva)
123
+ if(idx === 0) {
124
+ return null;
125
+
126
+ } else {
127
+ // elemento `sizes` diverso dal primo e dall'ultimo
128
+ const middle_size_mq = `(${w}px < width <= ${item.imgWidths.at(idx-1)}px) ${item.imgWidths.at(idx-1)}px`;
129
+
130
+ // primo elemento dell'attributo sizes
131
+ if(idx === 1) {
132
+ return `(width > ${w}px) ${item.imgWidths.at(0)}px`;
133
+
134
+ // ultimo
135
+ } else if(idx === item.imgWidths.length - 1) {
136
+ return [
137
+ middle_size_mq,
138
+ `${w}px`
139
+ ];
140
+
141
+ // tutti gli altri
142
+ } else {
143
+ return middle_size_mq;
144
+ }
145
+ }
146
+ }).filter(s => s != null).flat().join(','); // rimuove elemento null, flat e crea stringa sizes
147
+
148
+ // il formato `null` è riferito a jpeg, che non è necessario indicare esplicitamente
149
+ // ed è utilizzato solo nel caso di presenza di media query per creare il tag source per il formato jpeg,
150
+ // non va inoltre considerato nell'ultimo elemento di source
151
+ // visto che, in questo caso, il formato jpeg viene gestito nel tag `img`
152
+ [...formats, ...(item.mq && !isLastSource? [null] : [])].forEach(format => {
153
+ sourceTagsArray.push({
154
+ tag: 'source',
155
+ attrs: {
156
+ type: format? `image/${format}` : null,
157
+ srcset: srcset_builder(format, srcsetsArray),
158
+ sizes: sizes,
159
+ media: item.mq,
160
+
161
+ // width e height sono necessarie solo in caso di presenza della media query
162
+ // presupponendo che in questi casi l'immagine abbia proporzioni diverse
163
+ // da quelle dell'immagine del tag `img`.
164
+ // In assenza di media query, vengono "ereditate" quelle indicate in `img`
165
+ width: item.mq? item.w : null,
166
+ height: item.mq? item.h : null,
167
+ }
168
+ });
169
+ });
170
+
171
+ });
172
+
173
+ const img_source_obj = sources.at(-1);
174
+
175
+ return {
176
+ tag: 'picture',
177
+ condition: condition,
178
+ children: [
179
+ ...sourceTagsArray,
180
+ {
181
+ tag: 'img',
182
+ className: imgClassName,
183
+ attrs: {
184
+ alt: alt_text,
185
+ width: img_source_obj.w,
186
+ height: img_source_obj.h,
187
+ srcset: srcset_builder(null, srcsetsArray),
188
+ sizes: sizes,
189
+ src: srcsetsArray.at(-1)[0],
190
+ loading: loadingMode,
191
+ }
192
+ }
193
+ ]
194
+ };
195
+
196
+ }
@@ -0,0 +1,11 @@
1
+ // navigazione scheda / elenco
2
+ let nav_primary = document.querySelector('.inner-nav-main');
3
+ if (nav_primary) {
4
+ // nav_primary.classList.add('text-right', 'd-print-none');
5
+ let nav = nav_primary.innerHTML;
6
+
7
+ document.querySelectorAll('.inner-nav').forEach(item => {
8
+ item.innerHTML = nav;
9
+ // item.classList.add('text-right', 'd-print-none');
10
+ });
11
+ }
@@ -0,0 +1,12 @@
1
+ // import './overlay.css'; incorporato nel css principale
2
+
3
+ export function overlay(context = document.body, scroll_lock = false) {
4
+ context.insertAdjacentHTML('beforeend', '<div class="overlay"/>');
5
+ if(scroll_lock) {
6
+ document.body.classList.add('overflow-hidden');
7
+ }
8
+ }
9
+ export function removeOverlay(context = document.body) {
10
+ context.querySelector(':scope > .overlay')?.remove();
11
+ document.body.classList.remove('overflow-hidden');
12
+ }
@@ -0,0 +1,51 @@
1
+ import { domBuilder } from '@massimo-cassandro/js-utilities/index.js';
2
+
3
+ const icon_file = document.body.dataset.icons;
4
+
5
+ export function printIcon({iconId, className='icon', title= null, output = 'html'}) {
6
+
7
+ if(!['html', 'element', 'builderArray'].includes(output)) {
8
+ // eslint-disable-next-line no-console
9
+ console.error(`'printIcon': parametro output non corretto (${output}). `+
10
+ 'Deve essere `html`, `element` o `builderArray`');
11
+ }
12
+
13
+ // accetta anche un solo argomento stringa (= iconId)
14
+ if(arguments.length === 1 && typeof arguments[0] === 'string') {
15
+ iconId = arguments[0];
16
+ }
17
+
18
+
19
+ const iconArray = [
20
+ {
21
+ tag: 'svg',
22
+ className: className,
23
+ children: [
24
+ {
25
+ tag: 'title',
26
+ content: title,
27
+ condition: title != null
28
+ },
29
+ {
30
+ tag: 'use',
31
+ attrs: {
32
+ href: `${icon_file}#${iconId}`
33
+ }
34
+ }
35
+ ]
36
+ }
37
+ ];
38
+
39
+ if(output === 'builderArray') {
40
+ return iconArray;
41
+ } else {
42
+
43
+ const iconElement = domBuilder(iconArray);
44
+ if(output === 'element') {
45
+ return iconElement;
46
+ } else {
47
+ return iconElement.outerHTML;
48
+ }
49
+ }
50
+
51
+ }
@@ -0,0 +1,30 @@
1
+ // import './spinner.css'; // incorporato nel css principale
2
+
3
+ /*
4
+ uso
5
+
6
+ import { spinner } from '@minimo/spinner.js';
7
+
8
+ spinner({container: xxxx, withOverlay: false|true });
9
+
10
+ */
11
+
12
+ // TODO rifare con domBuilder
13
+
14
+ export function spinner({container, withOverlay = false }) {
15
+ const spinnerMarkup =
16
+ `<div class="${withOverlay? 'spinner-overlay' : 'spinner-wrapper'}"><div class="spinner">Loading...</div></div>`;
17
+
18
+
19
+ if(container != null) {
20
+ container.insertAdjacentHTML('beforeend', spinnerMarkup);
21
+ } else {
22
+ return spinnerMarkup;
23
+ }
24
+ }
25
+
26
+ export function removeSpinner(container) {
27
+ container.querySelectorAll('.spinner-overlay, .spinner-wrapper')
28
+ .forEach(item => item.remove());
29
+
30
+ }