@sofidevo/astro-dynamic-header 2.0.2 → 3.1.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.
- package/README.md +165 -323
- package/package.json +8 -5
- package/src/ChevronIcon.astro +25 -0
- package/src/HamburgerButton.astro +2 -12
- package/src/Header.astro +75 -116
- package/src/MobileNav.astro +57 -26
- package/src/MoonIcon.astro +106 -0
- package/src/NavMenu.astro +41 -23
- package/src/SunIcon.astro +34 -0
- package/src/defaults.ts +36 -0
- package/src/index.ts +71 -63
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
export interface Props {
|
|
3
|
-
/** Override the default color of the hamburger lines */
|
|
4
|
-
color?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const { color = "var(--text-color, var(--color-hamburger-lines, #fff))" } =
|
|
8
|
-
Astro.props;
|
|
2
|
+
export interface Props {}
|
|
9
3
|
---
|
|
10
4
|
|
|
11
5
|
<button
|
|
12
6
|
class="hamburger hamburger--collapse"
|
|
13
7
|
type="button"
|
|
14
8
|
id="hamburger-btn"
|
|
15
|
-
style={`--hamburger-color: ${color};`}
|
|
16
9
|
>
|
|
17
10
|
<span class="hamburger-box">
|
|
18
11
|
<span class="hamburger-inner"></span>
|
|
@@ -65,10 +58,7 @@ const { color = "var(--text-color, var(--color-hamburger-lines, #fff))" } =
|
|
|
65
58
|
.hamburger-inner,
|
|
66
59
|
.hamburger-inner::before,
|
|
67
60
|
.hamburger-inner::after {
|
|
68
|
-
background-color: var(
|
|
69
|
-
--hamburger-color,
|
|
70
|
-
var(--color-hamburger-lines, #fff)
|
|
71
|
-
);
|
|
61
|
+
background-color: var(--text-color, inherit);
|
|
72
62
|
position: absolute;
|
|
73
63
|
width: 40px;
|
|
74
64
|
height: 4px;
|
package/src/Header.astro
CHANGED
|
@@ -4,21 +4,20 @@ import NavMenu from "./NavMenu.astro";
|
|
|
4
4
|
import MobileNav from "./MobileNav.astro";
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
|
-
LogoConfig,
|
|
8
7
|
NavConfig,
|
|
9
8
|
DualThemeConfig,
|
|
10
|
-
|
|
9
|
+
HeaderClassNames,
|
|
11
10
|
} from "./index.js";
|
|
12
11
|
|
|
13
12
|
export interface Props {
|
|
14
|
-
/**
|
|
13
|
+
/**
|
|
15
14
|
* Layout style.
|
|
16
15
|
* - "floating": Centered with max-width and rounded corners.
|
|
17
16
|
* - "fullscreen": Full width with no border radius.
|
|
18
17
|
* @default "floating"
|
|
19
18
|
*/
|
|
20
19
|
headerType?: "floating" | "fullscreen";
|
|
21
|
-
/**
|
|
20
|
+
/**
|
|
22
21
|
* Theme behavior.
|
|
23
22
|
* - "light": Force light mode.
|
|
24
23
|
* - "dark": Force dark mode.
|
|
@@ -26,35 +25,31 @@ export interface Props {
|
|
|
26
25
|
* @default "auto"
|
|
27
26
|
*/
|
|
28
27
|
preset?: "light" | "dark" | "auto";
|
|
29
|
-
|
|
30
|
-
logo?: LogoConfig;
|
|
28
|
+
|
|
31
29
|
/** Navigation links and structure. */
|
|
32
30
|
navigation?: NavConfig;
|
|
33
31
|
/** Custom theme overrides for light and dark modes. */
|
|
34
32
|
theme?: DualThemeConfig;
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
33
|
+
/**
|
|
34
|
+
* High-level CSS class overrides for structural wrapper elements.
|
|
35
|
+
* For fine-grained nav/logo element classes, use the nested `xxx__class`
|
|
36
|
+
* props inside `navigation` or `logo` instead.
|
|
37
|
+
* @example { header: "shadow-lg", container: "top-4" }
|
|
38
38
|
*/
|
|
39
|
-
classNames?:
|
|
39
|
+
classNames?: HeaderClassNames;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const {
|
|
43
43
|
headerType = "floating",
|
|
44
44
|
preset = "auto",
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
navigation = {},
|
|
47
47
|
theme = {},
|
|
48
48
|
classNames = {},
|
|
49
49
|
} = Astro.props;
|
|
50
50
|
|
|
51
51
|
if (import.meta.env.DEV) {
|
|
52
|
-
|
|
53
|
-
if (typeof logo === "string") {
|
|
54
|
-
console.warn(
|
|
55
|
-
"[@sofidevo/astro-dynamic-header] BREAKING CHANGE: The 'logo' prop now expects an object. Please use logo={{ src: '...' }} instead.",
|
|
56
|
-
);
|
|
57
|
-
}
|
|
52
|
+
|
|
58
53
|
if (Array.isArray(navigation)) {
|
|
59
54
|
console.warn(
|
|
60
55
|
"[@sofidevo/astro-dynamic-header] BREAKING CHANGE: The 'navigation' prop now expects an object with 'menuItems'. Please use navigation={{ menuItems: [...] }} instead.",
|
|
@@ -62,42 +57,36 @@ if (import.meta.env.DEV) {
|
|
|
62
57
|
}
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
light: {
|
|
68
|
-
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
|
69
|
-
backgroundColorOpaque: "rgb(255, 255, 255)",
|
|
70
|
-
backdropBlur: "blur(20px)",
|
|
71
|
-
zIndex: 10,
|
|
72
|
-
textColor: "#1a1a1a",
|
|
73
|
-
accentColor: "#3e1c71",
|
|
74
|
-
},
|
|
75
|
-
dark: {
|
|
76
|
-
backgroundColor: "#0d0d0dcc",
|
|
77
|
-
backgroundColorOpaque: "#0d0d0d",
|
|
78
|
-
backdropBlur: "blur(20px)",
|
|
79
|
-
zIndex: 10,
|
|
80
|
-
textColor: "#ffffff",
|
|
81
|
-
accentColor: "#00ffff",
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Merge with user overrides
|
|
86
|
-
const lightTheme = { ...defaultThemes.light, ...theme.light };
|
|
87
|
-
const darkTheme = { ...defaultThemes.dark, ...theme.dark };
|
|
60
|
+
// Map user theme overrides to CSS custom properties
|
|
61
|
+
const inlineStyles: Record<string, string> = {};
|
|
88
62
|
|
|
89
|
-
|
|
90
|
-
|
|
63
|
+
if (theme.light) {
|
|
64
|
+
if (theme.light.backgroundColor) inlineStyles["--l-bg"] = theme.light.backgroundColor;
|
|
65
|
+
if (theme.light.backgroundColorOpaque) inlineStyles["--l-bg-opaque"] = theme.light.backgroundColorOpaque;
|
|
66
|
+
if (theme.light.textColor) inlineStyles["--l-text"] = theme.light.textColor;
|
|
67
|
+
if (theme.light.accentColor) inlineStyles["--l-accent"] = theme.light.accentColor;
|
|
68
|
+
if (theme.light.backdropBlur) inlineStyles["--l-blur"] = theme.light.backdropBlur;
|
|
69
|
+
}
|
|
70
|
+
if (theme.dark) {
|
|
71
|
+
if (theme.dark.backgroundColor) inlineStyles["--d-bg"] = theme.dark.backgroundColor;
|
|
72
|
+
if (theme.dark.backgroundColorOpaque) inlineStyles["--d-bg-opaque"] = theme.dark.backgroundColorOpaque;
|
|
73
|
+
if (theme.dark.textColor) inlineStyles["--d-text"] = theme.dark.textColor;
|
|
74
|
+
if (theme.dark.accentColor) inlineStyles["--d-accent"] = theme.dark.accentColor;
|
|
75
|
+
if (theme.dark.backdropBlur) inlineStyles["--d-blur"] = theme.dark.backdropBlur;
|
|
76
|
+
}
|
|
91
77
|
|
|
92
|
-
|
|
78
|
+
const zIndex = theme.light?.zIndex ?? 10;
|
|
79
|
+
|
|
80
|
+
// Navigation configuration
|
|
93
81
|
const {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
82
|
+
homeUrl = "/",
|
|
83
|
+
menuItems = [],
|
|
84
|
+
header__menu__class,
|
|
85
|
+
header__item__class,
|
|
86
|
+
menu__link__class,
|
|
87
|
+
} = navigation;
|
|
88
|
+
|
|
89
|
+
|
|
101
90
|
|
|
102
91
|
const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
103
92
|
---
|
|
@@ -150,7 +139,7 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
|
150
139
|
`header__container--${headerType}`,
|
|
151
140
|
classNames.container,
|
|
152
141
|
]}
|
|
153
|
-
style={{ zIndex
|
|
142
|
+
style={{ zIndex }}
|
|
154
143
|
>
|
|
155
144
|
<header
|
|
156
145
|
class:list={[
|
|
@@ -159,40 +148,18 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
|
159
148
|
forcedClass,
|
|
160
149
|
classNames.header,
|
|
161
150
|
]}
|
|
162
|
-
style={
|
|
163
|
-
"--l-bg": lightTheme.backgroundColor,
|
|
164
|
-
"--l-bg-opaque": lightTheme.backgroundColorOpaque,
|
|
165
|
-
"--l-text": lightTheme.textColor,
|
|
166
|
-
"--l-accent": lightTheme.accentColor,
|
|
167
|
-
"--l-blur": lightTheme.backdropBlur,
|
|
168
|
-
"--d-bg": darkTheme.backgroundColor,
|
|
169
|
-
"--d-bg-opaque": darkTheme.backgroundColorOpaque,
|
|
170
|
-
"--d-text": darkTheme.textColor,
|
|
171
|
-
"--d-accent": darkTheme.accentColor,
|
|
172
|
-
"--d-blur": darkTheme.backdropBlur,
|
|
173
|
-
}}
|
|
151
|
+
style={Object.keys(inlineStyles).length > 0 ? inlineStyles : undefined}
|
|
174
152
|
>
|
|
175
|
-
<
|
|
176
|
-
<img
|
|
177
|
-
class="header__logo"
|
|
178
|
-
src={logoSrc}
|
|
179
|
-
alt={logoAlt}
|
|
180
|
-
style={{ width: logoWidth }}
|
|
181
|
-
/>
|
|
182
|
-
{
|
|
183
|
-
logoText && (
|
|
184
|
-
<span
|
|
185
|
-
class:list={["header__logo-text", classNames.logoText]}
|
|
186
|
-
style={{ color: logoTextColor, fontSize: logoTextSize }}
|
|
187
|
-
>
|
|
188
|
-
{logoText}
|
|
189
|
-
</span>
|
|
190
|
-
)
|
|
191
|
-
}
|
|
192
|
-
</a>
|
|
153
|
+
<slot name="logo" />
|
|
193
154
|
|
|
194
155
|
<div class:list={["nav-menu-wrapper", classNames.nav]}>
|
|
195
|
-
<NavMenu
|
|
156
|
+
<NavMenu
|
|
157
|
+
menuItems={menuItems}
|
|
158
|
+
homeUrl={homeUrl}
|
|
159
|
+
header__menu__class={header__menu__class}
|
|
160
|
+
header__item__class={header__item__class}
|
|
161
|
+
menu__link__class={menu__link__class}
|
|
162
|
+
/>
|
|
196
163
|
</div>
|
|
197
164
|
|
|
198
165
|
{
|
|
@@ -205,7 +172,12 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
|
205
172
|
|
|
206
173
|
<HamburgerButton />
|
|
207
174
|
|
|
208
|
-
<MobileNav
|
|
175
|
+
<MobileNav
|
|
176
|
+
menuItems={menuItems}
|
|
177
|
+
homeUrl={homeUrl}
|
|
178
|
+
type={headerType}
|
|
179
|
+
mobileNav__class={classNames.mobileNav}
|
|
180
|
+
>
|
|
209
181
|
{
|
|
210
182
|
Astro.slots.has("actions") && (
|
|
211
183
|
<div class="actions-mobile" slot="slot-panel">
|
|
@@ -219,11 +191,11 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
|
219
191
|
|
|
220
192
|
<style is:inline>
|
|
221
193
|
.header {
|
|
222
|
-
--bg-color: var(--l-bg);
|
|
223
|
-
--bg-color-opaque: var(--l-bg-opaque);
|
|
224
|
-
--text-color: var(--l-text);
|
|
225
|
-
--accent-color: var(--l-accent);
|
|
226
|
-
--backdrop-blur: var(--l-blur);
|
|
194
|
+
--bg-color: var(--l-bg, rgba(255, 255, 255, 0.9));
|
|
195
|
+
--bg-color-opaque: var(--l-bg-opaque, rgb(255, 255, 255));
|
|
196
|
+
--text-color: var(--l-text, #1a1a1a);
|
|
197
|
+
--accent-color: var(--l-accent, #3e1c71);
|
|
198
|
+
--backdrop-blur: var(--l-blur, blur(20px));
|
|
227
199
|
|
|
228
200
|
display: flex;
|
|
229
201
|
align-items: center;
|
|
@@ -247,28 +219,28 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
|
247
219
|
|
|
248
220
|
/* Automatic dark mode detection via root class */
|
|
249
221
|
:root.dark .header:not(.header--force-light) {
|
|
250
|
-
--bg-color: var(--d-bg);
|
|
251
|
-
--bg-color-opaque: var(--d-bg-opaque);
|
|
252
|
-
--text-color: var(--d-text);
|
|
253
|
-
--accent-color: var(--d-accent);
|
|
254
|
-
--backdrop-blur: var(--d-blur);
|
|
222
|
+
--bg-color: var(--d-bg, #0d0d0dcc);
|
|
223
|
+
--bg-color-opaque: var(--d-bg-opaque, #0d0d0d);
|
|
224
|
+
--text-color: var(--d-text, #ffffff);
|
|
225
|
+
--accent-color: var(--d-accent, #00ffff);
|
|
226
|
+
--backdrop-blur: var(--d-blur, blur(20px));
|
|
255
227
|
}
|
|
256
228
|
|
|
257
229
|
/* Forced themes */
|
|
258
230
|
.header--force-dark {
|
|
259
|
-
--bg-color: var(--d-bg) !important;
|
|
260
|
-
--bg-color-opaque: var(--d-bg-opaque) !important;
|
|
261
|
-
--text-color: var(--d-text) !important;
|
|
262
|
-
--accent-color: var(--d-accent) !important;
|
|
263
|
-
--backdrop-blur: var(--d-blur) !important;
|
|
231
|
+
--bg-color: var(--d-bg, #0d0d0dcc) !important;
|
|
232
|
+
--bg-color-opaque: var(--d-bg-opaque, #0d0d0d) !important;
|
|
233
|
+
--text-color: var(--d-text, #ffffff) !important;
|
|
234
|
+
--accent-color: var(--d-accent, #00ffff) !important;
|
|
235
|
+
--backdrop-blur: var(--d-blur, blur(20px)) !important;
|
|
264
236
|
}
|
|
265
237
|
|
|
266
238
|
.header--force-light {
|
|
267
|
-
--bg-color: var(--l-bg) !important;
|
|
268
|
-
--bg-color-opaque: var(--l-bg-opaque) !important;
|
|
269
|
-
--text-color: var(--l-text) !important;
|
|
270
|
-
--accent-color: var(--l-accent) !important;
|
|
271
|
-
--backdrop-blur: var(--l-blur) !important;
|
|
239
|
+
--bg-color: var(--l-bg, rgba(255, 255, 255, 0.9)) !important;
|
|
240
|
+
--bg-color-opaque: var(--l-bg-opaque, rgb(255, 255, 255)) !important;
|
|
241
|
+
--text-color: var(--l-text, #1a1a1a) !important;
|
|
242
|
+
--accent-color: var(--l-accent, #3e1c71) !important;
|
|
243
|
+
--backdrop-blur: var(--l-blur, blur(20px)) !important;
|
|
272
244
|
}
|
|
273
245
|
|
|
274
246
|
.header__container {
|
|
@@ -313,20 +285,7 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
|
|
|
313
285
|
}
|
|
314
286
|
}
|
|
315
287
|
|
|
316
|
-
.logo__container {
|
|
317
|
-
display: flex;
|
|
318
|
-
align-items: center;
|
|
319
|
-
text-decoration: none;
|
|
320
|
-
color: inherit;
|
|
321
|
-
& span {
|
|
322
|
-
font-weight: 600;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
288
|
|
|
326
|
-
.header__logo {
|
|
327
|
-
margin-right: 1em;
|
|
328
|
-
object-fit: contain;
|
|
329
|
-
}
|
|
330
289
|
|
|
331
290
|
.actions-desktop {
|
|
332
291
|
@media (width < 768px) {
|
package/src/MobileNav.astro
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
|
|
3
|
+
import ChevronIcon from "./ChevronIcon.astro";
|
|
3
4
|
|
|
4
5
|
export interface Props {
|
|
5
6
|
/** Header type for styling: "floating" or "fullscreen" */
|
|
@@ -10,30 +11,38 @@ export interface Props {
|
|
|
10
11
|
showHomeLink?: boolean;
|
|
11
12
|
/** Text for the home link (defaults to "Home") */
|
|
12
13
|
homeText?: string;
|
|
14
|
+
/** The destination URL for the home link. @default "/" */
|
|
15
|
+
homeUrl?: string;
|
|
16
|
+
/** Extra CSS class(es) for the root mobile `<nav>` panel. */
|
|
17
|
+
mobileNav__class?: string;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
const { type } = Astro.props;
|
|
16
|
-
|
|
17
20
|
const {
|
|
21
|
+
type,
|
|
22
|
+
mobileNav__class,
|
|
18
23
|
menuItems = [],
|
|
19
24
|
showHomeLink = true,
|
|
20
25
|
homeText = "Home",
|
|
26
|
+
homeUrl = "/",
|
|
21
27
|
} = Astro.props;
|
|
22
28
|
|
|
23
29
|
const pagePathname = Astro.url.pathname;
|
|
24
30
|
---
|
|
25
31
|
|
|
26
32
|
<nav
|
|
27
|
-
class={
|
|
33
|
+
class:list={[
|
|
34
|
+
`mobile-header__menu mobile-header__menu--${type}`,
|
|
35
|
+
mobileNav__class,
|
|
36
|
+
]}
|
|
28
37
|
id="mobile-header-menu"
|
|
29
38
|
>
|
|
30
39
|
<ol class="mobile-menu">
|
|
31
40
|
{
|
|
32
|
-
showHomeLink && pagePathname !==
|
|
41
|
+
showHomeLink && pagePathname !== homeUrl && (
|
|
33
42
|
<li class="header__item mobile-header__item">
|
|
34
43
|
<a
|
|
35
44
|
class="menu__link mobile-menu__link"
|
|
36
|
-
href=
|
|
45
|
+
href={homeUrl}
|
|
37
46
|
data-astro-prefetch="hover"
|
|
38
47
|
>
|
|
39
48
|
{homeText}
|
|
@@ -47,16 +56,13 @@ const pagePathname = Astro.url.pathname;
|
|
|
47
56
|
return (
|
|
48
57
|
<li>
|
|
49
58
|
{item.submenu ? (
|
|
50
|
-
<details
|
|
59
|
+
<details
|
|
60
|
+
class="mobile-details"
|
|
61
|
+
open={item.submenu.some((sub) => pagePathname === sub.link)}
|
|
62
|
+
>
|
|
51
63
|
<summary class:list={["menu__summary", { active: isActive }]}>
|
|
52
64
|
{item.text}
|
|
53
|
-
<iconify-
|
|
54
|
-
class="iconify-arrow"
|
|
55
|
-
icon="codicon:triangle-down"
|
|
56
|
-
width="15"
|
|
57
|
-
height="15"
|
|
58
|
-
style="color: var(--accent-color);"
|
|
59
|
-
/>
|
|
65
|
+
<ChevronIcon class="iconify-arrow" width={15} height={15} />
|
|
60
66
|
</summary>
|
|
61
67
|
<ol class="mobile-submenu">
|
|
62
68
|
{item?.submenu?.map((sub: SecondaryMenuItem) => {
|
|
@@ -64,24 +70,37 @@ const pagePathname = Astro.url.pathname;
|
|
|
64
70
|
return (
|
|
65
71
|
<li>
|
|
66
72
|
{sub.submenu && sub.submenu.length > 0 ? (
|
|
67
|
-
<details
|
|
68
|
-
|
|
73
|
+
<details
|
|
74
|
+
class="mobile-details"
|
|
75
|
+
open={sub.submenu.some(
|
|
76
|
+
(subsub) => pagePathname === subsub.link,
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
<summary
|
|
80
|
+
class:list={[
|
|
81
|
+
"menu__summary",
|
|
82
|
+
{ active: isSubActive },
|
|
83
|
+
]}
|
|
84
|
+
>
|
|
69
85
|
{sub.text}
|
|
70
|
-
<
|
|
86
|
+
<ChevronIcon
|
|
71
87
|
class="iconify-arrow"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
height="15"
|
|
75
|
-
style="color: var(--accent-color);"
|
|
88
|
+
width={15}
|
|
89
|
+
height={15}
|
|
76
90
|
/>
|
|
77
91
|
</summary>
|
|
78
92
|
<ol class="mobile-subsubmenu">
|
|
79
93
|
{sub.submenu.map((subsub: TertiaryMenuItem) => {
|
|
80
|
-
const isSubSubActive =
|
|
94
|
+
const isSubSubActive =
|
|
95
|
+
pagePathname === subsub.link;
|
|
81
96
|
return (
|
|
82
97
|
<li class="mobile-submenu__item secondary">
|
|
83
98
|
<a
|
|
84
|
-
class:list={[
|
|
99
|
+
class:list={[
|
|
100
|
+
"menu__link",
|
|
101
|
+
"mobile-menu__link",
|
|
102
|
+
{ active: isSubSubActive },
|
|
103
|
+
]}
|
|
85
104
|
href={subsub.link}
|
|
86
105
|
>
|
|
87
106
|
{subsub.text}
|
|
@@ -92,7 +111,14 @@ const pagePathname = Astro.url.pathname;
|
|
|
92
111
|
</ol>
|
|
93
112
|
</details>
|
|
94
113
|
) : (
|
|
95
|
-
<a
|
|
114
|
+
<a
|
|
115
|
+
class:list={[
|
|
116
|
+
"menu__link",
|
|
117
|
+
"mobile-menu__link",
|
|
118
|
+
{ active: isSubActive },
|
|
119
|
+
]}
|
|
120
|
+
href={sub.link}
|
|
121
|
+
>
|
|
96
122
|
{sub.text}
|
|
97
123
|
</a>
|
|
98
124
|
)}
|
|
@@ -102,7 +128,10 @@ const pagePathname = Astro.url.pathname;
|
|
|
102
128
|
</ol>
|
|
103
129
|
</details>
|
|
104
130
|
) : (
|
|
105
|
-
<a
|
|
131
|
+
<a
|
|
132
|
+
class:list={["mobile-menu__link", { active: isActive }]}
|
|
133
|
+
href={item.link}
|
|
134
|
+
>
|
|
106
135
|
{item.text}
|
|
107
136
|
</a>
|
|
108
137
|
)}
|
|
@@ -128,7 +157,9 @@ const pagePathname = Astro.url.pathname;
|
|
|
128
157
|
left: 0;
|
|
129
158
|
background: var(--bg-color-opaque, #151515);
|
|
130
159
|
z-index: 20;
|
|
131
|
-
transition:
|
|
160
|
+
transition:
|
|
161
|
+
transform 0.3s ease-in-out,
|
|
162
|
+
background-color 0.3s ease;
|
|
132
163
|
transform: translateX(100%);
|
|
133
164
|
padding: 10rem 2rem 2rem;
|
|
134
165
|
color: var(--text-color, #fff);
|
|
@@ -205,7 +236,7 @@ const pagePathname = Astro.url.pathname;
|
|
|
205
236
|
overflow: clip;
|
|
206
237
|
}
|
|
207
238
|
|
|
208
|
-
.mobile-details:open iconify-
|
|
239
|
+
.mobile-details:open svg.iconify-arrow {
|
|
209
240
|
transform: rotate(180deg);
|
|
210
241
|
}
|
|
211
242
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
const props = Astro.props;
|
|
3
|
+
|
|
4
|
+
const regex = /\sid="(\S+)"/g;
|
|
5
|
+
const counters = new Map<string, number>();
|
|
6
|
+
|
|
7
|
+
function nextID(id: string) {
|
|
8
|
+
id = id.replace(/[0-9]+$/, "") || "a";
|
|
9
|
+
const count = counters.get(id) || 0;
|
|
10
|
+
counters.set(id, count + 1);
|
|
11
|
+
return count ? `${id}${count}` : id;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function replaceIDs(body: string) {
|
|
15
|
+
const ids: string[] = [];
|
|
16
|
+
let match;
|
|
17
|
+
while ((match = regex.exec(body))) ids.push(match[1]);
|
|
18
|
+
if (!ids.length) return body;
|
|
19
|
+
const suffix =
|
|
20
|
+
"suffix" + ((Math.random() * 16777216) | Date.now()).toString(16);
|
|
21
|
+
ids.forEach((id) => {
|
|
22
|
+
const newID = nextID(id);
|
|
23
|
+
const escapedID = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24
|
+
body = body.replace(
|
|
25
|
+
new RegExp('([#;"])(' + escapedID + ')([")]|\\.[a-z])', "g"),
|
|
26
|
+
"$1" + newID + suffix + "$3",
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
body = body.replace(new RegExp(suffix, "g"), "");
|
|
30
|
+
return body;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const viewBox = "0 0 24 24";
|
|
34
|
+
const content = replaceIDs(
|
|
35
|
+
`<defs><mask id="SVGnw0mAdjE"><path d="M12 2c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z" class="n1mjuz"><animate fill="freeze" attributeName="d" dur="0.4s" values="M12 6c3.31 0 6 2.69 6 6c0 3.31 -2.69 6 -6 6c-3.31 0 -6 -2.69 -6 -6c0 -3.31 2.69 -6 6 -6Z;M12 2c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z"/><set fill="freeze" attributeName="opacity" begin="0.4s" to="0"/></path><path d="M18 -4c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z"><animate fill="freeze" attributeName="d" dur="0.4s" values="M22 -4c3.31 0 6 2.69 6 6c0 3.31 -2.69 6 -6 6c-3.31 0 -6 -2.69 -6 -6c0 -3.31 2.69 -6 6 -6Z;M18 -4c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z"/><set fill="freeze" attributeName="opacity" begin="0.4s" to="0"/></path></mask></defs><path class="smogax"><animate fill="freeze" attributeName="opacity" dur="0.4s" values="1;0"/></path><g class="cuyn6t"><path mask="url(#SVGnw0mAdjE)" class="t_ev7s"/><path opacity="0" class="izfmla"><set fill="freeze" attributeName="opacity" begin="0.4s" to="1"/></path></g><g stroke-dashoffset="4" class="abf34n"><path class="k7_l3l"><animate attributeName="stroke-dashoffset" begin="0.4s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="hhuniw"><animate attributeName="stroke-dashoffset" begin="1.07s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="z8cgtd"><animate attributeName="stroke-dashoffset" begin="1.74s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="drcfta"><animate attributeName="stroke-dashoffset" begin="2.41s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="yij7vx"><animate attributeName="stroke-dashoffset" begin="3.08s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="emoajy"><animate attributeName="stroke-dashoffset" begin="3.75s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="f1to6s"><animate attributeName="stroke-dashoffset" begin="4.42s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="niixso"><animate attributeName="stroke-dashoffset" begin="5.09s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="z8cgtd"><animate attributeName="stroke-dashoffset" begin="5.76s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path></g>`,
|
|
36
|
+
);
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
<svg
|
|
40
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
41
|
+
width="25px"
|
|
42
|
+
height="25px"
|
|
43
|
+
viewBox={viewBox}
|
|
44
|
+
{...props}
|
|
45
|
+
set:html={content}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<style is:inline>
|
|
49
|
+
.n1mjuz {
|
|
50
|
+
fill: var(--svg-color--fff, #fff);
|
|
51
|
+
}
|
|
52
|
+
.smogax {
|
|
53
|
+
fill: none;
|
|
54
|
+
stroke: white;
|
|
55
|
+
stroke-linecap: round;
|
|
56
|
+
stroke-linejoin: round;
|
|
57
|
+
stroke-width: var(--svg-stroke-width--2px, 2px);
|
|
58
|
+
d: path(
|
|
59
|
+
"M12 21v1M21 12h1M12 3v-1M3 12h-1M18.5 18.5l0.5 0.5M18.5 5.5l0.5 -0.5M5.5 5.5l-0.5 -0.5M5.5 18.5l-0.5 0.5"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
.t_ev7s {
|
|
63
|
+
d: path("M0 0h24v24H0z");
|
|
64
|
+
}
|
|
65
|
+
.izfmla {
|
|
66
|
+
stroke: white;
|
|
67
|
+
stroke-linecap: round;
|
|
68
|
+
stroke-linejoin: round;
|
|
69
|
+
stroke-width: var(--svg-stroke-width--2px, 2px);
|
|
70
|
+
d: path(
|
|
71
|
+
"M7 6c0 6.08 4.92 11 11 11c0.53 0 1.05 -0.04 1.56 -0.11c-1.61 2.47 -4.39 4.11 -7.56 4.11c-4.97 0 -9 -4.03 -9 -9c0 -3.17 1.64 -5.95 4.11 -7.56c-0.07 0.51 -0.11 1.03 -0.11 1.56Z"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
.abf34n {
|
|
75
|
+
fill: none;
|
|
76
|
+
stroke: white;
|
|
77
|
+
stroke-dasharray: 4;
|
|
78
|
+
stroke-linecap: round;
|
|
79
|
+
stroke-linejoin: round;
|
|
80
|
+
}
|
|
81
|
+
.k7_l3l {
|
|
82
|
+
d: path("M12 5h1.5M12 5h-1.5M12 5v1.5M12 5v-1.5");
|
|
83
|
+
}
|
|
84
|
+
.hhuniw {
|
|
85
|
+
d: path("M17 11h1.5M17 11h-1.5M17 11v1.5M17 11v-1.5");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.z8cgtd {
|
|
89
|
+
d: path("M20 5h1.5M20 5h-1.5M20 5v1.5M20 5v-1.5");
|
|
90
|
+
}
|
|
91
|
+
.drcfta {
|
|
92
|
+
d: path("M12 4h1.5M12 4h-1.5M12 4v1.5M12 4v-1.5");
|
|
93
|
+
}
|
|
94
|
+
.yij7vx {
|
|
95
|
+
d: path("M18 12h1.5M18 12h-1.5M18 12v1.5M18 12v-1.5");
|
|
96
|
+
}
|
|
97
|
+
.emoajy {
|
|
98
|
+
d: path("M19 4h1.5M19 4h-1.5M19 4v1.5M19 4v-1.5");
|
|
99
|
+
}
|
|
100
|
+
.f1to6s {
|
|
101
|
+
d: path("M13 4h1.5M13 4h-1.5M13 4v1.5M13 4v-1.5");
|
|
102
|
+
}
|
|
103
|
+
.niixso {
|
|
104
|
+
d: path("M19 11h1.5M19 11h-1.5M19 11v1.5M19 11v-1.5");
|
|
105
|
+
}
|
|
106
|
+
</style>
|