@yatoday/astro-ui 0.17.2 → 0.17.5
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/components/ImageGalleryIkea/ImageGalleryIkea.astro +28 -0
- package/components/ImageGalleryIkea/ImageGalleryIkea.svelte +28 -0
- package/components/WidgetSwiperPhotoSlider/WidgetSwiperPhotoSlider.astro +16 -2
- package/components/WidgetSwiperPhotoSlider/types.ts +1 -0
- package/components/ZoomedImage/ZoomedImage.astro +7 -1
- package/components/ZoomedImage/ZoomedImage.svelte +7 -1
- package/package.json +1 -1
- package/styles/styles.css +4 -0
- package/components/Button/__tests__/Button.test.ts +0 -28
|
@@ -22,9 +22,34 @@ const { container: containerClass = '', swiper: swiperClass = '', swiperThumb: s
|
|
|
22
22
|
opacity: 1;
|
|
23
23
|
border: 1px solid #666;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/* Hide slides before Swiper initializes to prevent FOUC */
|
|
27
|
+
[data-image-gallery-ikea]:not([data-initialized]) swiper-slide {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
[data-image-gallery-ikea]:not([data-initialized]) swiper-slide:first-child {
|
|
31
|
+
display: block;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Hide loader after initialization */
|
|
35
|
+
[data-image-gallery-ikea][data-initialized] .gallery-loader {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Loading spinner animation */
|
|
40
|
+
@keyframes gallery-spin {
|
|
41
|
+
to { transform: rotate(360deg); }
|
|
42
|
+
}
|
|
43
|
+
.gallery-spinner {
|
|
44
|
+
animation: gallery-spin 1s linear infinite;
|
|
45
|
+
}
|
|
25
46
|
</style>
|
|
26
47
|
|
|
27
48
|
<div class={cn('@container relative', containerClass)} data-image-gallery-ikea={id}>
|
|
49
|
+
<!-- Loading skeleton -->
|
|
50
|
+
<div class="gallery-loader absolute inset-0 z-30 flex items-center justify-center bg-muted/50 rounded-lg">
|
|
51
|
+
<div class="gallery-spinner size-10 border-4 border-muted-foreground/20 border-t-primary rounded-full"></div>
|
|
52
|
+
</div>
|
|
28
53
|
<div class="absolute left-0 top-0 z-20 hidden md:block group w-20">
|
|
29
54
|
<!-- Thumb Slider -->
|
|
30
55
|
<button
|
|
@@ -204,6 +229,9 @@ const { container: containerClass = '', swiper: swiperClass = '', swiperThumb: s
|
|
|
204
229
|
Object.assign(mainSwiperEl, {});
|
|
205
230
|
|
|
206
231
|
(mainSwiperEl as any)?.initialize();
|
|
232
|
+
|
|
233
|
+
// Mark as initialized to show content and hide loader
|
|
234
|
+
elem.setAttribute('data-initialized', 'true');
|
|
207
235
|
});
|
|
208
236
|
};
|
|
209
237
|
|
|
@@ -70,6 +70,9 @@
|
|
|
70
70
|
|
|
71
71
|
Object.assign(mainSwiperEl, {});
|
|
72
72
|
(mainSwiperEl as any)?.initialize();
|
|
73
|
+
|
|
74
|
+
// Mark as initialized to show content and hide loader
|
|
75
|
+
elem.setAttribute('data-initialized', 'true');
|
|
73
76
|
};
|
|
74
77
|
|
|
75
78
|
init();
|
|
@@ -82,9 +85,34 @@
|
|
|
82
85
|
opacity: 1;
|
|
83
86
|
border: 1px solid #666;
|
|
84
87
|
}
|
|
88
|
+
|
|
89
|
+
/* Hide slides before Swiper initializes to prevent FOUC */
|
|
90
|
+
:global([data-image-gallery-ikea]:not([data-initialized]) swiper-slide) {
|
|
91
|
+
display: none;
|
|
92
|
+
}
|
|
93
|
+
:global([data-image-gallery-ikea]:not([data-initialized]) swiper-slide:first-child) {
|
|
94
|
+
display: block;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Hide loader after initialization */
|
|
98
|
+
:global([data-image-gallery-ikea][data-initialized] .gallery-loader) {
|
|
99
|
+
display: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Loading spinner animation */
|
|
103
|
+
@keyframes gallery-spin {
|
|
104
|
+
to { transform: rotate(360deg); }
|
|
105
|
+
}
|
|
106
|
+
.gallery-spinner {
|
|
107
|
+
animation: gallery-spin 1s linear infinite;
|
|
108
|
+
}
|
|
85
109
|
</style>
|
|
86
110
|
|
|
87
111
|
<div bind:this={ref} class={cn('@container relative', containerClass)} data-image-gallery-ikea={id} {...rest}>
|
|
112
|
+
<!-- Loading skeleton -->
|
|
113
|
+
<div class="gallery-loader absolute inset-0 z-30 flex items-center justify-center bg-muted/50 rounded-lg">
|
|
114
|
+
<div class="gallery-spinner size-10 border-4 border-muted-foreground/20 border-t-primary rounded-full"></div>
|
|
115
|
+
</div>
|
|
88
116
|
<div class="absolute left-0 top-0 z-20 hidden md:block group w-20">
|
|
89
117
|
<!-- Thumb Slider -->
|
|
90
118
|
<button
|
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
subtitle = await Astro.slots.render('subtitle'),
|
|
13
13
|
tagline = await Astro.slots.render('tagline'),
|
|
14
14
|
imagesFolder,
|
|
15
|
+
alt,
|
|
15
16
|
position = 'center',
|
|
16
17
|
asHeader = 'h2',
|
|
17
18
|
asSubtitle = 'p',
|
|
@@ -51,13 +52,24 @@ const imagePaths = Object.keys(images).filter((imagePath) => {
|
|
|
51
52
|
{
|
|
52
53
|
imagePaths && (
|
|
53
54
|
<SwiperSlider id={id} withNavigation={withNavigation} {...rest}>
|
|
54
|
-
{imagePaths.map(async (imagePath) => {
|
|
55
|
+
{imagePaths.map(async (imagePath, index) => {
|
|
55
56
|
let image = images[imagePath]();
|
|
56
57
|
let optimizedImage = await getImage({
|
|
57
58
|
src: image,
|
|
58
59
|
width: 860,
|
|
59
60
|
});
|
|
60
61
|
|
|
62
|
+
// Generate alt text: alt prop > title-based > filename > default
|
|
63
|
+
const filename = imagePath.split('/').pop()?.replace(/\.[^/.]+$/, '') || '';
|
|
64
|
+
const imageNumber = index + 1;
|
|
65
|
+
const imageName = filename || 'Image ' + imageNumber;
|
|
66
|
+
const baseTitle = title ? String(title).replace(/<[^>]*>/g, '').trim() : '';
|
|
67
|
+
const altText = alt
|
|
68
|
+
? alt + ' - ' + imageName
|
|
69
|
+
: baseTitle
|
|
70
|
+
? baseTitle + ' - ' + imageName
|
|
71
|
+
: imageName;
|
|
72
|
+
|
|
61
73
|
return (
|
|
62
74
|
<swiper-slide>
|
|
63
75
|
<a
|
|
@@ -65,11 +77,13 @@ const imagePaths = Object.keys(images).filter((imagePath) => {
|
|
|
65
77
|
data-pswp-width={optimizedImage.attributes.width}
|
|
66
78
|
data-pswp-height={optimizedImage.attributes.height}
|
|
67
79
|
target="_blank"
|
|
80
|
+
title={altText}
|
|
68
81
|
class="group overflow-hidden rounded-md border-primary cursor-zoom-in block aspect-square"
|
|
69
82
|
>
|
|
70
83
|
<AstroImage
|
|
71
84
|
src={image}
|
|
72
|
-
alt={
|
|
85
|
+
alt={altText}
|
|
86
|
+
title={altText}
|
|
73
87
|
widths={[400, 900]}
|
|
74
88
|
width={400}
|
|
75
89
|
sizes="(max-width: 900px) 400px, 900px"
|
|
@@ -61,8 +61,8 @@ if (
|
|
|
61
61
|
type="button"
|
|
62
62
|
class={cn('zoom-bg block w-full h-full cursor-zoom-in')}
|
|
63
63
|
tabindex="0"
|
|
64
|
-
style={`background-image: url('${image.src}')`}
|
|
65
64
|
data-yt-zoom-button
|
|
65
|
+
data-yt-zoom-src={image.src}
|
|
66
66
|
>
|
|
67
67
|
<span>
|
|
68
68
|
{!image ? (
|
|
@@ -170,6 +170,7 @@ if (
|
|
|
170
170
|
span.classList.remove('zoom-wrap');
|
|
171
171
|
zoomButton.classList.remove('cursor-zoom-out', 'bg-zoomed');
|
|
172
172
|
zoomButton.classList.add('cursor-zoom-in');
|
|
173
|
+
zoomButton.style.backgroundImage = 'none';
|
|
173
174
|
zoomButton.style.backgroundSize = '100%';
|
|
174
175
|
zoomButton.style.backgroundPosition = '0% 0%';
|
|
175
176
|
|
|
@@ -194,6 +195,11 @@ if (
|
|
|
194
195
|
zoomed = !zoomed;
|
|
195
196
|
|
|
196
197
|
if (zoomed) {
|
|
198
|
+
// Set background-image only when zooming in
|
|
199
|
+
const zoomSrc = zoomButton.dataset.ytZoomSrc;
|
|
200
|
+
if (zoomSrc) {
|
|
201
|
+
zoomButton.style.backgroundImage = `url('${zoomSrc}')`;
|
|
202
|
+
}
|
|
197
203
|
image.classList.add('hidden');
|
|
198
204
|
span.classList.add('zoom-wrap');
|
|
199
205
|
zoomButton.classList.add('cursor-zoom-out', 'bg-zoomed');
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
if (zoomButtonRef) {
|
|
65
65
|
zoomButtonRef.classList.remove('cursor-zoom-out', 'bg-zoomed');
|
|
66
66
|
zoomButtonRef.classList.add('cursor-zoom-in');
|
|
67
|
+
zoomButtonRef.style.backgroundImage = 'none';
|
|
67
68
|
zoomButtonRef.style.backgroundSize = '100%';
|
|
68
69
|
zoomButtonRef.style.backgroundPosition = '0% 0%';
|
|
69
70
|
}
|
|
@@ -87,6 +88,11 @@
|
|
|
87
88
|
if (imageRef) imageRef.classList.add('hidden');
|
|
88
89
|
if (spanRef) spanRef.classList.add('zoom-wrap');
|
|
89
90
|
if (zoomButtonRef) {
|
|
91
|
+
// Set background-image only when zooming in
|
|
92
|
+
const zoomSrc = zoomButtonRef.dataset.ytZoomSrc;
|
|
93
|
+
if (zoomSrc) {
|
|
94
|
+
zoomButtonRef.style.backgroundImage = `url('${zoomSrc}')`;
|
|
95
|
+
}
|
|
90
96
|
zoomButtonRef.classList.add('cursor-zoom-out', 'bg-zoomed');
|
|
91
97
|
zoomButtonRef.classList.remove('cursor-zoom-in');
|
|
92
98
|
zoomButtonRef.style.backgroundSize = '200%';
|
|
@@ -139,8 +145,8 @@
|
|
|
139
145
|
type="button"
|
|
140
146
|
class={cn('zoom-bg block w-full h-full cursor-zoom-in')}
|
|
141
147
|
tabindex="0"
|
|
142
|
-
style={`background-image: url('${src}')`}
|
|
143
148
|
data-yt-zoom-button
|
|
149
|
+
data-yt-zoom-src={src}
|
|
144
150
|
>
|
|
145
151
|
<span bind:this={spanRef}>
|
|
146
152
|
<img
|
package/package.json
CHANGED
package/styles/styles.css
CHANGED
|
@@ -1055,6 +1055,10 @@
|
|
|
1055
1055
|
border-style: var(--tw-border-style);
|
|
1056
1056
|
border-width: 2px;
|
|
1057
1057
|
}
|
|
1058
|
+
.border-4 {
|
|
1059
|
+
border-style: var(--tw-border-style);
|
|
1060
|
+
border-width: 4px;
|
|
1061
|
+
}
|
|
1058
1062
|
.border-t {
|
|
1059
1063
|
border-top-style: var(--tw-border-style);
|
|
1060
1064
|
border-top-width: 1px;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { themeVariants } from '../theme-variants';
|
|
3
|
-
|
|
4
|
-
describe('Button Theme Variants', () => {
|
|
5
|
-
it('has the correct default variant', () => {
|
|
6
|
-
const classes = themeVariants({ variant: 'default' });
|
|
7
|
-
expect(classes).toContain('bg-secondary');
|
|
8
|
-
expect(classes).toContain('text-secondary-foreground');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('has the correct primary variant', () => {
|
|
12
|
-
const classes = themeVariants({ variant: 'primary' });
|
|
13
|
-
expect(classes).toContain('bg-primary');
|
|
14
|
-
expect(classes).toContain('text-primary-foreground');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('has the correct size classes for default size', () => {
|
|
18
|
-
const classes = themeVariants({ size: 'default' });
|
|
19
|
-
expect(classes).toContain('px-6');
|
|
20
|
-
expect(classes).toContain('h-10');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('has the correct size classes for small size', () => {
|
|
24
|
-
const classes = themeVariants({ size: 'sm' });
|
|
25
|
-
expect(classes).toContain('h-8');
|
|
26
|
-
expect(classes).toContain('px-4');
|
|
27
|
-
});
|
|
28
|
-
});
|