@massimo-cassandro/minimo 1.17.1 → 1.18.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.
Files changed (57) hide show
  1. package/design-tokens/tokens/src/components/unsplash-page/unsplash-page.minimo.tokens.jsonc +3 -3
  2. package/dev-tools/starter-kit/templates/__project__.code-workspace +2 -2
  3. package/dev-tools/starter-kit/webpack-modules/css-rules.mjs +1 -0
  4. package/dev-tools/starter-kit/webpack-modules/postcss.config.mjs +7 -0
  5. package/dev-tools/starter-kit/webpack-modules/purgecss-setup.mjs +95 -0
  6. package/dev-tools/starter-kit/webpack.config.mjs +102 -74
  7. package/index.js +1 -2
  8. package/package.json +9 -1
  9. package/snippets/error-pages/bundles/TwigBundle/Exception/error.html.twig +6 -6
  10. package/snippets/error-pages/error-pages.js +2 -2
  11. package/src/components/dismiss-alerts.js +1 -0
  12. package/src/components/form-reset-submit.js +1 -0
  13. package/src/components/inner-nav/inner-nav.css +2 -0
  14. package/src/components/inner-nav/inner-nav.js +1 -0
  15. package/src/components/modal-alert/modal-alert.js +12 -7
  16. package/src/components/modal-alert/modal-alert.module.css +11 -8
  17. package/src/components/modal-popup/modal-popup.js +1 -0
  18. package/src/components/modal-popup/modal-popup.module.css +1 -0
  19. package/src/components/overlay/overlay.css +2 -0
  20. package/src/components/overlay/overlay.js +1 -0
  21. package/src/components/sf-macro/sf-macro.js +1 -0
  22. package/src/components/slide-up-down-toggle/slide-up-down-toggle.js +1 -0
  23. package/src/components/slide-up-down-toggle/slide-up-down-toggle.module.css +1 -0
  24. package/src/components/snackbar/snackbar.js +1 -0
  25. package/src/components/snackbar/snackbar.module.css +2 -0
  26. package/src/components/unsplash-page/README.md +23 -0
  27. package/src/components/unsplash-page/unsplash-page.js +246 -117
  28. package/src/components/unsplash-page/unsplash-page.minimo.tokens.mjs +3 -3
  29. package/src/components/unsplash-page/unsplash-page.module.css +79 -77
  30. package/src/custom-properties.css +23 -23
  31. package/src/utilities/better-text.js +1 -0
  32. package/src/utilities/build-picture-tag.js +1 -0
  33. package/src/utilities/classnames.js +1 -0
  34. package/src/utilities/cookies.js +1 -0
  35. package/src/utilities/create-id.js +1 -0
  36. package/src/utilities/disable-btns-on-submit.js +1 -0
  37. package/src/utilities/dom-builder/dom-builder.js +2 -1
  38. package/src/utilities/enable-submit-btns.js +1 -0
  39. package/src/utilities/escape-html.js +1 -0
  40. package/src/utilities/generate-pwd.js +1 -0
  41. package/src/utilities/linked-dates.js +1 -0
  42. package/src/utilities/parse-file-size.js +1 -0
  43. package/src/utilities/random-id.js +1 -0
  44. package/src/utilities/relative-date.js +1 -0
  45. package/src/utilities/sentence-case.js +1 -0
  46. package/src/utilities/strip-tags.js +1 -0
  47. package/src/utilities/title-case.js +1 -0
  48. package/src/utilities/truncate-string.js +1 -0
  49. package/src/utilities/validation-error-class.js +1 -0
  50. package/src/components/unsplash-page/TODO unsplash-page-demo/README.md +0 -194
  51. package/src/components/unsplash-page/TODO unsplash-page-demo/changelog.md +0 -5
  52. package/src/components/unsplash-page/TODO unsplash-page-demo/unsplash-page/demo.js +0 -15
  53. package/src/components/unsplash-page/TODO unsplash-page-demo/unsplash-page/rollup.config.mjs +0 -26
  54. package/src/components/unsplash-page/TODO unsplash-page-demo/unsplash-page/unsplash-page-demo.min.js +0 -3
  55. package/src/components/unsplash-page/TODO.md +0 -3
  56. package/src/components/unsplash-page/img-breakpoints.js +0 -66
  57. package/src/components/unsplash-page/unsplash-page-body-reset.css +0 -14
