@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
package/src/NavMenu.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
|
/**
|
|
@@ -18,11 +19,29 @@ export interface Props {
|
|
|
18
19
|
* Text for the home link.
|
|
19
20
|
* @default "Home"
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
22
|
homeText?: string;
|
|
23
|
+
/**
|
|
24
|
+
* The destination URL for the home link.
|
|
25
|
+
* @default "/"
|
|
26
|
+
*/
|
|
27
|
+
homeUrl?: string;
|
|
28
|
+
/** Extra CSS class(es) for the `<nav>` element. */
|
|
29
|
+
header__menu__class?: string;
|
|
30
|
+
/** Extra CSS class(es) for each top-level `<li>` item. */
|
|
31
|
+
header__item__class?: string;
|
|
32
|
+
/** Extra CSS class(es) for each top-level menu `<a>` link. */
|
|
33
|
+
menu__link__class?: string;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
|
-
const {
|
|
36
|
+
const {
|
|
37
|
+
menuItems = [],
|
|
38
|
+
showHomeLink = true,
|
|
39
|
+
homeText = "Home",
|
|
40
|
+
homeUrl = "/",
|
|
41
|
+
header__menu__class,
|
|
42
|
+
header__item__class,
|
|
43
|
+
menu__link__class,
|
|
44
|
+
} = Astro.props;
|
|
26
45
|
|
|
27
46
|
const pagePathname = Astro.url.pathname;
|
|
28
47
|
---
|
|
@@ -129,12 +148,16 @@ const pagePathname = Astro.url.pathname;
|
|
|
129
148
|
});
|
|
130
149
|
</script>
|
|
131
150
|
|
|
132
|
-
<nav class="header__menu" id="header-menu">
|
|
151
|
+
<nav class:list={["header__menu", header__menu__class]} id="header-menu">
|
|
133
152
|
<ul class="menu">
|
|
134
153
|
{
|
|
135
|
-
showHomeLink && pagePathname !==
|
|
136
|
-
<li class="header__item">
|
|
137
|
-
<a
|
|
154
|
+
showHomeLink && pagePathname !== homeUrl && (
|
|
155
|
+
<li class:list={["header__item", header__item__class]}>
|
|
156
|
+
<a
|
|
157
|
+
class:list={["menu__link", menu__link__class]}
|
|
158
|
+
href={homeUrl}
|
|
159
|
+
data-astro-prefetch="hover"
|
|
160
|
+
>
|
|
138
161
|
{homeText}
|
|
139
162
|
</a>
|
|
140
163
|
</li>
|
|
@@ -142,16 +165,11 @@ const pagePathname = Astro.url.pathname;
|
|
|
142
165
|
}
|
|
143
166
|
{
|
|
144
167
|
menuItems.map((item: MenuItem) => (
|
|
145
|
-
<li class="menu__item">
|
|
146
|
-
<a class="menu__link" href={item.link}>
|
|
168
|
+
<li class:list={["menu__item", header__item__class]}>
|
|
169
|
+
<a class:list={["menu__link", menu__link__class]} href={item.link}>
|
|
147
170
|
{item.text}
|
|
148
171
|
{item.submenu && (
|
|
149
|
-
<
|
|
150
|
-
class="iconify-arrow"
|
|
151
|
-
icon="codicon:triangle-down"
|
|
152
|
-
width="15"
|
|
153
|
-
height="15"
|
|
154
|
-
/>
|
|
172
|
+
<ChevronIcon class="arrow-down" width={15} height={15} />
|
|
155
173
|
)}
|
|
156
174
|
</a>
|
|
157
175
|
{item.submenu && (
|
|
@@ -161,11 +179,10 @@ const pagePathname = Astro.url.pathname;
|
|
|
161
179
|
<a href={sub.link}>
|
|
162
180
|
{sub.text}
|
|
163
181
|
{sub.submenu && sub.submenu.length > 0 && (
|
|
164
|
-
<
|
|
165
|
-
class="
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
height="15"
|
|
182
|
+
<ChevronIcon
|
|
183
|
+
class="ChevronIcon-arrow"
|
|
184
|
+
width={15}
|
|
185
|
+
height={15}
|
|
169
186
|
/>
|
|
170
187
|
)}
|
|
171
188
|
</a>
|
|
@@ -239,11 +256,11 @@ const pagePathname = Astro.url.pathname;
|
|
|
239
256
|
}
|
|
240
257
|
}
|
|
241
258
|
|
|
242
|
-
.
|
|
259
|
+
.ChevronIcon-arrow {
|
|
243
260
|
transition: transform 0.3s linear;
|
|
244
261
|
}
|
|
245
262
|
|
|
246
|
-
.menu__link:hover > .
|
|
263
|
+
.menu__link:hover > .ChevronIcon-arrow {
|
|
247
264
|
transform: rotate(180deg);
|
|
248
265
|
}
|
|
249
266
|
|
|
@@ -309,11 +326,12 @@ const pagePathname = Astro.url.pathname;
|
|
|
309
326
|
.submenu::after {
|
|
310
327
|
content: "";
|
|
311
328
|
position: absolute;
|
|
312
|
-
top: -
|
|
329
|
+
top: -1.1em;
|
|
313
330
|
width: 100%;
|
|
314
331
|
z-index: -3;
|
|
315
332
|
pointer-events: none;
|
|
316
|
-
height:
|
|
333
|
+
height: 7em;
|
|
334
|
+
right: 0;
|
|
317
335
|
}
|
|
318
336
|
|
|
319
337
|
.subsubmenu {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
const props = Astro.props;
|
|
3
|
+
|
|
4
|
+
const viewBox = "0 0 24 24";
|
|
5
|
+
const content = `<g class="gutruu"><path fill-opacity="0" class="xkoryc"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.4s" values="34;0"/><animate fill="freeze" attributeName="fill-opacity" begin="0.5s" dur="0.4s" to="1"/></path><g class="ft5dvn"><path d="M12 21v1M21 12h1M12 3v-1M3 12h-1" opacity="0"><animateTransform attributeName="transform" dur="30s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/><set fill="freeze" attributeName="opacity" begin="1s" to="1"/><animate fill="freeze" attributeName="d" begin="1s" dur="0.2s" values="M12 19v1M19 12h1M12 5v-1M5 12h-1;M12 21v1M21 12h1M12 3v-1M3 12h-1"/></path><path d="M18.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" opacity="0"><animateTransform attributeName="transform" dur="30s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/><set fill="freeze" attributeName="opacity" begin="1.2s" to="1"/><animate fill="freeze" attributeName="d" begin="1.2s" dur="0.2s" values="M17 17l0.5 0.5M17 7l0.5 -0.5M7 7l-0.5 -0.5M7 17l-0.5 0.5;M18.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"/></path></g></g>`;
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<svg
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
+
width="25px"
|
|
11
|
+
height="25px"
|
|
12
|
+
viewBox={viewBox}
|
|
13
|
+
{...props}
|
|
14
|
+
set:html={content}
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
<style is:inline>
|
|
18
|
+
.gutruu {
|
|
19
|
+
stroke: currentColor;
|
|
20
|
+
stroke-linecap: round;
|
|
21
|
+
stroke-linejoin: round;
|
|
22
|
+
stroke-width: var(--svg-stroke-width--2px, 2px);
|
|
23
|
+
}
|
|
24
|
+
.xkoryc {
|
|
25
|
+
fill: currentColor;
|
|
26
|
+
stroke-dasharray: 34;
|
|
27
|
+
d: path(
|
|
28
|
+
"M12 7c2.76 0 5 2.24 5 5c0 2.76 -2.24 5 -5 5c-2.76 0 -5 -2.24 -5 -5c0 -2.76 2.24 -5 5 -5Z"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
.ft5dvn {
|
|
32
|
+
fill: none;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
package/src/defaults.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DualThemeConfig } from "./index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in default theme tokens used by the Header component.
|
|
5
|
+
* These are merged with any user-supplied `theme` overrides.
|
|
6
|
+
*
|
|
7
|
+
* You can import this object if you want to build on top of the defaults
|
|
8
|
+
* rather than replacing them wholesale:
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { defaultThemes } from '@sofidevo/astro-dynamic-header/defaults';
|
|
13
|
+
* const theme = {
|
|
14
|
+
* light: { ...defaultThemes.light, accentColor: "#e11d48" },
|
|
15
|
+
* dark: { ...defaultThemes.dark, accentColor: "#f43f5e" },
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export const defaultThemes: Required<DualThemeConfig> = {
|
|
20
|
+
light: {
|
|
21
|
+
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
|
22
|
+
backgroundColorOpaque: "rgb(255, 255, 255)",
|
|
23
|
+
backdropBlur: "blur(20px)",
|
|
24
|
+
zIndex: 10,
|
|
25
|
+
textColor: "#1a1a1a",
|
|
26
|
+
accentColor: "#3e1c71",
|
|
27
|
+
},
|
|
28
|
+
dark: {
|
|
29
|
+
backgroundColor: "#0d0d0dcc",
|
|
30
|
+
backgroundColorOpaque: "#0d0d0d",
|
|
31
|
+
backdropBlur: "blur(20px)",
|
|
32
|
+
zIndex: 10,
|
|
33
|
+
textColor: "#ffffff",
|
|
34
|
+
accentColor: "#00ffff",
|
|
35
|
+
},
|
|
36
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export { defaultThemes } from "./defaults.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents a menu item in the navigation.
|
|
@@ -46,87 +46,70 @@ export interface MenuItem {
|
|
|
46
46
|
submenu?: SecondaryMenuItem[];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* Configuration for the site logo.
|
|
51
|
-
*/
|
|
52
|
-
export interface LogoConfig {
|
|
53
|
-
/**
|
|
54
|
-
* The URL of the logo image.
|
|
55
|
-
* @example "/assets/logo.png"
|
|
56
|
-
* @default "/logo.png"
|
|
57
|
-
*/
|
|
58
|
-
src?: string;
|
|
59
|
-
/** Alternative text for the logo image */
|
|
60
|
-
alt?: string;
|
|
61
|
-
/**
|
|
62
|
-
* CSS width for the logo.
|
|
63
|
-
* @example "150px" or "4rem"
|
|
64
|
-
* @default "55px"
|
|
65
|
-
*/
|
|
66
|
-
width?: string;
|
|
67
|
-
/**
|
|
68
|
-
* Optional text to display next to the logo image.
|
|
69
|
-
* @example "My Awesome Site"
|
|
70
|
-
*/
|
|
71
|
-
text?: string;
|
|
72
|
-
/**
|
|
73
|
-
* CSS font-size for the logo text.
|
|
74
|
-
* @default "1em"
|
|
75
|
-
*/
|
|
76
|
-
textSize?: string;
|
|
77
|
-
/**
|
|
78
|
-
* CSS color for the logo text.
|
|
79
|
-
* If not provided, it will inherit from the theme's text color.
|
|
80
|
-
* @default "inherit"
|
|
81
|
-
*/
|
|
82
|
-
textColor?: string;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
49
|
/**
|
|
86
50
|
* Configuration for the main navigation.
|
|
87
51
|
*/
|
|
88
52
|
export interface NavConfig {
|
|
89
|
-
/**
|
|
53
|
+
/**
|
|
90
54
|
* The URL for the home link.
|
|
91
55
|
* @default "/"
|
|
92
56
|
*/
|
|
93
57
|
homeUrl?: string;
|
|
94
|
-
/**
|
|
58
|
+
/**
|
|
95
59
|
* Array of top-level menu items.
|
|
96
60
|
* @example [{ link: "/about", text: "About Us" }]
|
|
97
61
|
*/
|
|
98
62
|
menuItems?: MenuItem[];
|
|
63
|
+
/**
|
|
64
|
+
* Fine-grained class override for the desktop `<nav>` element.
|
|
65
|
+
* Use this when you want the class to live alongside the rest of the
|
|
66
|
+
* navigation configuration rather than in the top-level `classNames` prop.
|
|
67
|
+
* @example "flex gap-4"
|
|
68
|
+
*/
|
|
69
|
+
header__menu__class?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Fine-grained class override applied to every top-level `<li>` item
|
|
72
|
+
* in the desktop navigation.
|
|
73
|
+
* @example "px-2 py-1"
|
|
74
|
+
*/
|
|
75
|
+
header__item__class?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Fine-grained class override applied to every top-level `<a>` link
|
|
78
|
+
* in the desktop navigation.
|
|
79
|
+
* @example "hover:underline font-medium"
|
|
80
|
+
*/
|
|
81
|
+
menu__link__class?: string;
|
|
99
82
|
}
|
|
100
83
|
|
|
101
84
|
/**
|
|
102
85
|
* Individual theme settings for a specific state (light/dark).
|
|
103
86
|
*/
|
|
104
87
|
export interface ThemeConfig {
|
|
105
|
-
/**
|
|
88
|
+
/**
|
|
106
89
|
* Main background color. Supports hex, rgb, rgba, etc.
|
|
107
90
|
* @example "rgba(255, 255, 255, 0.9)"
|
|
108
91
|
*/
|
|
109
92
|
backgroundColor?: string;
|
|
110
|
-
/**
|
|
93
|
+
/**
|
|
111
94
|
* Solid background color for submenus and mobile panels to ensure readability.
|
|
112
95
|
* @example "#ffffff"
|
|
113
96
|
*/
|
|
114
97
|
backgroundColorOpaque?: string;
|
|
115
|
-
/**
|
|
98
|
+
/**
|
|
116
99
|
* CSS backdrop-filter blur value.
|
|
117
100
|
* @default "blur(20px)"
|
|
118
101
|
*/
|
|
119
102
|
backdropBlur?: string;
|
|
120
|
-
/**
|
|
103
|
+
/**
|
|
121
104
|
* CSS z-index for the header container.
|
|
122
105
|
* @default 10
|
|
123
106
|
*/
|
|
124
107
|
zIndex?: number;
|
|
125
|
-
/**
|
|
108
|
+
/**
|
|
126
109
|
* Primary text color for navigation and logo.
|
|
127
110
|
*/
|
|
128
111
|
textColor?: string;
|
|
129
|
-
/**
|
|
112
|
+
/**
|
|
130
113
|
* Color for highlights, active states, underscores, and small borders.
|
|
131
114
|
*/
|
|
132
115
|
accentColor?: string;
|
|
@@ -143,33 +126,50 @@ export interface DualThemeConfig {
|
|
|
143
126
|
}
|
|
144
127
|
|
|
145
128
|
/**
|
|
146
|
-
* Custom CSS class names for
|
|
129
|
+
* Custom CSS class names for high-level layout & appearance customization.
|
|
130
|
+
*
|
|
131
|
+
* These target the structural wrapper elements of the Header. For fine-grained
|
|
132
|
+
* control over individual nav links or the logo internals, use the nested
|
|
133
|
+
* `xxx__class` props inside the `navigation` or `logo` config objects instead.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```astro
|
|
137
|
+
* <Header classNames={{ header: "shadow-xl", container: "top-4 px-6" }} />
|
|
138
|
+
* ```
|
|
147
139
|
*/
|
|
148
|
-
export interface
|
|
149
|
-
/**
|
|
140
|
+
export interface HeaderClassNames {
|
|
141
|
+
/** Outermost fixed `<div>` that positions the header on the page. */
|
|
150
142
|
container?: string;
|
|
151
|
-
/**
|
|
143
|
+
/** Inner `<header>` element — best place for shadows, borders, transitions. */
|
|
152
144
|
header?: string;
|
|
153
|
-
/**
|
|
145
|
+
/** Logo anchor `<a>` — add hover states or focus rings here. */
|
|
154
146
|
logo?: string;
|
|
155
|
-
/**
|
|
147
|
+
/** Logo text `<span>` — override typography here. */
|
|
156
148
|
logoText?: string;
|
|
157
|
-
/**
|
|
149
|
+
/** Desktop nav wrapper `<div>` — adjust spacing between logo and menu. */
|
|
158
150
|
nav?: string;
|
|
151
|
+
/** Mobile nav panel `<nav>` — add slide-in overrides or z-index tweaks. */
|
|
152
|
+
mobileNav?: string;
|
|
159
153
|
}
|
|
160
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @deprecated Use {@link HeaderClassNames} instead.
|
|
157
|
+
* Kept as an alias for backwards compatibility.
|
|
158
|
+
*/
|
|
159
|
+
export type CustomClassNames = HeaderClassNames;
|
|
160
|
+
|
|
161
161
|
/**
|
|
162
162
|
* Main properties for the Header component.
|
|
163
163
|
*/
|
|
164
164
|
export interface HeaderProps {
|
|
165
|
-
/**
|
|
165
|
+
/**
|
|
166
166
|
* Layout style.
|
|
167
167
|
* - "floating": Centered with max-width and rounded corners.
|
|
168
168
|
* - "fullscreen": Full width with no border radius.
|
|
169
169
|
* @default "floating"
|
|
170
170
|
*/
|
|
171
171
|
headerType?: "floating" | "fullscreen";
|
|
172
|
-
/**
|
|
172
|
+
/**
|
|
173
173
|
* Theme behavior.
|
|
174
174
|
* - "light": Force light mode.
|
|
175
175
|
* - "dark": Force dark mode.
|
|
@@ -177,32 +177,40 @@ export interface HeaderProps {
|
|
|
177
177
|
* @default "auto"
|
|
178
178
|
*/
|
|
179
179
|
preset?: "light" | "dark" | "auto";
|
|
180
|
-
|
|
181
|
-
logo?: LogoConfig;
|
|
180
|
+
|
|
182
181
|
/** Navigation links and structure. */
|
|
183
182
|
navigation?: NavConfig;
|
|
184
|
-
/** Custom theme overrides. See @
|
|
183
|
+
/** Custom theme overrides. See {@link DualThemeConfig} */
|
|
185
184
|
theme?: DualThemeConfig;
|
|
186
|
-
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
185
|
+
/**
|
|
186
|
+
* High-level CSS class overrides for structural wrapper elements.
|
|
187
|
+
* For fine-grained nav/logo element classes, use the nested `xxx__class`
|
|
188
|
+
* props inside `navigation` or `logo` instead.
|
|
189
|
+
* @example { header: "shadow-lg", container: "top-4" }
|
|
189
190
|
*/
|
|
190
|
-
classNames?:
|
|
191
|
+
classNames?: HeaderClassNames;
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
export interface NavMenuProps {
|
|
194
|
-
|
|
195
|
+
type?: "floating" | "fullscreen";
|
|
196
|
+
menuItems?: MenuItem[];
|
|
195
197
|
showHomeLink?: boolean;
|
|
196
198
|
homeText?: string;
|
|
199
|
+
header__menu__class?: string;
|
|
200
|
+
header__item__class?: string;
|
|
201
|
+
menu__link__class?: string;
|
|
197
202
|
}
|
|
198
203
|
|
|
199
204
|
export interface MobileNavProps {
|
|
200
|
-
|
|
205
|
+
type?: "floating" | "fullscreen";
|
|
206
|
+
menuItems?: MenuItem[];
|
|
201
207
|
showHomeLink?: boolean;
|
|
202
208
|
homeText?: string;
|
|
209
|
+
mobileNav__class?: string;
|
|
203
210
|
accentColor?: string;
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
export interface HamburgerButtonProps {
|
|
207
214
|
color?: string;
|
|
208
215
|
}
|
|
216
|
+
|