@massimo-cassandro/minimo 1.12.1 → 1.14.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.
@@ -0,0 +1,195 @@
1
+ import { domBuilder, classnames } from '@massimo-cassandro/minimo';
2
+ import { unsplashParams, unsplashPictureTag } from './unsplash-utilities.js';
3
+ import { unsplashGetImgs } from './unsplash-get-imgs.js';
4
+ import * as styles from './unsplash-imgs-display.module.css';
5
+ import imageIcon from '../icons/image.svg?inline';
6
+
7
+ let displayContainer;
8
+
9
+ // elemento .imgDisplayWrapper attualmente trascinato (drag and drop riordinamento)
10
+ let draggedItem = null;
11
+
12
+
13
+ // abilita il trascinamento di wrapperEl tramite la maniglia handleEl
14
+ function initSortHandle(handleEl, wrapperEl){
15
+ handleEl.addEventListener('mousedown', () => {
16
+ wrapperEl.draggable = true;
17
+ });
18
+ handleEl.addEventListener('mouseup', () => {
19
+ wrapperEl.draggable = false;
20
+ });
21
+
22
+ wrapperEl.addEventListener('dragstart', e => {
23
+ draggedItem = wrapperEl;
24
+ e.dataTransfer.effectAllowed = 'move';
25
+ e.dataTransfer.setData('text/plain', wrapperEl.dataset.imgId);
26
+ wrapperEl.classList.add(styles.dragging);
27
+ });
28
+
29
+ wrapperEl.addEventListener('dragend', () => {
30
+ wrapperEl.draggable = false;
31
+ wrapperEl.classList.remove(styles.dragging);
32
+ draggedItem = null;
33
+ updateHiddenFieldOrder();
34
+ });
35
+ }
36
+
37
+ // aggiorna il value del campo hidden in base al nuovo ordine (DOM) degli .imgDisplayWrapper
38
+ function updateHiddenFieldOrder(){
39
+ const data = JSON.parse(unsplashParams.hiddenField.value || '[]');
40
+ const order = [...displayContainer.querySelectorAll(`.${styles.imgDisplayWrapper}`)]
41
+ .map(el => el.dataset.imgId);
42
+
43
+ data.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
44
+ unsplashParams.hiddenField.value = JSON.stringify(data);
45
+ }
46
+
47
+
48
+ // markup placeholder
49
+ const placeHolderObjEl = {
50
+ className: styles.placeholder,
51
+ content: imageIcon
52
+ };
53
+
54
+
55
+ export function unsplashImgsDisplayInit(){
56
+
57
+ domBuilder([
58
+
59
+ 'p.my-0.small Utilizzare il pulsante “Seleziona immagine” per inserire un termine di ricerca e quindi selezionare ' +
60
+ `almeno un’immagine <strong>(massimo ${unsplashParams.maxImgs})</strong>.`,
61
+ 'p-my-0.small Se vuoi sostituire una foto, prima rimuovila dall’elenco delle immagini selezionate (qui sotto) ' +
62
+ 'e quindi selezionane una nuova.',
63
+ 'p.my-0.small Per pubblicare un gruppo, è necessario che sia presente almeno un’immagine; '+
64
+ '<strong>la prima sarà utilizzata come copertina</strong> (puoi variare l’ordinamento, se necessario, trascinando l’icona in fondo ad ogni card immagine).',
65
+
66
+ {
67
+ className: styles.displayContainer,
68
+ callback: el => displayContainer = el
69
+ },
70
+ {
71
+ className: 'mt-lg text-right',
72
+ children: [
73
+ {
74
+ tag: 'button',
75
+ attrs: {
76
+ class: 'btn btn-outline-secondary',
77
+ type: 'button'
78
+ },
79
+ content: 'Seleziona immagini',
80
+ callback: el => {
81
+ el.addEventListener('click', () => {
82
+ unsplashGetImgs();
83
+ });
84
+ }
85
+ }
86
+ ]
87
+ }
88
+
89
+ ], unsplashParams.hiddenField, {insertMode: 'before'});
90
+
91
+ // aggiunta displayContainer ad unsplashParams perché utilizzato anche dall'applicazione principale
92
+ unsplashParams.displayContainer = displayContainer;
93
+
94
+
95
+ // =>> listener rimozione foto
96
+ displayContainer.addEventListener('click', e => {
97
+ if(e.target.classList.contains(styles.closeBtn)) {
98
+ const imgId = e.target.imgId;
99
+
100
+ e.target.closest(`.${styles.imgDisplayWrapper}`).remove();
101
+
102
+ // aggiornamento value hidden
103
+ let data = JSON.parse(unsplashParams.hiddenField.value || '[]');
104
+ data = data.filter(row => row.id !== imgId);
105
+ unsplashParams.hiddenField.value = JSON.stringify(data);
106
+
107
+ displayContainer.insertAdjacentElement('beforeend', domBuilder([placeHolderObjEl]));
108
+
109
+ }
110
+ });
111
+
112
+ // riordinamento via drag and drop (maniglia: styles.sortArea > div)
113
+ // le card sono disposte in griglia su un'unica riga (grid-template-columns: repeat(maxImgs, 1fr)),
114
+ // quindi l'inserimento prima/dopo si basa sulla posizione orizzontale del cursore
115
+ displayContainer.addEventListener('dragover', e => {
116
+ if(!draggedItem) {
117
+ return;
118
+ }
119
+ e.preventDefault();
120
+
121
+ const targetWrapper = e.target.closest(`.${styles.imgDisplayWrapper}`);
122
+ if(!targetWrapper || targetWrapper === draggedItem) {
123
+ return;
124
+ }
125
+
126
+ const rect = targetWrapper.getBoundingClientRect();
127
+ const insertBefore = (e.clientX - rect.left) < rect.width / 2;
128
+
129
+ targetWrapper.parentNode.insertBefore(
130
+ draggedItem,
131
+ insertBefore? targetWrapper : targetWrapper.nextSibling
132
+ );
133
+ });
134
+ }
135
+
136
+ export function unsplashImgsDisplay(data = null){
137
+
138
+ if(data == null) {
139
+ data = JSON.parse(unsplashParams.hiddenField.value || '[]');
140
+ }
141
+
142
+
143
+ domBuilder([
144
+ ...data.map( row => (
145
+ {
146
+ className: styles.imgDisplayWrapper,
147
+ attrs: { 'data-img-id': row.id },
148
+ // inizializzazione trascinamento: a questo punto l'intero sotto-albero
149
+ // (incluso styles.sortArea) è già collegato al wrapper
150
+ // l'intera area sortArea (non solo l'icona al suo interno) fa da maniglia
151
+ callback: el => initSortHandle(el.querySelector(`.${styles.sortArea}`), el),
152
+ children: [
153
+ ...unsplashPictureTag(row, {
154
+ width: 300,
155
+ addPopover: true,
156
+ popoverImgWidth: 800,
157
+ imgExtraClass: styles.img,
158
+ imgExtraAttrs: {draggable: 'false'}
159
+ }),
160
+
161
+ {
162
+ className: styles.imgDisplayInfo,
163
+ children: [
164
+ {
165
+ tag: 'ul',
166
+ children: [
167
+ `li ${row.description}`,
168
+ {
169
+ tag: 'li',
170
+ children: [
171
+ row.author_tag
172
+ ]
173
+ }
174
+ ]
175
+ },
176
+ {
177
+ tag: 'button',
178
+ className: classnames('btn-close', styles.closeBtn),
179
+ callback: el => el.imgId = row.id
180
+ }
181
+ ]
182
+ },
183
+
184
+ {
185
+ className: styles.sortArea,
186
+ children: [
187
+ 'div'
188
+ ]
189
+ }
190
+ ]
191
+ }
192
+ )),
193
+ ...(Array.from({length: unsplashParams.maxImgs - data.length}, () => placeHolderObjEl ))
194
+ ], displayContainer, {emptyParent: true});
195
+ }
@@ -0,0 +1,119 @@
1
+ .displayContainer {
2
+
3
+ display: grid;
4
+ grid-template-rows: auto auto auto;
5
+ grid-template-columns: repeat(4, 1fr);
6
+ /* grid-auto-rows: 1fr; */ /* al posto di grid-template-rows */
7
+ /* grid-template: row / col; */
8
+ grid-auto-flow: row;
9
+ gap: 1.5rem;
10
+ /* align-items: center; */
11
+ /* justify-items: start; */
12
+ min-height: 0;
13
+ padding: .3rem;
14
+
15
+ margin-block: 1.5rem;
16
+ }
17
+
18
+ .imgDisplayWrapper, .placeholder {
19
+ grid-row: span 3;
20
+ border-radius: var(--ada-border-radius);
21
+ }
22
+ .placeholder {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ min-height: 10rem;
27
+ background-color: var(--ada-grigio-1);
28
+ border: 1px solid var(--ada-grigio-3);
29
+ opacity: .7;
30
+ svg {
31
+ width: 3rem;
32
+ fill: var(--ada-grigio-3);
33
+ }
34
+ }
35
+
36
+ .imgDisplayWrapper {
37
+ position: relative;
38
+ display: grid;
39
+ grid-template-rows: subgrid;
40
+ gap: .3rem;
41
+ padding: 0;
42
+ overflow: hidden;
43
+ background-color: var(--ada-body-bg);
44
+ border: 1px solid var(--ada-grigio-2);
45
+ box-shadow: var(--ada-box-shadow);
46
+ }
47
+
48
+ .img {
49
+ display: block;
50
+ width: 100%;
51
+ height: 200px;
52
+ /* padding: .3rem; */
53
+ object-fit: cover;
54
+ object-position: center;
55
+ }
56
+ .imgDisplayInfo {
57
+ padding: .5rem;
58
+
59
+ ul {
60
+ padding: 0;
61
+ margin: 0;
62
+ list-style: none;
63
+ }
64
+ }
65
+ .closeBtn {
66
+ position: absolute;
67
+ top: .5rem;
68
+ right: .5rem;
69
+ padding: .4rem;
70
+ background-color: var(--ada-body-bg);
71
+ border: 1px solid var(--ada-grigio-4);
72
+ border-radius: 50%;
73
+ }
74
+
75
+ .sortArea {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ padding: .4rem;
80
+ margin: .2rem;
81
+ cursor: grab;
82
+ border-radius: var(--ada-border-radius);
83
+
84
+ @media (hover: hover) {
85
+ &:hover {
86
+ background-color: var(--ada-grigio-1);
87
+ }
88
+ }
89
+
90
+ &:active {
91
+ cursor: grabbing;
92
+ }
93
+
94
+ > div {
95
+ position: relative;
96
+
97
+ &, &::after, &::before {
98
+ width: 2px;
99
+ height: 20px;
100
+ background-color: var(--ada-grigio-4);
101
+ }
102
+ &::after, &::before {
103
+ position: absolute;
104
+ top: 0;
105
+ content: '';
106
+ }
107
+ &::before {
108
+ left: -6px;
109
+ }
110
+ &::after {
111
+ right: -6px;
112
+ }
113
+ }
114
+ }
115
+
116
+ .dragging {
117
+ cursor: grabbing;
118
+ opacity: .4;
119
+ }
@@ -0,0 +1,344 @@
1
+ // estrae da un oggetto result di una singola riga, le informazioni necessarie
2
+ // all'applicazione e dove necessario esegue piccole post-eleborazioni
3
+
4
+ /*
5
+ raw data:
6
+
7
+ {
8
+ "id": "uNvgvo2cs7k",
9
+ "alternative_slugs": {...},
10
+ "created_at": "2018-04-10T13:00:44Z",
11
+ "updated_at": "2026-07-06T07:30:35Z",
12
+ "promoted_at": "2018-04-11T07:34:53Z",
13
+ "width": 3840,
14
+ "height": 2565,
15
+ "color": "#26260c",
16
+ "blur_hash": "LoGuj:IVaKWX~XIqWBf6x]NeWDo0",
17
+ "description": null,
18
+ "breadcrumbs": [],
19
+ "urls": {
20
+ "raw" : "https://images.unsplash.com/photo-1523365154888-8a758819b722?ixid=M3w5OTA3Mjh8MHwxfHNlYXJjaHwxfHxTaWNpbGlhfGl0fDF8MHx8fDE3ODMzMzA5NTN8MA&ixlib=rb-4.1.0",
21
+ "full" : "...",
22
+ "regular" : "...",
23
+ "small" : "...",
24
+ "thumb" : "...",
25
+ "small_s3" : "..."
26
+ },
27
+ "likes": 1001,
28
+ "liked_by_user": false,
29
+ "bookmarked": false,
30
+ "current_user_collections": [],
31
+ "sponsorship": null,
32
+ "topic_submissions": {...},
33
+ "asset_type": "photo",
34
+ "slug": "photo-of-house-near-cliff-and-body-of-water-uNvgvo2cs7k",
35
+ "alt_description": "photo of house near cliff and body of water",
36
+ "links": {
37
+ "html": "https://unsplash.com/photos/photo-of-house-near-cliff-and-body-of-water-uNvgvo2cs7k",
38
+ "self": "...",
39
+ "download": "...",
40
+ "download_location": "..."
41
+ },
42
+ "user": {
43
+ ...
44
+ "links": {
45
+ "html": "https://unsplash.com/@samferrara"
46
+ "self": "...",
47
+ "photos": "...",
48
+ "likes": "...",
49
+ },
50
+ "profile_image": {..},
51
+ ...
52
+ "social": {...}
53
+ }
54
+ }
55
+
56
+ */
57
+
58
+ import { getAppData } from '@src/index.js';
59
+ import { randomId, classnames } from '@massimo-cassandro/minimo';
60
+
61
+ import * as styles from './unsplash-get-imgs.module.css';
62
+
63
+ export const unsplashParams = {
64
+ maxImgs: 4, // numero massimo di immagini totali
65
+ hiddenField : document.getElementById('infoImmagini')
66
+ };
67
+
68
+
69
+
70
+ // Default query parameters for the Unsplash /search/photos endpoint.
71
+ // All params are optional except `query` (passed at call time).
72
+ const defaultUnsplashParams = {
73
+ per_page: 18, // results per page — max: 30
74
+ // order_by: 'relevant', // "relevant" | "latest"
75
+ content_filter: 'high', // "low" | "high" — filters explicit content
76
+ orientation: 'landscape', // "landscape" | "portrait" | "squarish"
77
+ lang: 'it',
78
+
79
+ // Other available params (not set by default):
80
+ // page : (int) — page number, default 1
81
+ // color : (string) — "black_and_white" | "black" | "white" | "yellow" |
82
+ // "orange" | "red" | "purple" | "magenta" |
83
+ // "green" | "teal" | "blue"
84
+ // collections : (string) — comma-separated collection IDs to narrow results
85
+ }
86
+
87
+ ,app_data = getAppData()
88
+ ,SEARCH_API = app_data.apps + '/unsplash-browser'
89
+ ;
90
+
91
+ export function setRequestUrl(query, page = null) {
92
+ const searchParams = new URLSearchParams({
93
+ query,
94
+ ...defaultUnsplashParams,
95
+ ...(page? {page} : {})
96
+ });
97
+ return `${SEARCH_API}?${searchParams}`;
98
+ }
99
+
100
+
101
+ export function parseUnsplashResult(data){
102
+
103
+ const img_data = {
104
+ id : data.id,
105
+ color : data.color,
106
+ width : data.width,
107
+ height : data.height,
108
+ // description : data.description,
109
+ // location : data.location.name,
110
+ alt_description : data.alt_description,
111
+ // created_at : data.created_at,
112
+ raw_url : data.urls?.raw,
113
+ unsplash_url : data.links.html,
114
+ author : data.user?.name?? data.user?.username,
115
+ author_profile_url : data.user?.links?.html,
116
+ blur_hash : data.blur_hash,
117
+ description : (() => {
118
+ let location = data.location?.name?? data.location?.city,
119
+ description = data.description?? data.alt_description;
120
+ if(data.description && location) {
121
+ location = (new RegExp(location, 'i')).test(description)? null : location;
122
+ }
123
+ return [description, location]
124
+ .filter(i => i != null).join (' / ');
125
+ })()
126
+ };
127
+
128
+ const author_profile_url = new URL(img_data.author_profile_url);
129
+ author_profile_url.search = new URLSearchParams({ utm_source: 'xxxxxx', utm_medium: 'referral' });
130
+ img_data.author_profile_full_url = author_profile_url.toString();
131
+
132
+ img_data.author_tag = {
133
+ tag: 'a',
134
+ attrs: {
135
+ href: img_data.author_profile_full_url,
136
+ target: '_blank',
137
+ rel: 'noopener noreferrer'
138
+ },
139
+ content: `${img_data.author} / Unsplash`,
140
+ };
141
+
142
+
143
+ return img_data;
144
+ }
145
+
146
+
147
+ // restituisce l'array domBuilder per la costruizione di un tag picture da un'immagine unsplah
148
+ // NB con dati preelaborati da parseUnsplashResult
149
+ // https://unsplash.com/documentation#supported-parameters
150
+ // https://docs.imgix.com/apis/rendering/size/w
151
+ // https://docs.imgix.com/apis/rendering/size/h
152
+ // https://docs.imgix.com/apis/rendering/size/ar
153
+ // https://docs.imgix.com/apis/rendering/size/fit
154
+ // https://docs.imgix.com/apis/rendering/size/crop
155
+ // https://docs.imgix.com/apis/rendering/format/q
156
+ // https://docs.imgix.com/en-US/apis/rendering/device-pixel-ratio
157
+
158
+ export function unsplashPictureTag(img_data, options = {}){
159
+
160
+ options = {
161
+ fixedRatio: true, // utilizza l'aspect ratio originale della foto
162
+ width: 300,
163
+ pictureExtraClass: null,
164
+ pictureExtraAttrs: {},
165
+ imgExtraClass: null,
166
+ imgExtraAttrs: null,
167
+ addPopover: true,
168
+ popoverImgWidth: 600,
169
+
170
+ ...options
171
+ };
172
+
173
+ const formats = ['avif', 'webp', 'pjpg'] // `fmt` parameter, in order of use
174
+ ,base_url = new URL(img_data.raw_url)
175
+ ,searchParams = new URLSearchParams(base_url.search)
176
+ ,img_params = [['q', '60']]
177
+ ;
178
+
179
+
180
+ if(options.fixedRatio) {
181
+ img_params.push(
182
+ ['w', options.width],
183
+ // ['fit', 'crop'],
184
+ // ['crop', 'faces,entropy,edges']
185
+ );
186
+ img_params.forEach(param => searchParams.set(...param));
187
+
188
+ // nome univoco per l'associazione anchor (Anchor Positioning API) tra thumb e popover di ingrandimento
189
+ const zoomAnchorName = `--unsplash-zoom-${randomId()}`;
190
+
191
+ // riferimento al popover di ingrandimento, valorizzato dalla sua callback domBuilder
192
+ // (usato nel listener click della thumb, eseguito solo dopo che l'intera figure è stata costruita)
193
+ let zoomPopoverEl;
194
+
195
+ return [
196
+ {
197
+ tag: 'picture',
198
+ className: options.pictureExtraClass,
199
+ attrs: {
200
+ style: options.addPopover? `anchor-name: ${zoomAnchorName};` : null,
201
+ ...options.pictureExtraAttrs
202
+ },
203
+ children: formats.map(fmt => {
204
+ const is_default_fmt = fmt === formats.at(-1);
205
+ const srcsetArray= [];
206
+
207
+ searchParams.set('fm', fmt);
208
+
209
+ [1,2,3].forEach(dpr => {
210
+ searchParams.set('dpr', dpr);
211
+ base_url.search = searchParams.toString();
212
+ srcsetArray.push(base_url.toString() +` ${dpr}x`);
213
+ });
214
+
215
+ if(is_default_fmt) {
216
+
217
+ return {
218
+ tag: 'img',
219
+ className: classnames(options.imgExtraClass, options.addPopover && styles.hasPopover),
220
+ attrs: {
221
+ alt: img_data.alt_description?? `${img_data.author} / Unsplash`,
222
+ src: srcsetArray[0],
223
+ srcset: srcsetArray.join(','),
224
+ width: options.width,
225
+ // height:
226
+ ...options.imgExtraAttrs
227
+ },
228
+ callback: options.addPopover
229
+ ? el => el.addEventListener('click', () => zoomPopoverEl.togglePopover())
230
+ : null
231
+ };
232
+
233
+ } else {
234
+ return {
235
+ tag: 'source',
236
+ attrs: {
237
+ srcset: srcsetArray.join(','),
238
+ type: `image/${fmt}`,
239
+ // width:
240
+ // height:
241
+ }
242
+ };
243
+ }
244
+
245
+ })
246
+ },
247
+ // popover di ingrandimento (Popover API + Anchor Positioning API)
248
+ ...(options.addPopover
249
+ ? [{
250
+ tag: 'div',
251
+ className: styles.zoomPopover,
252
+ attrs: {
253
+ popover: 'auto',
254
+ style: `position-anchor: ${zoomAnchorName};`
255
+ },
256
+ callback: el => zoomPopoverEl = el,
257
+ children: unsplashPictureTag(img_data, {width: options.popoverImgWidth, imgExtraClass: styles.popoverImg, addPopover: false})
258
+ }]
259
+ : []
260
+ )
261
+ ];
262
+
263
+ } else {
264
+
265
+ // img_params.push(
266
+ // ['fit', 'crop'],
267
+ // ['crop', 'faces,entropy,edges'], // top, bottom, left, right, faces, focalpoint, edges, and entropy
268
+ // );
269
+ // const sizes = [
270
+ // {w: 2560, ratio: 16/9},
271
+ // {w: 1920, ratio: 16/9},
272
+ // {w: 1400, ratio: 16/9},
273
+ // {w: 1280, ratio: 16/9},
274
+ // {w: 1024, ratio: 4/3},
275
+ // {w: 760, ratio: 4/3},
276
+ // {w: 400, ratio: 9/21},
277
+ // ].map(size => ({...size, h: Math.ceil(size.w / size.ratio)}) );
278
+
279
+ // console.log(sizes);
280
+
281
+ // const sizesAttr = sizes.map(s => `(width <= ${s.w}px and min-aspect-ratio: ${s.ratio.toFixed(3)}) ${s.w}px`).join(',');
282
+ // img_params.forEach(param => searchParams.set(...param));
283
+
284
+ // return [
285
+ // {
286
+ // tag: 'picture',
287
+ // children: formats.map(fmt => {
288
+ // const is_default_fmt = fmt === formats.at(-1);
289
+
290
+ // const srcsetArray = [];
291
+ // searchParams.set('fmt', fmt);
292
+
293
+ // sizes.forEach(size => {
294
+ // searchParams.set('w', size.w);
295
+ // searchParams.set('h', size.h);
296
+ // base_url.search = searchParams.toString();
297
+ // srcsetArray.push({url: base_url.toString(), size: size.w});
298
+ // });
299
+
300
+ // const srcsetAttr = srcsetArray.map(item => `${item.url} ${item.size}w`).join(',');
301
+
302
+ // if(is_default_fmt) {
303
+
304
+ // return {
305
+ // tag: 'img',
306
+ // className: 'up-img',
307
+ // attrs: {
308
+ // srcset: srcsetAttr,
309
+ // sizes: sizesAttr,
310
+ // alt: img_data.alt_description?? `${img_data.author} / Unsplash`,
311
+ // src: srcsetArray[Math.floor(srcsetArray.length / 2)].url,
312
+
313
+ // // width e height non necessari (e scorretti visto le differenti proporzioni delle varie foto)
314
+ // // le dimesnioni sono gestite nel css
315
+ // // width:
316
+ // // height:
317
+ // }
318
+ // };
319
+
320
+ // } else {
321
+ // return {
322
+ // tag: 'source',
323
+ // attrs: {
324
+ // srcset: srcsetAttr,
325
+ // sizes: sizesAttr,
326
+ // type: `image/${fmt}`,
327
+ // // width:
328
+ // // height:
329
+ // }
330
+ // };
331
+ // }
332
+
333
+ // })
334
+ // }
335
+ // ];
336
+ }
337
+
338
+ }
339
+
340
+
341
+ // helper testo pulsante aggiuni/rimuovi img
342
+ export function setImgBtnText(imgIsSelected) {
343
+ return imgIsSelected? 'Rimuovi' : 'Seleziona';
344
+ }
@@ -52,6 +52,9 @@ function closeDialog(el) {
52
52
  * @param {string | any[] | null} [params.footerContent=null] - Footer content: plain text, HTML, or a domBuilder array.
53
53
  * @returns {HTMLDialogElement} The dialog element.
54
54
  */
55
+
56
+ // TODO attributi arua anche sul triggerm vedi ada-frontend/src/modal-dialog/modal-dialog.js
57
+
55
58
  export function modalPopup({
56
59
 
57
60
  /** dialog extra class */