@easy-web/content-blocks 1.0.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 (54) hide show
  1. package/README.md +332 -0
  2. package/dist/components/__tests__/UniversalMedia.test.d.ts +2 -0
  3. package/dist/components/__tests__/UniversalMedia.test.d.ts.map +1 -0
  4. package/dist/components/__tests__/UniversalMedia.test.js +46 -0
  5. package/dist/components/__tests__/UniversalMedia.test.js.map +1 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/schemas/__tests__/notFound.test.d.ts +2 -0
  11. package/dist/schemas/__tests__/notFound.test.d.ts.map +1 -0
  12. package/dist/schemas/__tests__/notFound.test.js +29 -0
  13. package/dist/schemas/__tests__/notFound.test.js.map +1 -0
  14. package/dist/schemas/galleries.d.ts +319 -0
  15. package/dist/schemas/galleries.d.ts.map +1 -0
  16. package/dist/schemas/galleries.js +136 -0
  17. package/dist/schemas/galleries.js.map +1 -0
  18. package/dist/schemas/notFound.d.ts +35 -0
  19. package/dist/schemas/notFound.d.ts.map +1 -0
  20. package/dist/schemas/notFound.js +30 -0
  21. package/dist/schemas/notFound.js.map +1 -0
  22. package/package.json +51 -0
  23. package/src/components/.gitkeep +0 -0
  24. package/src/components/BlogPostCard.astro +79 -0
  25. package/src/components/Card.astro +70 -0
  26. package/src/components/CardGrid.astro +29 -0
  27. package/src/components/ContactSection.astro +73 -0
  28. package/src/components/CtaSection.astro +82 -0
  29. package/src/components/DraftBanner.astro +33 -0
  30. package/src/components/Footer.astro +81 -0
  31. package/src/components/GalleryCarousel.astro +296 -0
  32. package/src/components/GalleryFeatureHighlight.astro +153 -0
  33. package/src/components/GalleryHeroSlider.astro +362 -0
  34. package/src/components/GalleryImageGrid.astro +143 -0
  35. package/src/components/GalleryLightboxGrid.astro +353 -0
  36. package/src/components/GalleryMasonryGrid.astro +127 -0
  37. package/src/components/GallerySection.astro +69 -0
  38. package/src/components/Header.astro +210 -0
  39. package/src/components/HeaderCentered.astro +231 -0
  40. package/src/components/HeaderFlyout.astro +373 -0
  41. package/src/components/HeaderHideOnScroll.astro +237 -0
  42. package/src/components/Hero.astro +78 -0
  43. package/src/components/LanguageNotice.astro +32 -0
  44. package/src/components/LanguageSwitch.astro +67 -0
  45. package/src/components/LegalLayout.astro +53 -0
  46. package/src/components/NotFound.astro +124 -0
  47. package/src/components/Prose.astro +156 -0
  48. package/src/components/Section.astro +37 -0
  49. package/src/components/ThemeToggle.astro +82 -0
  50. package/src/components/UniversalMedia.astro +102 -0
  51. package/src/components/__tests__/UniversalMedia.test.ts +65 -0
  52. package/src/schemas/__tests__/notFound.test.ts +32 -0
  53. package/src/schemas/galleries.ts +152 -0
  54. package/src/schemas/notFound.ts +33 -0