@@ -1,7 +1,10 @@
1
+ /*! minimo - Modal Alert */
2
+
1
3
  .malert {
2
4
  width: 600px;
3
5
  max-width: 90vw;
4
6
  padding: 0;
7
+ margin: auto;
5
8
  background-color: var(--malert-background-color);
6
9
  border: none;
7
10
  border-radius: var(--malert-border-radius);
@@ -19,23 +22,23 @@
19
22
  }
20
23
 
21
24
  .success {
22
- --_malert-color: var(--malert-success-bg);
25
+ --_malert-color-bg: var(--malert-success-bg);
23
26
  --_malert-color-fg: var(--malert-success-fg);
24
27
  }
25
28
  .info {
26
- --_malert-color: var(--malert-info-bg);
29
+ --_malert-color-bg: var(--malert-info-bg);
27
30
  --_malert-color-fg: var(--malert-info-fg);
28
31
  }
29
32
  .warning {
30
- --_malert-color: var(--malert-warning-bg);
33
+ --_malert-color-bg: var(--malert-warning-bg);
31
34
  --_malert-color-fg: var(--malert-warning-fg);
32
35
  }
33
36
  .error {
34
- --_malert-color: var(--malert-error-bg);
37
+ --_malert-color-bg: var(--malert-error-bg);
35
38
  --_malert-color-fg: var(--malert-error-fg);
36
39
  }
37
40
  .confirm {
38
- --_malert-color: var(--malert-confirm-bg);
41
+ --_malert-color-bg: var(--malert-confirm-bg);
39
42
  --_malert-color-fg: var(--malert-confirm-fg);
40
43
  }
41
44
 
@@ -55,7 +58,6 @@
55
58
 
56
59
  .mMark {
57
60
  display: flex;
58
- flex: 0;
59
61
  align-items: center;
60
62
  justify-content: center;
61
63
  width: 100%;
@@ -65,6 +67,7 @@
65
67
  svg {
66
68
  width: 100px;
67
69
  aspect-ratio: 1;
70
+ background-color: transparent;
68
71
  fill: var(--_malert-color-fg);
69
72
  }
70
73
 
@@ -112,13 +115,13 @@
112
115
  flex-flow: column nowrap;
113
116
  gap: 1rem;
114
117
  align-items: center;
115
- justify-content: flex-start;
118
+ justify-content: flex-end;
116
119
 
117
120
  padding-top: 1rem;
118
121
  margin-top: auto;
119
122
 
120
123
  @media screen and (min-width: 600px) {
121
- flex-direction: row;
124
+ flex-direction: row-reverse;
122
125
  justify-content: space-between;
123
126
  }
124
127
  }
@@ -1,3 +1,4 @@
1
+ /*! minimo - Modal Popup */
1
2
  import { domBuilder } from '../../utilities/dom-builder/dom-builder.js';
2
3
  import { classnames } from '../../utilities/classnames.js';
3
4
  import { spinner } from '../spinner/spinner.js';
