@yatoday/astro-ui 0.18.1 → 0.18.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.
- package/components/Analytics/AnalyticsGTM.astro +3 -3
- package/components/Analytics/AnalyticsGTMBody.astro +16 -12
- package/components/Analytics/AnalyticsGoogle.astro +3 -1
- package/components/BreadcrumbsItem/BreadcrumbsItem.astro +8 -1
- package/components/Button/types.ts +2 -1
- package/components/CallToAction/CallToAction.astro +1 -1
- package/components/Card0/Card0.astro +20 -32
- package/components/Card1/Card1.astro +11 -19
- package/components/Card2/Card2.astro +10 -14
- package/components/Card3/Card3.astro +15 -20
- package/components/Card4/Card4.astro +6 -2
- package/components/Card5/Card5.astro +8 -1
- package/components/Card6/Card6.astro +76 -59
- package/components/Card7/Card7.astro +27 -18
- package/components/CookieConsent/CookieConsent.astro +323 -308
- package/components/CopyToClipboard/CopyToClipboard.astro +6 -14
- package/components/CopyToClipboard/copy.ts +1 -1
- package/components/DarkMode/__tests__/DarkMode.test.ts +8 -8
- package/components/Headline/Headline.astro +4 -1
- package/components/HeroSection/HeroSection.astro +13 -16
- package/components/Image/Image.astro +1 -7
- package/components/ImageGallery/ImageGallery.astro +85 -90
- package/components/ImageGalleryIkea/ImageGalleryIkea.astro +3 -2
- package/components/ImagePreload/ImagePreload.astro +22 -20
- package/components/ItemGrid1/ItemGrid1.astro +34 -24
- package/components/Metadata/Metadata.astro +1 -5
- package/components/PointMap/PointMap.astro +62 -48
- package/components/PointMap/types.ts +3 -3
- package/components/SEO/SEO.astro +34 -33
- package/components/WidgetBrands/WidgetBrands.astro +8 -1
- package/components/WidgetContent/WidgetContent.astro +22 -9
- package/components/WidgetContent/types.ts +1 -1
- package/components/WidgetFeaturesCard/WidgetFeaturesCard.astro +10 -2
- package/components/WidgetHeroSlider/WidgetHeroSlider.astro +6 -1
- package/components/WidgetNavbarFlyout/WidgetNavbarFlyout.astro +29 -9
- package/components/WidgetSwiperPhotoSlider/WidgetSwiperPhotoSlider.astro +18 -7
- package/components/WidgetTestimonials/WidgetTestimonials.astro +8 -1
- package/components/WidgetWrapper/WidgetWrapper.astro +11 -2
- package/index.d.ts +20 -20
- package/package.json +11 -9
- package/styles/styles.css +303 -160
- package/utils/i18n.ts +2 -2
- package/utils/images.ts +0 -1
- package/utils/slugify.ts +3 -0
- package/vendor-config/config.example.yaml +0 -1
- package/vendor-config/index.ts +1 -1
- package/vendor-config/utils/loadConfig.ts +15 -18
|
@@ -1,56 +1,60 @@
|
|
|
1
1
|
---
|
|
2
|
-
import type {PointMapProps as Props} from './types';
|
|
3
|
-
import {cn, id} from
|
|
2
|
+
import type { PointMapProps as Props } from './types';
|
|
3
|
+
import { cn, id } from '../../utils';
|
|
4
4
|
import Card7 from '../Card7/Card7.astro';
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
7
|
via = Card7,
|
|
8
8
|
items = [],
|
|
9
9
|
pointsDisplayMode = 'always', // default to always visible
|
|
10
|
-
classes = {}
|
|
10
|
+
classes = {},
|
|
11
11
|
} = Astro.props;
|
|
12
12
|
|
|
13
13
|
const mapId = `map-${id()}`;
|
|
14
14
|
|
|
15
|
-
const {
|
|
16
|
-
container: containerClass = '',
|
|
17
|
-
popup: popupClass = '',
|
|
18
|
-
} = classes;
|
|
15
|
+
const { container: containerClass = '', popup: popupClass = '' } = classes;
|
|
19
16
|
|
|
20
17
|
const Component = via;
|
|
21
18
|
---
|
|
22
19
|
|
|
23
20
|
<div id={mapId} class={cn(`map-picker relative h-full w-full points-${pointsDisplayMode}`, containerClass)}>
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</button>
|
|
36
|
-
|
|
37
|
-
<!-- Popup content (hidden by default) -->
|
|
38
|
-
<div
|
|
39
|
-
class={cn("popup-content absolute rounded-md shadow-lg z-10 max-w-xs hidden", popupClass)}
|
|
40
|
-
style={`left: ${point.x}%; top: ${point.y}%; transform: translate(${point.x > 50 ? '-100%' : '0'}, ${point.y > 50 ? '-100%' : '0'});`}
|
|
41
|
-
data-popup={i}
|
|
42
|
-
data-map-id={mapId}
|
|
43
|
-
>
|
|
44
|
-
<Component
|
|
45
|
-
{...point}
|
|
46
|
-
classes={Object.assign({container: 'border-transparent dark:border-input'}, classes?.card || {}, point?.classes || {}) as Record<string, string>}
|
|
21
|
+
{
|
|
22
|
+
items.map((point, i) => (
|
|
23
|
+
<>
|
|
24
|
+
{/* Hotspot point */}
|
|
25
|
+
<button
|
|
26
|
+
class="hotspot-point cursor-pointer"
|
|
27
|
+
style={`left: ${point.x}%; top: ${point.y}%; width: 24px; height: 24px; transform: translate(-50%, -50%);`}
|
|
28
|
+
data-index={i}
|
|
29
|
+
data-map-id={mapId}
|
|
30
|
+
aria-label={point.title}
|
|
31
|
+
{...(point.isPopupOpen ? { 'data-initially-open': '' } : {})}
|
|
47
32
|
/>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
33
|
+
|
|
34
|
+
{/* Popup content (hidden by default) */}
|
|
35
|
+
<div
|
|
36
|
+
class={cn('popup-content absolute rounded-md shadow-lg z-10 max-w-xs hidden', popupClass)}
|
|
37
|
+
style={`left: ${point.x}%; top: ${point.y}%; transform: translate(${point.x > 50 ? '-100%' : '0'}, ${point.y > 50 ? '-100%' : '0'});`}
|
|
38
|
+
data-popup={i}
|
|
39
|
+
data-map-id={mapId}
|
|
40
|
+
>
|
|
41
|
+
<Component
|
|
42
|
+
{...point}
|
|
43
|
+
classes={
|
|
44
|
+
Object.assign(
|
|
45
|
+
{ container: 'border-transparent dark:border-input' },
|
|
46
|
+
classes?.card || {},
|
|
47
|
+
point?.classes || {}
|
|
48
|
+
) as Record<string, string>
|
|
49
|
+
}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</>
|
|
53
|
+
))
|
|
54
|
+
}
|
|
51
55
|
|
|
52
56
|
<div class="w-full h-full">
|
|
53
|
-
<slot/>
|
|
57
|
+
<slot />
|
|
54
58
|
</div>
|
|
55
59
|
</div>
|
|
56
60
|
|
|
@@ -70,7 +74,9 @@ const Component = via;
|
|
|
70
74
|
.points-hover .hotspot-point {
|
|
71
75
|
opacity: 0;
|
|
72
76
|
visibility: hidden;
|
|
73
|
-
transition:
|
|
77
|
+
transition:
|
|
78
|
+
opacity 250ms ease-in-out,
|
|
79
|
+
visibility 250ms ease-in-out;
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
.points-hover:hover .hotspot-point,
|
|
@@ -81,7 +87,10 @@ const Component = via;
|
|
|
81
87
|
|
|
82
88
|
.hotspot-point {
|
|
83
89
|
box-sizing: border-box;
|
|
84
|
-
transition:
|
|
90
|
+
transition:
|
|
91
|
+
border-color 250ms ease-in-out,
|
|
92
|
+
opacity 250ms ease-in-out,
|
|
93
|
+
visibility 250ms ease-in-out;
|
|
85
94
|
transform: translateX(-1rem) translateY(-1rem) scale(0.999);
|
|
86
95
|
padding: 0;
|
|
87
96
|
width: 2rem;
|
|
@@ -91,14 +100,14 @@ const Component = via;
|
|
|
91
100
|
position: absolute;
|
|
92
101
|
background-color: rgba(17, 17, 17, 0.75);
|
|
93
102
|
border-radius: 64px;
|
|
94
|
-
line-height: .5;
|
|
103
|
+
line-height: 0.5;
|
|
95
104
|
display: flex;
|
|
96
105
|
align-items: center;
|
|
97
106
|
justify-content: center;
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
.hotspot-point::before {
|
|
101
|
-
content:
|
|
110
|
+
content: '';
|
|
102
111
|
position: absolute;
|
|
103
112
|
top: -0.5rem;
|
|
104
113
|
left: -0.5rem;
|
|
@@ -108,18 +117,19 @@ const Component = via;
|
|
|
108
117
|
}
|
|
109
118
|
|
|
110
119
|
.hotspot-point::after {
|
|
111
|
-
content:
|
|
120
|
+
content: '';
|
|
112
121
|
position: relative;
|
|
113
122
|
display: block;
|
|
114
123
|
background: rgb(255, 255, 255);
|
|
115
124
|
box-shadow: 0 1px 4px rgba(17, 17, 17, 0.55);
|
|
116
|
-
width: .75rem;
|
|
117
|
-
height: .75rem;
|
|
125
|
+
width: 0.75rem;
|
|
126
|
+
height: 0.75rem;
|
|
118
127
|
border-radius: 64px;
|
|
119
128
|
transition: transform 250ms ease-in-out;
|
|
120
129
|
}
|
|
121
130
|
|
|
122
|
-
.hotspot-point:active,
|
|
131
|
+
.hotspot-point:active,
|
|
132
|
+
.hotspot-point:hover {
|
|
123
133
|
background-color: rgba(17, 17, 17, 0.9);
|
|
124
134
|
}
|
|
125
135
|
|
|
@@ -139,13 +149,15 @@ const Component = via;
|
|
|
139
149
|
const MAP_ID_ATTRIBUTE = 'data-map-id';
|
|
140
150
|
|
|
141
151
|
// Initialize each map component separately
|
|
142
|
-
document.querySelectorAll('.map-picker').forEach(mapContainer => {
|
|
152
|
+
document.querySelectorAll('.map-picker').forEach((mapContainer) => {
|
|
143
153
|
const mapId = mapContainer.id;
|
|
144
154
|
if (!mapId) return;
|
|
145
155
|
|
|
146
156
|
// Get all hotspot points and popups for this specific map
|
|
147
157
|
const hotspotPoints = document.querySelectorAll(`.hotspot-point[${MAP_ID_ATTRIBUTE}="${mapId}"]`);
|
|
148
|
-
const popups = document.querySelectorAll(
|
|
158
|
+
const popups = document.querySelectorAll(
|
|
159
|
+
`.popup-content[${MAP_ID_ATTRIBUTE}="${mapId}"]`
|
|
160
|
+
) as NodeListOf<HTMLElement>;
|
|
149
161
|
|
|
150
162
|
if (!hotspotPoints.length || !popups.length) return;
|
|
151
163
|
|
|
@@ -196,7 +208,9 @@ const Component = via;
|
|
|
196
208
|
return;
|
|
197
209
|
}
|
|
198
210
|
|
|
199
|
-
const point = document.querySelector(
|
|
211
|
+
const point = document.querySelector(
|
|
212
|
+
`.hotspot-point[data-index="${popupIndex}"][${MAP_ID_ATTRIBUTE}="${mapId}"]`
|
|
213
|
+
) as HTMLElement;
|
|
200
214
|
if (!point) return;
|
|
201
215
|
const coordinates = getPointCoordinates(point);
|
|
202
216
|
const translateX = coordinates.x > 50 ? '-100%' : '0';
|
|
@@ -214,7 +228,7 @@ const Component = via;
|
|
|
214
228
|
function getPointCoordinates(pointElement: HTMLElement): PointCoordinates {
|
|
215
229
|
return {
|
|
216
230
|
x: parseFloat(pointElement.style.left) || 0,
|
|
217
|
-
y: parseFloat(pointElement.style.top) || 0
|
|
231
|
+
y: parseFloat(pointElement.style.top) || 0,
|
|
218
232
|
};
|
|
219
233
|
}
|
|
220
234
|
|
|
@@ -258,13 +272,13 @@ const Component = via;
|
|
|
258
272
|
|
|
259
273
|
// Hide all popups (including initially open ones)
|
|
260
274
|
function hideAllPopups() {
|
|
261
|
-
popups.forEach(popup => {
|
|
275
|
+
popups.forEach((popup) => {
|
|
262
276
|
popup.classList.add('hidden');
|
|
263
277
|
});
|
|
264
278
|
}
|
|
265
279
|
|
|
266
280
|
// Add event listeners to hotspot points
|
|
267
|
-
hotspotPoints.forEach(point => {
|
|
281
|
+
hotspotPoints.forEach((point) => {
|
|
268
282
|
const index = getPointIndex(point);
|
|
269
283
|
if (index === null) return;
|
|
270
284
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {HotspotPoint} from
|
|
1
|
+
import type { HotspotPoint } from '../../types';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
|
|
4
4
|
export type PointMapProps = {
|
|
@@ -6,7 +6,7 @@ export type PointMapProps = {
|
|
|
6
6
|
items: HotspotPoint[];
|
|
7
7
|
pointsDisplayMode?: 'always' | 'hover' | undefined; // controls visibility of hotspot points
|
|
8
8
|
classes?: Record<string, string>;
|
|
9
|
-
}
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
export type SveltePointMapProps = {
|
|
12
12
|
via?: any;
|
|
@@ -14,4 +14,4 @@ export type SveltePointMapProps = {
|
|
|
14
14
|
pointsDisplayMode?: 'always' | 'hover' | undefined;
|
|
15
15
|
classes?: Record<string, string>;
|
|
16
16
|
children?: Snippet;
|
|
17
|
-
}
|
|
17
|
+
};
|
package/components/SEO/SEO.astro
CHANGED
|
@@ -65,45 +65,46 @@ const getOGLocale = (loc: string): string => {
|
|
|
65
65
|
};
|
|
66
66
|
---
|
|
67
67
|
|
|
68
|
-
<!-- Primary Meta Tags -->
|
|
69
|
-
{title && <title>{title}</title>}
|
|
68
|
+
<!-- Primary Meta Tags -->{title && <title>{title}</title>}
|
|
70
69
|
{description && <meta name="description" content={description} />}
|
|
71
70
|
{canonical && <link rel="canonical" href={canonical} />}
|
|
72
71
|
<meta name="robots" content={robotsContent} />
|
|
73
72
|
|
|
74
73
|
<!-- Open Graph / Facebook -->
|
|
75
|
-
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
{
|
|
75
|
+
openGraph && (
|
|
76
|
+
<>
|
|
77
|
+
{openGraph.title && <meta property="og:title" content={openGraph.title} />}
|
|
78
|
+
{openGraph.description && <meta property="og:description" content={openGraph.description} />}
|
|
79
|
+
{openGraph.url && <meta property="og:url" content={openGraph.url} />}
|
|
80
|
+
{openGraph.type && <meta property="og:type" content={openGraph.type} />}
|
|
81
|
+
{openGraph.image && (
|
|
82
|
+
<>
|
|
83
|
+
<meta property="og:image" content={openGraph.image.url} />
|
|
84
|
+
{openGraph.image.width && <meta property="og:image:width" content={openGraph.image.width.toString()} />}
|
|
85
|
+
{openGraph.image.height && <meta property="og:image:height" content={openGraph.image.height.toString()} />}
|
|
86
|
+
{openGraph.image.alt && <meta property="og:image:alt" content={openGraph.image.alt} />}
|
|
87
|
+
</>
|
|
88
|
+
)}
|
|
89
|
+
<meta property="og:locale" content={getOGLocale(locale)} />
|
|
90
|
+
{openGraph.siteName && <meta property="og:site_name" content={openGraph.siteName} />}
|
|
91
|
+
</>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
93
94
|
|
|
94
95
|
<!-- Alternate Language Links (hreflang) -->
|
|
95
|
-
{alternateLocales.map(({ locale: altLocale, url }) =>
|
|
96
|
-
<link rel="alternate" hreflang={altLocale} href={url} />
|
|
97
|
-
))}
|
|
96
|
+
{alternateLocales.map(({ locale: altLocale, url }) => <link rel="alternate" hreflang={altLocale} href={url} />)}
|
|
98
97
|
|
|
99
98
|
<!-- Twitter Card -->
|
|
100
|
-
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
{
|
|
100
|
+
twitter && (
|
|
101
|
+
<>
|
|
102
|
+
<meta name="twitter:card" content={twitter.card || 'summary_large_image'} />
|
|
103
|
+
{twitter.site && <meta name="twitter:site" content={twitter.site} />}
|
|
104
|
+
{twitter.creator && <meta name="twitter:creator" content={twitter.creator} />}
|
|
105
|
+
{twitter.title && <meta name="twitter:title" content={twitter.title} />}
|
|
106
|
+
{twitter.description && <meta name="twitter:description" content={twitter.description} />}
|
|
107
|
+
{twitter.image && <meta name="twitter:image" content={twitter.image} />}
|
|
108
|
+
</>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
@@ -29,7 +29,14 @@ const {
|
|
|
29
29
|
containerClass={`${isAfterContent ? 'pt-0 md:pt-0 lg:pt-0' : ''} ${classes?.container ?? ''}`}
|
|
30
30
|
bg={bg}
|
|
31
31
|
>
|
|
32
|
-
<Headline
|
|
32
|
+
<Headline
|
|
33
|
+
title={title}
|
|
34
|
+
subtitle={subtitle}
|
|
35
|
+
tagline={tagline}
|
|
36
|
+
position={position}
|
|
37
|
+
as={asHeader}
|
|
38
|
+
asSubtitle={asSubtitle}
|
|
39
|
+
/>
|
|
33
40
|
|
|
34
41
|
<div class="flex flex-wrap justify-center gap-x-6 sm:gap-x-12 lg:gap-x-24">
|
|
35
42
|
{
|
|
@@ -50,7 +50,7 @@ const {
|
|
|
50
50
|
/>
|
|
51
51
|
|
|
52
52
|
<div class={`md:flex ${isReversed ? 'md:flex-row-reverse' : ''} md:gap-16`}>
|
|
53
|
-
<div class={cn(
|
|
53
|
+
<div class={cn('md:basis-1/2 self-center', classes?.start ?? '')}>
|
|
54
54
|
<slot />
|
|
55
55
|
{content && <div class={cn('mb-12 text-lg', classes?.content ?? '')} set:html={content} />}
|
|
56
56
|
|
|
@@ -70,21 +70,34 @@ const {
|
|
|
70
70
|
icon={defaultIcon}
|
|
71
71
|
{...item}
|
|
72
72
|
classes={{
|
|
73
|
-
container: cn(
|
|
74
|
-
|
|
73
|
+
container: cn(
|
|
74
|
+
'gap-0 border-0 py-0 justify-start',
|
|
75
|
+
(classes?.items as Record<string, string>)?.container
|
|
76
|
+
),
|
|
77
|
+
title: cn(
|
|
78
|
+
'text-lg font-medium leading-6 dark:text-white ml-2 rtl:ml-0 rtl:mr-2',
|
|
79
|
+
(classes?.items as Record<string, string>)?.title
|
|
80
|
+
),
|
|
75
81
|
description: cn('ml-2 rtl:ml-0 rtl:mr-2', (classes?.items as Record<string, string>)?.description),
|
|
76
|
-
icon: cn(
|
|
82
|
+
icon: cn(
|
|
83
|
+
'flex h-7 w-7 items-center justify-center rounded-full bg-green-600 dark:bg-green-700 text-gray-50 p-1',
|
|
84
|
+
(classes?.items as Record<string, string>)?.icon
|
|
85
|
+
),
|
|
77
86
|
}}
|
|
78
87
|
/>
|
|
79
88
|
))
|
|
80
89
|
}
|
|
81
90
|
</ItemGrid0>
|
|
82
91
|
</div>
|
|
83
|
-
<div aria-hidden="true" class={cn(
|
|
92
|
+
<div aria-hidden="true" class={cn('mt-10 md:mt-0 md:basis-1/2', classes?.end ?? '')}>
|
|
84
93
|
{
|
|
85
94
|
image && (
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
<div class={cn('relative m-auto max-w-4xl', classes?.image ?? '')}>
|
|
96
|
+
<PointMap
|
|
97
|
+
items={points}
|
|
98
|
+
pointsDisplayMode={pointsDisplayMode}
|
|
99
|
+
classes={{ ...((classes?.points as object) ?? {}) }}
|
|
100
|
+
>
|
|
88
101
|
{typeof image === 'string' ? (
|
|
89
102
|
<Fragment set:html={image} />
|
|
90
103
|
) : (
|
|
@@ -97,8 +110,8 @@ const {
|
|
|
97
110
|
{...image}
|
|
98
111
|
/>
|
|
99
112
|
)}
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
</PointMap>
|
|
114
|
+
</div>
|
|
102
115
|
)
|
|
103
116
|
}
|
|
104
117
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {Item, HotspotPoint, ToAction} from '../../types';
|
|
1
|
+
import type { Item, HotspotPoint, ToAction } from '../../types';
|
|
2
2
|
import type { WidgetWrapperProps, SvelteWidgetWrapperProps } from '../WidgetWrapper/types';
|
|
3
3
|
import type { HeadlineProps } from '../Headline/types.ts';
|
|
4
4
|
import type { Snippet } from 'svelte';
|
|
@@ -49,7 +49,7 @@ const ComponentGrid = viaGrid;
|
|
|
49
49
|
as={asHeader}
|
|
50
50
|
asSubtitle={asSubtitle}
|
|
51
51
|
/>
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
{
|
|
54
54
|
image && (
|
|
55
55
|
<div aria-hidden="true" class={cn('aspect-w-16 aspect-h-7 mb-6')}>
|
|
@@ -79,6 +79,14 @@ const ComponentGrid = viaGrid;
|
|
|
79
79
|
<slot />
|
|
80
80
|
|
|
81
81
|
<ComponentGrid columns={columns} class={classes?.grid && ''}>
|
|
82
|
-
{
|
|
82
|
+
{
|
|
83
|
+
items &&
|
|
84
|
+
items.map((item) => (
|
|
85
|
+
<Component
|
|
86
|
+
{...item}
|
|
87
|
+
classes={Object.assign({}, classes?.card || {}, item?.classes || {}) as Record<string, string>}
|
|
88
|
+
/>
|
|
89
|
+
))
|
|
90
|
+
}
|
|
83
91
|
</ComponentGrid>
|
|
84
92
|
</WidgetWrapper>
|
|
@@ -197,7 +197,12 @@ const {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
<!-- Loading indicator -->
|
|
200
|
-
<div
|
|
200
|
+
<div
|
|
201
|
+
class={cn(
|
|
202
|
+
'absolute top-0 left-0 w-full h-full flex items-center justify-center pointer-events-none',
|
|
203
|
+
`${classes?.loading}`
|
|
204
|
+
)}
|
|
205
|
+
>
|
|
201
206
|
<svg class="animate-spin h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
202
207
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
203
208
|
<path
|
|
@@ -118,8 +118,7 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
118
118
|
aria-expanded="false"
|
|
119
119
|
class={cn('hidden md:flex px-4 py-3 items-center whitespace-nowrap cursor-pointer', linksClass)}
|
|
120
120
|
>
|
|
121
|
-
{item.text}
|
|
122
|
-
<Icon name="tabler:chevron-down" class="w-3.5 h-3.5 ml-0.5 rtl:ml-0 rtl:mr-0.5" />
|
|
121
|
+
{item.text} <Icon name="tabler:chevron-down" class="w-3.5 h-3.5 ml-0.5 rtl:ml-0 rtl:mr-0.5" />
|
|
123
122
|
</button>
|
|
124
123
|
) : (
|
|
125
124
|
<a
|
|
@@ -140,7 +139,9 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
140
139
|
<div class="md:hidden">
|
|
141
140
|
{hasPanel ? (
|
|
142
141
|
<>
|
|
143
|
-
<span
|
|
142
|
+
<span
|
|
143
|
+
class={cn('block px-4 py-3 text-base font-semibold uppercase tracking-wider', megaHeadingClass)}
|
|
144
|
+
>
|
|
144
145
|
{item.text}
|
|
145
146
|
</span>
|
|
146
147
|
<ul class="flex flex-col pb-2">
|
|
@@ -202,7 +203,10 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
202
203
|
role="region"
|
|
203
204
|
aria-label="Menu details"
|
|
204
205
|
>
|
|
205
|
-
<div
|
|
206
|
+
<div
|
|
207
|
+
class={cn('flyout-bg relative overflow-hidden rounded-xl border border-border', megaMenuClass)}
|
|
208
|
+
data-flyout-bg
|
|
209
|
+
>
|
|
206
210
|
{panelItems.map(({ link, index }) => (
|
|
207
211
|
<div class="flyout-panel absolute top-0 left-0" data-flyout-panel data-flyout-key={index}>
|
|
208
212
|
{link.mega ? (
|
|
@@ -297,9 +301,14 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
297
301
|
>
|
|
298
302
|
<div class="items-center flex justify-between w-full md:w-auto">
|
|
299
303
|
<div class="flex">
|
|
300
|
-
{
|
|
301
|
-
|
|
302
|
-
|
|
304
|
+
{
|
|
305
|
+
showToggleTheme && (
|
|
306
|
+
<DarkMode
|
|
307
|
+
initialMode={initialMode}
|
|
308
|
+
iconClass={cn('w-6 h-6 md:w-5 md:h-5 md:inline-block', darkModeIconClass)}
|
|
309
|
+
/>
|
|
310
|
+
)
|
|
311
|
+
}
|
|
303
312
|
</div>
|
|
304
313
|
|
|
305
314
|
<!-- Actions Buttons -->
|
|
@@ -344,6 +353,12 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
344
353
|
opacity 0.18s ease,
|
|
345
354
|
transform 0.18s ease,
|
|
346
355
|
visibility 0s linear 0.18s;
|
|
356
|
+
/* Wrapper is full-width; only the visible box should catch clicks so a click
|
|
357
|
+
to the side of the panel falls through to the page (and closes the menu). */
|
|
358
|
+
pointer-events: none;
|
|
359
|
+
}
|
|
360
|
+
.flyout-bg {
|
|
361
|
+
pointer-events: auto;
|
|
347
362
|
}
|
|
348
363
|
.flyout.is-open {
|
|
349
364
|
opacity: 1;
|
|
@@ -533,9 +548,11 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
533
548
|
const key = trigger.dataset.flyoutKey!;
|
|
534
549
|
trigger.addEventListener('mouseenter', () => openTo(key));
|
|
535
550
|
trigger.addEventListener('focus', () => openTo(key));
|
|
551
|
+
// Clicking a trigger only ever opens (never toggles closed) so a click that
|
|
552
|
+
// races the open animation can't dismiss the menu the user just summoned.
|
|
536
553
|
trigger.addEventListener('click', (e) => {
|
|
537
554
|
e.preventDefault();
|
|
538
|
-
|
|
555
|
+
openTo(key);
|
|
539
556
|
});
|
|
540
557
|
});
|
|
541
558
|
|
|
@@ -553,8 +570,11 @@ const panelItems = links.map((link, index) => ({ link, index })).filter(({ link
|
|
|
553
570
|
document.addEventListener('keydown', (e) => {
|
|
554
571
|
if (e.key === 'Escape') close();
|
|
555
572
|
});
|
|
573
|
+
// Close on any click outside the *visible* box (bgEl) or the nav. The flyout
|
|
574
|
+
// wrapper spans the full width, so clicking to the side of the panel must not
|
|
575
|
+
// count as "inside" — otherwise the menu would stay stuck open.
|
|
556
576
|
document.addEventListener('click', (e) => {
|
|
557
|
-
if (open && !
|
|
577
|
+
if (open && !bgEl.contains(e.target as Node) && !nav?.contains(e.target as Node)) close();
|
|
558
578
|
});
|
|
559
579
|
window.addEventListener('resize', () => {
|
|
560
580
|
if (open && currentKey) {
|
|
@@ -6,7 +6,7 @@ import SwiperSlider from '../SwiperSlider/SwiperSlider.astro';
|
|
|
6
6
|
import { Image as AstroImage, getImage } from 'astro:assets';
|
|
7
7
|
import { fetchLocalImages } from '../../utils/images';
|
|
8
8
|
import { cn } from '../../utils';
|
|
9
|
-
import Button from
|
|
9
|
+
import Button from '../Button/Button.astro';
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
title = await Astro.slots.render('title'),
|
|
@@ -28,7 +28,7 @@ const {
|
|
|
28
28
|
...rest
|
|
29
29
|
} = Astro.props;
|
|
30
30
|
|
|
31
|
-
const images = await fetchLocalImages() as Record<string, () => any>;
|
|
31
|
+
const images = (await fetchLocalImages()) as Record<string, () => any>;
|
|
32
32
|
const imagePaths = Object.keys(images).filter((imagePath) => {
|
|
33
33
|
return imagePath.startsWith(`/src/assets/images/${imagesFolder}/`);
|
|
34
34
|
});
|
|
@@ -62,11 +62,19 @@ const imagePaths = Object.keys(images).filter((imagePath) => {
|
|
|
62
62
|
|
|
63
63
|
// Generate alt text: alt prop > title-based > numbered fallback
|
|
64
64
|
// Detect UUID filenames (8-4-4-4-12 hex pattern) and treat as meaningless
|
|
65
|
-
const filename =
|
|
65
|
+
const filename =
|
|
66
|
+
imagePath
|
|
67
|
+
.split('/')
|
|
68
|
+
.pop()
|
|
69
|
+
?.replace(/\.[^/.]+$/, '') || '';
|
|
66
70
|
const isUuidFilename = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(filename);
|
|
67
71
|
const imageNumber = index + 1;
|
|
68
|
-
const imageName =
|
|
69
|
-
const baseTitle = title
|
|
72
|
+
const imageName = filename && !isUuidFilename ? filename : String(imageNumber);
|
|
73
|
+
const baseTitle = title
|
|
74
|
+
? String(title)
|
|
75
|
+
.replace(/<[^>]*>/g, '')
|
|
76
|
+
.trim()
|
|
77
|
+
: '';
|
|
70
78
|
const altText = alt
|
|
71
79
|
? alt + ' - ' + imageName
|
|
72
80
|
: baseTitle
|
|
@@ -90,7 +98,10 @@ const imagePaths = Object.keys(images).filter((imagePath) => {
|
|
|
90
98
|
widths={[400, 900]}
|
|
91
99
|
width={400}
|
|
92
100
|
sizes="(max-width: 900px) 400px, 900px"
|
|
93
|
-
class={cn(
|
|
101
|
+
class={cn(
|
|
102
|
+
'object-cover object-top w-full md:h-full group-hover:scale-105 transition duration-300',
|
|
103
|
+
classes?.image
|
|
104
|
+
)}
|
|
94
105
|
/>
|
|
95
106
|
</a>
|
|
96
107
|
</swiper-slide>
|
|
@@ -107,7 +118,7 @@ const imagePaths = Object.keys(images).filter((imagePath) => {
|
|
|
107
118
|
</div>
|
|
108
119
|
)
|
|
109
120
|
}
|
|
110
|
-
|
|
121
|
+
|
|
111
122
|
<slot />
|
|
112
123
|
</WidgetWrapper>
|
|
113
124
|
|
|
@@ -30,7 +30,14 @@ const {
|
|
|
30
30
|
containerClass={`max-w-6xl mx-auto ${isAfterContent ? 'pt-0 md:pt-0 lg:pt-0' : ''} ${classes?.container ?? ''}`}
|
|
31
31
|
bg={bg}
|
|
32
32
|
>
|
|
33
|
-
<Headline
|
|
33
|
+
<Headline
|
|
34
|
+
title={title}
|
|
35
|
+
subtitle={subtitle}
|
|
36
|
+
tagline={tagline}
|
|
37
|
+
position={position}
|
|
38
|
+
as={asHeader}
|
|
39
|
+
asSubtitle={asSubtitle}
|
|
40
|
+
/>
|
|
34
41
|
|
|
35
42
|
<ItemGrid0 columns={columns}>
|
|
36
43
|
{
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { WidgetWrapperProps as Props } from './types';
|
|
3
3
|
import Background from '../Background/Background.astro';
|
|
4
|
-
import {cn} from
|
|
4
|
+
import { cn } from '../../utils';
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
id,
|
|
8
|
+
isDark = false,
|
|
9
|
+
class: className = '',
|
|
10
|
+
containerClass = '',
|
|
11
|
+
bg,
|
|
12
|
+
as = 'section',
|
|
13
|
+
isAfterContent = false,
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props;
|
|
7
16
|
|
|
8
17
|
const WrapperTag = as;
|
|
9
18
|
---
|