@@ -0,0 +1,353 @@
1
+ ---
2
+ import type { GalleryLightboxGridData } from '../schemas/galleries';
3
+
4
+ type Props = Omit<GalleryLightboxGridData, 'kind' | 'locale' | 'translationKey'>;
5
+
6
+ const {
7
+ title,
8
+ columns = 3,
9
+ gap = 'md',
10
+ aspectRatio = 'square',
11
+ items,
12
+ } = Astro.props;
13
+
14
+ const gridClass = [
15
+ 'ew-lightbox-grid',
16
+ `ew-lightbox-grid--cols-${columns}`,
17
+ `ew-lightbox-grid--gap-${gap}`,
18
+ ].join(' ');
19
+
20
+ const ratioClass = `ew-lightbox-grid__img--ratio-${aspectRatio.replace('/', '-')}`;
21
+ ---
22
+
23
+ {items.length > 0 && (
24
+ <section class="ew-lightbox-wrap" data-lightbox-root>
25
+ <div class="ew-lightbox-wrap__inner">
26
+ {title && <h2 class="ew-lightbox-wrap__title">{title}</h2>}
27
+ <div class={gridClass}>
28
+ {items.map((item, i) => (
29
+ <button
30
+ type="button"
31
+ class="ew-lightbox-grid__item"
32
+ data-index={i}
33
+ aria-label={`Open ${item.alt}`}
34
+ >
35
+ <img
36
+ src={item.src}
37
+ alt={item.alt}
38
+ class={`ew-lightbox-grid__img ${ratioClass}`}
39
+ loading="lazy"
40
+ />
41
+ </button>
42
+ ))}
43
+ </div>
44
+ <div
45
+ class="ew-lightbox"
46
+ role="dialog"
47
+ aria-modal="true"
48
+ aria-label={title ?? 'Image lightbox'}
49
+ data-open="false"
50
+ hidden
51
+ >
52
+ <button
53
+ type="button"
54
+ class="ew-lightbox__close"
55
+ aria-label="Close lightbox"
56
+ >
57
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
58
+ <line x1="18" y1="6" x2="6" y2="18"></line>
59
+ <line x1="6" y1="6" x2="18" y2="18"></line>
60
+ </svg>
61
+ </button>
62
+ <button
63
+ type="button"
64
+ class="ew-lightbox__arrow ew-lightbox__arrow--prev"
65
+ aria-label="Previous image"
66
+ >
67
+ <svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
68
+ <polyline points="15 18 9 12 15 6"></polyline>
69
+ </svg>
70
+ </button>
71
+ <button
72
+ type="button"
73
+ class="ew-lightbox__arrow ew-lightbox__arrow--next"
74
+ aria-label="Next image"
75
+ >
76
+ <svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
77
+ <polyline points="9 18 15 12 9 6"></polyline>
78
+ </svg>
79
+ </button>
80
+ <figure class="ew-lightbox__figure">
81
+ <img class="ew-lightbox__img" src="" alt="" />
82
+ <figcaption class="ew-lightbox__caption">
83
+ <h3 class="ew-lightbox__caption-title"></h3>
84
+ <p class="ew-lightbox__caption-description"></p>
85
+ </figcaption>
86
+ </figure>
87
+ </div>
88
+ <script type="application/json" data-lightbox-items set:html={JSON.stringify(items)} />
89
+ </div>
90
+ </section>
91
+ )}
92
+
93
+ <script>
94
+ type LightboxItem = {
95
+ src: string;
96
+ alt: string;
97
+ title?: string;
98
+ description?: string;
99
+ };
100
+
101
+ const initLightbox = (root: HTMLElement) => {
102
+ const dataNode = root.querySelector<HTMLScriptElement>('[data-lightbox-items]');
103
+ const modal = root.querySelector<HTMLElement>('.ew-lightbox');
104
+ const img = modal?.querySelector<HTMLImageElement>('.ew-lightbox__img');
105
+ const captionTitle = modal?.querySelector<HTMLElement>('.ew-lightbox__caption-title');
106
+ const captionDesc = modal?.querySelector<HTMLElement>('.ew-lightbox__caption-description');
107
+ const closeBtn = modal?.querySelector<HTMLButtonElement>('.ew-lightbox__close');
108
+ const prevBtn = modal?.querySelector<HTMLButtonElement>('.ew-lightbox__arrow--prev');
109
+ const nextBtn = modal?.querySelector<HTMLButtonElement>('.ew-lightbox__arrow--next');
110
+ const triggers = Array.from(root.querySelectorAll<HTMLButtonElement>('.ew-lightbox-grid__item'));
111
+
112
+ if (!dataNode || !modal || !img || !captionTitle || !captionDesc || !closeBtn || triggers.length === 0) return;
113
+
114
+ let items: LightboxItem[] = [];
115
+ try {
116
+ items = JSON.parse(dataNode.textContent || '[]');
117
+ } catch {
118
+ return;
119
+ }
120
+
121
+ let current = 0;
122
+ let lastTrigger: HTMLElement | null = null;
123
+
124
+ const render = (idx: number) => {
125
+ const next = ((idx % items.length) + items.length) % items.length;
126
+ current = next;
127
+ const item = items[current];
128
+ img.src = item.src;
129
+ img.alt = item.alt;
130
+ captionTitle.textContent = item.title ?? '';
131
+ captionTitle.hidden = !item.title;
132
+ captionDesc.textContent = item.description ?? '';
133
+ captionDesc.hidden = !item.description;
134
+ };
135
+
136
+ const open = (idx: number) => {
137
+ lastTrigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
138
+ render(idx);
139
+ modal.hidden = false;
140
+ modal.dataset.open = 'true';
141
+ document.body.style.overflow = 'hidden';
142
+ closeBtn.focus();
143
+ };
144
+
145
+ const close = () => {
146
+ modal.dataset.open = 'false';
147
+ modal.hidden = true;
148
+ document.body.style.overflow = '';
149
+ lastTrigger?.focus();
150
+ };
151
+
152
+ triggers.forEach((trigger, i) => {
153
+ trigger.addEventListener('click', () => open(i));
154
+ });
155
+
156
+ closeBtn.addEventListener('click', close);
157
+ prevBtn?.addEventListener('click', () => render(current - 1));
158
+ nextBtn?.addEventListener('click', () => render(current + 1));
159
+
160
+ modal.addEventListener('click', (e) => {
161
+ if (e.target === modal) close();
162
+ });
163
+
164
+ document.addEventListener('keydown', (e: KeyboardEvent) => {
165
+ if (modal.dataset.open !== 'true') return;
166
+ if (e.key === 'Escape') { e.preventDefault(); close(); }
167
+ else if (e.key === 'ArrowLeft') { e.preventDefault(); render(current - 1); }
168
+ else if (e.key === 'ArrowRight') { e.preventDefault(); render(current + 1); }
169
+ });
170
+ };
171
+
172
+ document.querySelectorAll<HTMLElement>('[data-lightbox-root]').forEach(initLightbox);
173
+ </script>
174
+
175
+ <style>
176
+ .ew-lightbox-wrap {
177
+ padding: var(--ew-space-8) var(--ew-space-5);
178
+ }
179
+ .ew-lightbox-wrap__inner {
180
+ max-width: 72rem;
181
+ margin: 0 auto;
182
+ }
183
+ .ew-lightbox-wrap__title {
184
+ font-family: var(--ew-font-sans);
185
+ font-size: var(--ew-text-xl);
186
+ font-weight: 700;
187
+ color: var(--ew-on-surface);
188
+ margin: 0 0 var(--ew-space-5);
189
+ line-height: var(--ew-leading-tight);
190
+ }
191
+
192
+ .ew-lightbox-grid {
193
+ display: grid;
194
+ gap: var(--ew-space-4);
195
+ }
196
+ .ew-lightbox-grid--cols-2 { grid-template-columns: repeat(2, 1fr); }
197
+ .ew-lightbox-grid--cols-3 { grid-template-columns: repeat(3, 1fr); }
198
+ .ew-lightbox-grid--cols-4 { grid-template-columns: repeat(4, 1fr); }
199
+
200
+ .ew-lightbox-grid--gap-sm { gap: var(--ew-space-2); }
201
+ .ew-lightbox-grid--gap-md { gap: var(--ew-space-4); }
202
+ .ew-lightbox-grid--gap-lg { gap: var(--ew-space-6); }
203
+
204
+ @media (max-width: 768px) {
205
+ .ew-lightbox-grid--cols-3,
206
+ .ew-lightbox-grid--cols-4 { grid-template-columns: repeat(2, 1fr); }
207
+ }
208
+ @media (max-width: 480px) {
209
+ .ew-lightbox-grid--cols-2,
210
+ .ew-lightbox-grid--cols-3,
211
+ .ew-lightbox-grid--cols-4 { grid-template-columns: 1fr; }
212
+ }
213
+
214
+ .ew-lightbox-grid__item {
215
+ padding: 0;
216
+ border: none;
217
+ background: none;
218
+ cursor: pointer;
219
+ display: block;
220
+ border-radius: var(--ew-radius-md);
221
+ overflow: hidden;
222
+ transition: transform 0.15s ease;
223
+ }
224
+ .ew-lightbox-grid__item:hover,
225
+ .ew-lightbox-grid__item:focus-visible {
226
+ transform: scale(1.02);
227
+ }
228
+ .ew-lightbox-grid__item:focus-visible {
229
+ outline: 2px solid var(--ew-primary);
230
+ outline-offset: 2px;
231
+ }
232
+
233
+ .ew-lightbox-grid__img {
234
+ width: 100%;
235
+ height: auto;
236
+ object-fit: cover;
237
+ display: block;
238
+ }
239
+ .ew-lightbox-grid__img--ratio-square { aspect-ratio: 1 / 1; }
240
+ .ew-lightbox-grid__img--ratio-4-3 { aspect-ratio: 4 / 3; }
241
+ .ew-lightbox-grid__img--ratio-auto { aspect-ratio: auto; }
242
+
243
+ .ew-lightbox {
244
+ position: fixed;
245
+ inset: 0;
246
+ background: rgba(0, 0, 0, 0.92);
247
+ display: flex;
248
+ flex-direction: column;
249
+ align-items: center;
250
+ justify-content: center;
251
+ z-index: 1000;
252
+ padding: var(--ew-space-5);
253
+ }
254
+ .ew-lightbox[data-open='false'] {
255
+ display: none;
256
+ }
257
+
258
+ .ew-lightbox__figure {
259
+ margin: 0;
260
+ max-width: min(90vw, 60rem);
261
+ max-height: 80vh;
262
+ display: flex;
263
+ flex-direction: column;
264
+ align-items: center;
265
+ gap: var(--ew-space-3);
266
+ }
267
+
268
+ .ew-lightbox__img {
269
+ max-width: 100%;
270
+ max-height: 70vh;
271
+ object-fit: contain;
272
+ border-radius: var(--ew-radius-md);
273
+ }
274
+
275
+ .ew-lightbox__caption {
276
+ color: #fff;
277
+ text-align: center;
278
+ font-family: var(--ew-font-sans);
279
+ max-width: 40rem;
280
+ }
281
+ .ew-lightbox__caption-title {
282
+ font-size: var(--ew-text-lg);
283
+ font-weight: 600;
284
+ margin: 0 0 var(--ew-space-2);
285
+ }
286
+ .ew-lightbox__caption-description {
287
+ font-size: var(--ew-text-sm);
288
+ margin: 0;
289
+ opacity: 0.85;
290
+ line-height: var(--ew-leading-normal);
291
+ }
292
+
293
+ .ew-lightbox__close {
294
+ position: absolute;
295
+ top: var(--ew-space-4);
296
+ right: var(--ew-space-4);
297
+ width: 2.75rem;
298
+ height: 2.75rem;
299
+ border-radius: var(--ew-radius-full);
300
+ border: none;
301
+ background: rgba(255, 255, 255, 0.15);
302
+ color: #fff;
303
+ cursor: pointer;
304
+ display: inline-flex;
305
+ align-items: center;
306
+ justify-content: center;
307
+ transition: background 0.15s ease;
308
+ }
309
+ .ew-lightbox__close:hover,
310
+ .ew-lightbox__close:focus-visible {
311
+ background: rgba(255, 255, 255, 0.3);
312
+ }
313
+ .ew-lightbox__close:focus-visible {
314
+ outline: 2px solid #fff;
315
+ outline-offset: 2px;
316
+ }
317
+
318
+ .ew-lightbox__arrow {
319
+ position: absolute;
320
+ top: 50%;
321
+ transform: translateY(-50%);
322
+ width: 3rem;
323
+ height: 3rem;
324
+ border-radius: var(--ew-radius-full);
325
+ border: none;
326
+ background: rgba(255, 255, 255, 0.15);
327
+ color: #fff;
328
+ cursor: pointer;
329
+ display: inline-flex;
330
+ align-items: center;
331
+ justify-content: center;
332
+ transition: background 0.15s ease;
333
+ }
334
+ .ew-lightbox__arrow:hover,
335
+ .ew-lightbox__arrow:focus-visible {
336
+ background: rgba(255, 255, 255, 0.3);
337
+ }
338
+ .ew-lightbox__arrow:focus-visible {
339
+ outline: 2px solid #fff;
340
+ outline-offset: 2px;
341
+ }
342
+ .ew-lightbox__arrow--prev { left: var(--ew-space-4); }
343
+ .ew-lightbox__arrow--next { right: var(--ew-space-4); }
344
+
345
+ @media (max-width: 480px) {
346
+ .ew-lightbox__arrow {
347
+ width: 2.5rem;
348
+ height: 2.5rem;
349
+ }
350
+ .ew-lightbox__arrow--prev { left: var(--ew-space-2); }
351
+ .ew-lightbox__arrow--next { right: var(--ew-space-2); }
352
+ }
353
+ </style>
@@ -0,0 +1,127 @@
1
+ ---
2
+ import type { GalleryMasonryGridData } from '../schemas/galleries';
3
+
4
+ type Props = Omit<GalleryMasonryGridData, 'kind' | 'locale' | 'translationKey'>;
5
+
6
+ const {
7
+ title,
8
+ columns = 3,
9
+ gap = 'md',
10
+ items,
11
+ } = Astro.props;
12
+
13
+ const sectionClass = [
14
+ 'ew-masonry',
15
+ `ew-masonry--cols-${columns}`,
16
+ `ew-masonry--gap-${gap}`,
17
+ ].join(' ');
18
+ ---
19
+
20
+ {items.length > 0 && (
21
+ <section class="ew-masonry-wrap">
22
+ <div class="ew-masonry-wrap__inner">
23
+ {title && <h2 class="ew-masonry-wrap__title">{title}</h2>}
24
+ <div class={sectionClass}>
25
+ {items.map((item) => {
26
+ const figure = (
27
+ <figure class="ew-masonry__item">
28
+ <img
29
+ src={item.src}
30
+ alt={item.alt}
31
+ class="ew-masonry__img"
32
+ loading="lazy"
33
+ />
34
+ {item.caption && (
35
+ <figcaption class="ew-masonry__caption">{item.caption}</figcaption>
36
+ )}
37
+ </figure>
38
+ );
39
+ return item.href ? (
40
+ <a href={item.href} class="ew-masonry__link" aria-label={item.alt}>
41
+ {figure}
42
+ </a>
43
+ ) : figure;
44
+ })}
45
+ </div>
46
+ </div>
47
+ </section>
48
+ )}
49
+
50
+ <style>
51
+ .ew-masonry-wrap {
52
+ padding: var(--ew-space-8) var(--ew-space-5);
53
+ }
54
+ .ew-masonry-wrap__inner {
55
+ max-width: 72rem;
56
+ margin: 0 auto;
57
+ }
58
+ .ew-masonry-wrap__title {
59
+ font-family: var(--ew-font-sans);
60
+ font-size: var(--ew-text-xl);
61
+ font-weight: 700;
62
+ color: var(--ew-on-surface);
63
+ margin: 0 0 var(--ew-space-5);
64
+ line-height: var(--ew-leading-tight);
65
+ }
66
+
67
+ .ew-masonry {
68
+ column-fill: balance;
69
+ }
70
+ .ew-masonry--cols-2 { column-count: 2; }
71
+ .ew-masonry--cols-3 { column-count: 3; }
72
+ .ew-masonry--cols-4 { column-count: 4; }
73
+
74
+ .ew-masonry--gap-sm { column-gap: var(--ew-space-2); }
75
+ .ew-masonry--gap-md { column-gap: var(--ew-space-4); }
76
+ .ew-masonry--gap-lg { column-gap: var(--ew-space-6); }
77
+
78
+ @media (max-width: 768px) {
79
+ .ew-masonry--cols-3,
80
+ .ew-masonry--cols-4 { column-count: 2; }
81
+ }
82
+ @media (max-width: 480px) {
83
+ .ew-masonry--cols-2,
84
+ .ew-masonry--cols-3,
85
+ .ew-masonry--cols-4 { column-count: 1; }
86
+ }
87
+
88
+ .ew-masonry__item {
89
+ margin: 0 0 var(--ew-space-4);
90
+ break-inside: avoid;
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: var(--ew-space-2);
94
+ }
95
+ .ew-masonry--gap-sm .ew-masonry__item { margin-bottom: var(--ew-space-2); }
96
+ .ew-masonry--gap-lg .ew-masonry__item { margin-bottom: var(--ew-space-6); }
97
+
98
+ .ew-masonry__img {
99
+ width: 100%;
100
+ height: auto;
101
+ display: block;
102
+ border-radius: var(--ew-radius-md);
103
+ transition: transform 0.2s ease;
104
+ }
105
+
106
+ .ew-masonry__caption {
107
+ font-family: var(--ew-font-sans);
108
+ font-size: var(--ew-text-sm);
109
+ color: var(--ew-muted);
110
+ line-height: var(--ew-leading-normal);
111
+ }
112
+
113
+ .ew-masonry__link {
114
+ text-decoration: none;
115
+ color: inherit;
116
+ display: block;
117
+ }
118
+ .ew-masonry__link:hover .ew-masonry__img,
119
+ .ew-masonry__link:focus-visible .ew-masonry__img {
120
+ transform: scale(1.02);
121
+ }
122
+ .ew-masonry__link:focus-visible {
123
+ outline: 2px solid var(--ew-primary);
124
+ outline-offset: 2px;
125
+ border-radius: var(--ew-radius-md);
126
+ }
127
+ </style>
@@ -0,0 +1,69 @@
1
+ ---
2
+ import type { GalleryEntry } from '../schemas/galleries';
3
+ import GalleryImageGrid from './GalleryImageGrid.astro';
4
+ import GalleryHeroSlider from './GalleryHeroSlider.astro';
5
+ import GalleryCarousel from './GalleryCarousel.astro';
6
+ import GalleryMasonryGrid from './GalleryMasonryGrid.astro';
7
+ import GalleryFeatureHighlight from './GalleryFeatureHighlight.astro';
8
+ import GalleryLightboxGrid from './GalleryLightboxGrid.astro';
9
+
10
+ interface Props {
11
+ entry: { data: GalleryEntry } | undefined | null;
12
+ }
13
+
14
+ const { entry } = Astro.props;
15
+ const data = entry?.data;
16
+ ---
17
+
18
+ {data?.kind === 'image-grid' && (
19
+ <GalleryImageGrid
20
+ title={data.title}
21
+ columns={data.columns}
22
+ gap={data.gap}
23
+ aspectRatio={data.aspectRatio}
24
+ items={data.items}
25
+ />
26
+ )}
27
+ {data?.kind === 'hero-slider' && (
28
+ <GalleryHeroSlider
29
+ title={data.title}
30
+ autoplay={data.autoplay}
31
+ interval={data.interval}
32
+ height={data.height}
33
+ slides={data.slides}
34
+ />
35
+ )}
36
+ {data?.kind === 'carousel' && (
37
+ <GalleryCarousel
38
+ title={data.title}
39
+ autoplay={data.autoplay}
40
+ interval={data.interval}
41
+ showDots={data.showDots}
42
+ showArrows={data.showArrows}
43
+ slides={data.slides}
44
+ />
45
+ )}
46
+ {data?.kind === 'masonry-grid' && (
47
+ <GalleryMasonryGrid
48
+ title={data.title}
49
+ columns={data.columns}
50
+ gap={data.gap}
51
+ items={data.items}
52
+ />
53
+ )}
54
+ {data?.kind === 'feature-highlight' && (
55
+ <GalleryFeatureHighlight
56
+ title={data.title}
57
+ gap={data.gap}
58
+ items={data.items}
59
+ />
60
+ )}
61
+ {data?.kind === 'lightbox-grid' && (
62
+ <GalleryLightboxGrid
63
+ title={data.title}
64
+ columns={data.columns}
65
+ gap={data.gap}
66
+ aspectRatio={data.aspectRatio}
67
+ items={data.items}
68
+ />
69
+ )}