@@ -1,3 +1,4 @@
1
+ /*! minimo - Modal Popup */
1
2
  /*
2
3
  NB:
3
4
  `--modal-popup-width` and `--modal-popup-height` are used with a fallback value,
@@ -1,3 +1,5 @@
1
+ /*! minimo - Overlay */
2
+
1
3
  .overlay {
2
4
  position: fixed;
3
5
  inset: 0;
@@ -1,3 +1,4 @@
1
+ /*! minimo - Overlay */
1
2
  import './overlay.css';
2
3
 
3
4
  /**
@@ -1,3 +1,4 @@
1
+ /*! minimo - Symfony Macro */
1
2
  // import './sf-macro.css'; // import in the consuming project
2
3
 
3
4
  /**
@@ -1,4 +1,5 @@
1
1
  // @ts-check
2
+ /*! minimo - Slide Up/Down Toggle */
2
3
 
3
4
  import * as styles from './slide-up-down-toggle.module.css';
4
5
 
@@ -1,3 +1,4 @@
1
+ /*! minimo - Slide Up/Down Toggle */
1
2
  /*
2
3
  * CSS Module for slide-up-down-toggle.js
3
4
  *
@@ -1,3 +1,4 @@
1
+ /*! minimo - Snackbar */
1
2
  import { domBuilder } from '../../utilities/dom-builder/dom-builder.js';
2
3
  import { classnames } from '../../utilities/classnames.js';
3
4
  import * as styles from './snackbar.module.css';
@@ -1,3 +1,5 @@
1
+ /*! minimo - Snackbar */
2
+
1
3
  /* purgecss start ignore */
2
4
 
3
5
  .snackbarOuter {
@@ -0,0 +1,23 @@
1
+ # Unsplash page
2
+ Single page with random photo from [Unsplash](https://unsplash.com/) to display some messages (useful for error pages).
3
+
4
+ This component comes from some experiments I did with the Unsplash API. The initial purpose was to display more appealing error pages, but you can use it for any use.
5
+
6
+ For best results, it is advisable to display images from a photo collection prepared on Unsplash. The demos in this repository use my collection ["World"](https://unsplash.com/collections/3660951/world).
7
+
8
+ You need to create a server script to retrieve the JSON data of a random image from Unsplash (see [Get a Random Photo](https://unsplash.com/documentation#get-a-random-photo) on Unsplash API docs).
9
+
10
+ The script also implements [BlurHash](https://blurha.sh/), to show a placeholder of the image until it loads.
11
+
12
+ For more details and examples take a look at:
13
+
14
+ * <https://unsplash.com/developers>
15
+ * [A Random Image Slideshow With Unsplash and React](https://betterprogramming.pub/a-random-image-slideshow-with-unsplash-and-react-1b6aee698652)
16
+ * [Unsplash Random Photo 1](https://github.com/massimo-cassandro/area-test/tree/main/2023-03-unsplash-random-photo-1)
17
+ * [Random Unsplash Photos Slideshow](https://github.com/massimo-cassandro/area-test/tree/main/2023-05-unsplash-random-photo-2)
18
+ * [A Split Image Effect in React](https://medium.com/better-programming/a-split-image-effect-in-react-beb2baa3fe5f) and [split image](https://github.com/massimo-cassandro/area-test/tree/main/2023-07-split-image)
19
+
20
+
21
+ The SVG icons used in the script are from [Phosphor Icon](https://phosphoricons.com/). At the moment it is not possible to use different ones without modifying the source code.
22
+
23
+ The `snippets` directory contains an example of implementing Unsplash Page for an error page system in twig/Symfony
@@ -1,8 +1,11 @@
1
+ /*! minimo - unsplashPage */
2
+ import { domBuilder } from '../../utilities/dom-builder/dom-builder.js';
3
+ import { classnames } from '../../utilities/classnames.js';
1
4
  import imageIcon from '../../icons/image-duotone.svg';
2
5
  import arrowIcon from '../../icons/arrow-fat-lines-left-duotone.svg';
3
- import { breakpoints } from './img-breakpoints.js';
4
6
  import { fetchUnsplashData } from './fetch-unsplash-data.js';
5
7
  import { decode } from 'blurhash';
8
+ import * as styles from './unsplash-page.module.css';
6
9
 
7
10
  /**
8
11
  * Initialises an Unsplash full-page photo with blurhash placeholder, responsive picture element,
@@ -16,10 +19,11 @@ import { decode } from 'blurhash';
16
19
  * @param {string | null} [settings.text=null] - Optional body text.
17
20
  * @param {string | null} [settings.backLink=null] - Optional back-link HTML.
18
21
  * @param {boolean} [settings.hidePhotoLink=true] - When true, the Unsplash link is shown only on hover.
19
- * @param {Record<string, string> | null} [settings.cssModulesObj=null] - CSS Modules class map; when null, class names are used as-is.
20
- * @returns {void}
22
+ * @returns {Promise<void>}
21
23
  */
22
- export function unsplashPageJS(settings) {
24
+
25
+
26
+ export async function unsplashPage(settings) {
23
27
 
24
28
  const default_settings = {
25
29
  targetElement : null,
@@ -49,122 +53,247 @@ export function unsplashPageJS(settings) {
49
53
 
50
54
  const targetElement = /** @type {HTMLElement} */ (settings.targetElement);
51
55
 
52
- const cssClass = /** @param {string} classname */ classname =>
53
- settings.cssModulesObj? settings.cssModulesObj[classname] : classname;
54
-
55
- targetElement.innerHTML = `<div class="${cssClass('upContainer') + (settings.className? ` ${settings.className}` : '')}"></div>`;
56
- const container = /** @type {HTMLElement} */ (targetElement.querySelector('.' + cssClass('upContainer')));
57
- container.innerHTML = `<div class="${cssClass('upLoaderWrapper')}"><div class="${cssClass('upLoader')}"></div></div>`;
58
-
59
- fetchUnsplashData({unsplash_data_url: settings.unsplashDataUrl})
60
- .then(photo => {
61
- if (!photo) return;
62
-
63
- const pixels = decode(photo.blur_hash, container.offsetWidth, container.offsetHeight);
64
-
65
- const canvas = document.createElement('canvas');
66
- canvas.className = cssClass('upCanvas');
67
- canvas.width = container.offsetWidth;
68
- canvas.height = container.offsetHeight;
69
- const ctx = canvas.getContext('2d');
70
- if (ctx) {
71
- const imageData = ctx.createImageData(container.offsetWidth, container.offsetHeight);
72
- imageData.data.set(pixels);
73
- ctx.putImageData(imageData, 0, 0);
74
- }
75
-
76
-
77
- container.insertAdjacentElement('afterbegin', canvas);
78
-
79
- const formats = ['avif', 'webp', 'pjpg']; // `fm` parameter, in order of use
80
-
81
- container.insertAdjacentHTML('beforeend',
82
- '<picture>' +
83
- breakpoints.map((brk, idx) => {
84
-
85
- const is_last_brk = idx === breakpoints.length - 1;
86
- return formats.map(fmt => {
87
- const is_default_fmt = fmt === formats.at(-1);
88
-
89
- // https://unsplash.com/documentation#supported-parameters
90
- // https://docs.imgix.com/apis/rendering/size/w
91
- // https://docs.imgix.com/apis/rendering/size/h
92
- // https://docs.imgix.com/apis/rendering/size/ar
93
- // https://docs.imgix.com/apis/rendering/size/fit
94
- // https://docs.imgix.com/apis/rendering/size/crop
95
- // https://docs.imgix.com/apis/rendering/format/q
96
-
97
- let url = photo.base_url + (/\?/.test(photo.base_url)? '&' : '?') +
98
- 'fit=crop&crop=faces,entropy,edges' + // top, bottom, left, right, faces, focalpoint, edges, and entropy
99
- '&q=60' +
100
- `&w=${brk.w}&h=${brk.h}` +
101
- `&fm=${fmt}`;
102
-
103
- if(is_last_brk && is_default_fmt) {
104
-
105
- return '<img ' +
106
- `class="${cssClass('unsplashPhoto')}" `+
107
- `src="${url}" `+
108
- `srcset="${url}${brk.dpr2? ` 1x, ${url}&dpr=2 2x` : ''}" `+
109
- `alt="${photo.alt_description?? `${photo.author} / Unsplash`}" `+
110
- `width="${brk.w}" height="${brk.h}">`;
111
-
112
- } else {
113
- return '<source ' +
114
- `srcset="${url}${brk.dpr2? ` 1x, ${url}&dpr=2 2x` : ''}" `+
115
- (!is_default_fmt? `type="image/${fmt}" ` : '')+
116
- `media="${brk.mq}" `+
117
- `width="${brk.w}" height="${brk.h}">`;
56
+ const container = /** @type {HTMLElement} */ (domBuilder([
57
+ {
58
+ className: classnames( styles.container, settings.className ),
59
+ children: [
60
+ {
61
+ className: styles.loaderWrapper,
62
+ children: [
63
+ `div.${styles.loader}`
64
+ ]
65
+ }
66
+ ]
67
+ }
68
+ ], targetElement));
69
+
70
+
71
+ const imgData = await fetchUnsplashData({unsplash_data_url: settings.unsplashDataUrl});
72
+ if (!imgData) return;
73
+
74
+ const pixels = decode(imgData.blur_hash, container.offsetWidth, container.offsetHeight);
75
+
76
+ const canvas = document.createElement('canvas');
77
+ canvas.className = styles.canvas;
78
+ canvas.width = container.offsetWidth;
79
+ canvas.height = container.offsetHeight;
80
+ const ctx = canvas.getContext('2d');
81
+ if (ctx) {
82
+ const imageData = ctx.createImageData(container.offsetWidth, container.offsetHeight);
83
+ imageData.data.set(pixels);
84
+ ctx.putImageData(imageData, 0, 0);
85
+ }
86
+
87
+ container.insertAdjacentElement('afterbegin', canvas);
88
+
89
+ const formats = ['avif', 'webp', 'pjpg']; // `fm` parameter, in order of use
90
+
91
+ /*
92
+ - image dimensions grouped by aspect ratio: each group becomes one <source> tag
93
+ per format (avif/webp/pjpg)
94
+ - each aspect ratio is expressed as [w, h] (e.g. [16, 9]); `widths` lists the target
95
+ render widths for that group, used both as the `w` imgix param and as the `Nw`
96
+ descriptor in `srcset`. Height is derived per width from the group's ratio and
97
+ passed to imgix as `h` (see buildImgixUrl), so groups can safely reuse the same
98
+ `widths` list (e.g. 9/16 and 9/20) while still requesting a different crop height
99
+ - orientation and the aspect-ratio media feature are derived from the ratio itself:
100
+ ratio >= 1 -> landscape + min-aspect-ratio, ratio < 1 -> portrait + max-aspect-ratio
101
+ - NB: order matters. <picture> uses "first matching <source> wins" (unlike the CSS
102
+ cascade, where the last matching rule wins), so groups must be ordered from most to
103
+ least restrictive: landscape groups from the widest ratio to the narrowest, portrait
104
+ groups from the narrowest ratio to the widest. Orientation makes the two families
105
+ mutually exclusive, so their relative order doesn't matter
106
+ */
107
+ const sizes = [
108
+ // landscape 16/9
109
+ {
110
+ ar: [16,9],
111
+ widths: [2560, 1920, 1440, 1200, 800, 570]
112
+ },
113
+ // landscape 4/3
114
+ {
115
+ ar: [4,3],
116
+ widths: [1536, 1440, 1280, 840, 570]
117
+ },
118
+ // portrait 9/20 (very tall phones)
119
+ {
120
+ ar: [9,20],
121
+ widths: [1080, 960, 768, 570, 400]
122
+ },
123
+ // portrait 9/16
124
+ {
125
+ ar: [9,16],
126
+ widths: [1080, 960, 768, 570, 400]
127
+ },
128
+ // portrait 3/4 (tablets)
129
+ {
130
+ ar: [3,4],
131
+ widths: [1200, 1024, 840]
132
+ }
133
+ ];
134
+
135
+ // `url`/`searchParams` and the static imgix params (fit/crop/q) are set up once here
136
+ // and reused on every call: `fm`/`w`/`h` are the only parts that change per image, and
137
+ // the returned string is always consumed synchronously before the next call mutates them
138
+ /** @type {(w: number, h: number, fmt: string) => string} */
139
+ const buildImgixUrl = (() => {
140
+ const url = new URL(imgData.base_url),
141
+ searchParams = new URLSearchParams(url.search);
142
+
143
+ searchParams.set('fit', 'crop');
144
+ searchParams.set('crop', 'faces,entropy,edges'); // top, bottom, left, right, faces, focalpoint, edges, and entropy
145
+ searchParams.set('q', '60');
146
+
147
+ return (w, h, fmt) => {
148
+ searchParams.set('fm', fmt);
149
+ searchParams.set('w', String(w));
150
+ searchParams.set('h', String(h));
151
+
152
+ url.search = searchParams.toString();
153
+ return url.toString();
154
+ };
155
+ })();
156
+
157
+ // smallest width of the widest (most common) group, used as the plain <img> fallback
158
+ // for browsers without <picture> support: the <source> tags above cover everything else
159
+ const default_group = sizes[0],
160
+ default_w = default_group.widths[default_group.widths.length - 1],
161
+ default_h = Math.round(default_w * default_group.ar[1] / default_group.ar[0]);
162
+
163
+ // https://unsplash.com/documentation#supported-parameters
164
+ // https://docs.imgix.com/apis/rendering/size/w
165
+ // https://docs.imgix.com/apis/rendering/size/h
166
+ // https://docs.imgix.com/apis/rendering/size/ar
167
+ // https://docs.imgix.com/apis/rendering/size/fit
168
+ // https://docs.imgix.com/apis/rendering/size/crop
169
+ // https://docs.imgix.com/apis/rendering/format/q
170
+ domBuilder([
171
+ {
172
+ tag: 'picture',
173
+ children: [
174
+ ...sizes.flatMap(({ar, widths}) => {
175
+ const ratio = ar[0] / ar[1],
176
+ orientation = ratio >= 1 ? 'landscape' : 'portrait',
177
+ aspectFeature = orientation === 'landscape' ? 'min-aspect-ratio' : 'max-aspect-ratio',
178
+ media = `(orientation: ${orientation}) and (${aspectFeature}: ${ar[0]}/${ar[1]})`;
179
+
180
+ return formats.map(fmt => {
181
+ const is_default_fmt = fmt === formats.at(-1),
182
+ srcset = widths
183
+ .map(w => {
184
+ const h = Math.round(w * ar[1] / ar[0]);
185
+ return `${buildImgixUrl(w, h, fmt)} ${w}w`;
186
+ })
187
+ .join(', ');
188
+
189
+ return {
190
+ tag: 'source',
191
+ attrs: {
192
+ type: is_default_fmt? null : `image/${fmt}`,
193
+ media,
194
+ sizes: '100vw',
195
+ srcset
118
196
  }
119
- }).join('');
120
- }).join('') +
121
- '</picture>'
122
- ); // end insert <picture>
123
-
124
- const img = /** @type {HTMLImageElement | null} */ (container.querySelector('.' + cssClass('unsplashPhoto')));
125
- if (img) {
126
- img.onload = () => {
127
- container.querySelector('.' + cssClass('upLoaderWrapper'))?.remove();
128
- container.classList.add(cssClass('show'));
129
-
130
- container.insertAdjacentHTML('beforeend', `<div class="${cssClass('upMessageBox')}">
131
- <div class="${cssClass('upMessage')}">
132
- <h1>${settings.title}</h1>
133
- ${settings.text? `<p>${settings.text}</p>` : ''}
134
- ${settings.backLink ? `<p class="${cssClass('upBackLink')}">${settings.backLink}</p>` : ''}
135
-
136
- <div class="${cssClass('upArrowWrapper')}" role="button">
137
- <img src="${arrowIcon}" alt="Icona freccia">
138
- </div>
139
- </div>
140
-
141
- <div class="${cssClass('upCredits')}">
142
- <em>${photo.image_description}</em>
143
- <span>Photo
144
- <a href="${`${photo.author_profile}?utmSource=${settings.utmSource}&utm_medium=referral`}">
145
- ${photo.author} / Unsplash
146
- </a>
147
- </span>
148
- </div>
149
- </div>
150
- <div class="${cssClass('unsplashPhotoLink')}${settings.hidePhotoLink? ` ${cssClass('upShowOnHover')}` : ''}">
151
- <a href="${`${photo.unsplash_url}?utm_source=${settings.utmSource}&utm_medium=referral`}" target="_blank" rel="noopener noreferrer">
152
- <img src="${imageIcon}" alt="Icona immagine" />
153
- </a>
154
- </div>`);
155
-
156
- container.querySelector('.' + cssClass('upArrowWrapper'))?.addEventListener('click', e => {
157
- const target = /** @type {HTMLElement} */ (e.target);
158
- target.closest(`.${cssClass('upMessage')}`)?.classList.toggle(cssClass('hidden'));
159
- }, false);
160
-
161
- }; // end img.onload
162
- }
163
-
164
- }); // end .then
197
+ };
198
+ });
199
+ }),
200
+
201
+ // =>> img and its listener
202
+ {
203
+ tag: 'img',
204
+ className: styles.unsplashPhoto,
205
+ attrs: {
206
+ src: buildImgixUrl(default_w, default_h, formats[formats.length - 1]),
207
+ alt: imgData.alt_description ?? `${imgData.author} / Unsplash`
208
+ },
209
+ callback: el => {
210
+ el.onload = () => {
211
+ try {
212
+ container.querySelector(`.${styles.loaderWrapper}`)?.remove();
213
+ container.classList.add(styles.show);
214
+
215
+ domBuilder([
216
+ {
217
+ className: styles.messageBox,
218
+ children: [
219
+ {
220
+ className: styles.message,
221
+ children: [
222
+ { tag: 'h1', content: settings.title },
223
+ {
224
+ tag: 'p',
225
+ content: settings.text,
226
+ condition: !!settings.text
227
+ },
228
+ {
229
+ tag: 'p',
230
+ className: styles.backLink,
231
+ content: settings.backLink,
232
+ condition: !!settings.backLink
233
+ },
234
+ {
235
+ className: styles.arrowWrapper,
236
+ attrs: { role: 'button' },
237
+ children: [
238
+ { tag: 'img', attrs: { src: arrowIcon, alt: 'Icona freccia' } }
239
+ ],
240
+ callback: arrowEl => {
241
+ arrowEl.addEventListener('click', e => {
242
+ const target = /** @type {HTMLElement} */ (e.target);
243
+ target.closest(`.${styles.message}`)?.classList.toggle(styles.hidden);
244
+ }, false);
245
+ }
246
+ }
247
+ ]
248
+ },
249
+ {
250
+ className: styles.credits,
251
+ children: [
252
+ { tag: 'em', content: imgData.image_description },
253
+ {
254
+ tag: 'span',
255
+ content: `Photo <a href="${imgData.author_profile}?utmSource=${settings.utmSource}&utm_medium=referral">${imgData.author} / Unsplash</a>`
256
+ }
257
+ ]
258
+ }
259
+ ]
260
+ },
261
+ {
262
+ className: classnames( styles.unsplashPhotoLink, settings.hidePhotoLink ? styles.showOnHover : null ),
263
+ children: [
264
+ {
265
+ tag: 'a',
266
+ attrs: {
267
+ href: `${imgData.unsplash_url}?utm_source=${settings.utmSource}&utm_medium=referral`,
268
+ target: '_blank',
269
+ rel: 'noopener noreferrer'
270
+ },
271
+ children: [
272
+ { tag: 'img', attrs: { src: imageIcon, alt: 'Icona immagine' } }
273
+ ]
274
+ }
275
+ ]
276
+ }
277
+ ], container);
278
+
279
+ } catch(e) {
280
+ console.error( '[Unsplash Page] ' + e ); // eslint-disable-line
281
+ }
282
+ };
283
+
284
+ el.onerror = () => {
285
+ console.error( '[Unsplash Page] Errore nel caricamento dell\'immagine' ); // eslint-disable-line
286
+ container.querySelector('.' + styles.loaderWrapper)?.remove();
287
+ };
288
+ }
289
+ }
290
+ ]
291
+ }
292
+ ], container);
293
+
165
294
 
166
295
  } catch(e) {
167
- console.error( e ); // eslint-disable-line
296
+ console.error( '[Unsplash Page] ' + e ); // eslint-disable-line
168
297
  }
169
298
 
170
299
  }
@@ -1,5 +1,5 @@
1
1
  export default {
2
- up: {
2
+ 'uspg': {
3
3
  bg: {
4
4
  color: {
5
5
  $type: 'color',
@@ -59,7 +59,7 @@ export default {
59
59
  h1: {
60
60
  color: {
61
61
  $type: 'color',
62
- $value: '{up.text.color}'
62
+ $value: '{uspg.text.color}'
63
63
  },
64
64
  font: {
65
65
  size: {
@@ -86,7 +86,7 @@ export default {
86
86
  box: {
87
87
  bg: {
88
88
  $type: 'color',
89
- $value: '{up.link.color}'
89
+ $value: '{uspg.link.color}'
90
90
  }
91
91
  },
92
92
  fill: {