master
1---
2import { navbarConfig, siteConfig } from '@/config';
3import { t } from '@utils/i18n';
4import { Icon } from 'astro-icon/components';
5import Button from './widgets/Button.astro';
6import DarkModeButton from './widgets/DarkModeButton.astro';
7
8interface Props {
9 title?: string;
10 lang?: string;
11}
12let { title } = Astro.props;
13if (!title) title = 'Astral Halo';
14
15const drawerItemClass =
16 'is-drawer-open:opacity-100 is-drawer-open:translate-x-0 translate-x-5 opacity-0 duration-150';
17---
18
19<div class="drawer drawer-end">
20 <input id="sidebar-drawer" type="checkbox" class="drawer-toggle" />
21 <div class="drawer-content flex flex-col">
22 <!-- Navbar -->
23 <div
24 id="navbar"
25 class="navbar bg-base-200/50 fixed z-20 flex h-16 w-full items-center backdrop-blur-md"
26 >
27 <div class="navbar-start">
28 <Button
29 id="site-name"
30 href="/"
31 class="group btn-ghost btn-accent not-hover:text-base-content"
32 >
33 <span class="text-xl font-bold duration-300 group-hover:opacity-0">{title}</span>
34 <Icon
35 name="material-symbols:home-rounded"
36 class="absolute text-3xl opacity-0 duration-300 group-hover:opacity-100"
37 />
38 </Button>
39 </div>
40 <nav class="join navbar-center max-md:hidden">
41 {
42 navbarConfig.navbarCenterItems.map((item) => {
43 if ('items' in item) {
44 return (
45 <div class="dropdown dropdown-center dropdown-hover">
46 <Button
47 title={item.title}
48 class="btn-ghost join-item btn-primary not-hover:text-base-content"
49 >
50 <span class="text-xl tracking-wide">{item.title}</span>
51 </Button>
52 <ul class="menu bg-base-200/50 dropdown-content absolute rounded-xl shadow">
53 {item.items.map((subItem) => (
54 <Button
55 {...('href' in subItem &&
56 subItem.href && {
57 href: subItem.href,
58 target: subItem.blank ? '_blank' : undefined,
59 })}
60 {...('onclick' in subItem &&
61 subItem.onclick &&
62 (typeof subItem.onclick === 'string'
63 ? { onclick: subItem.onclick }
64 : { id: 'nav-' + subItem.onclick.id }))}
65 {...(subItem.extraAttr || {})}
66 title={subItem.text}
67 aria-label={subItem.text}
68 class="btn-ghost btn-primary not-hover:text-base-content rounded-field"
69 >
70 <span class="text-xl tracking-wide text-nowrap">{subItem.text}</span>
71 </Button>
72 ))}
73 </ul>
74 </div>
75 );
76 } else {
77 return (
78 <Button
79 {...('href' in item &&
80 item.href && {
81 href: item.href,
82 target: item.blank ? '_blank' : undefined,
83 })}
84 {...('onclick' in item &&
85 item.onclick &&
86 (typeof item.onclick === 'string'
87 ? { onclick: item.onclick }
88 : { id: 'nav-' + item.onclick.id }))}
89 {...(item.extraAttr || {})}
90 title={item.text}
91 aria-label={item.text}
92 class="btn-ghost join-item btn-primary not-hover:text-base-content"
93 >
94 <span class="text-xl tracking-wide">{item.text}</span>
95 </Button>
96 );
97 }
98 })
99 }
100 </nav>
101 <div class="navbar-end">
102 <div class="flex max-md:hidden">
103 {
104 navbarConfig.navbarRightItems.onlyWide.map((item) => (
105 <Button
106 class="nav-menu-item btn-circle btn-ghost btn-primary not-hover:text-base-content"
107 {...('href' in item &&
108 item.href && { href: item.href, target: item.blank ? '_blank' : undefined })}
109 title={item.text}
110 aria-label={item.text}
111 {...('onclick' in item &&
112 item.onclick &&
113 (typeof item.onclick === 'string'
114 ? { onclick: item.onclick }
115 : { id: 'nav-' + item.onclick.id }))}
116 {...(item.extraAttr || {})}
117 >
118 <Icon name={item.icon} height="1.5rem" width="1.5rem" />
119 </Button>
120 ))
121 }
122 </div>
123 <div class="flex">
124 {
125 navbarConfig.navbarRightItems.always.map((item) => (
126 <Button
127 class="nav-menu-item btn-circle btn-ghost btn-primary not-hover:text-base-content"
128 {...('href' in item &&
129 item.href && { href: item.href, target: item.blank ? '_blank' : undefined })}
130 title={item.text}
131 aria-label={item.text}
132 {...('onclick' in item &&
133 item.onclick &&
134 (typeof item.onclick === 'string'
135 ? { onclick: item.onclick }
136 : { id: 'nav-' + item.onclick.id }))}
137 {...(item.extraAttr || {})}
138 >
139 <Icon name={item.icon} height="1.5rem" width="1.5rem" />
140 </Button>
141 ))
142 }
143 </div>
144 <div class="md:hidden">
145 <label
146 for="sidebar-drawer"
147 class="btn btn-circle btn-ghost btn-primary not-hover:text-base-content"
148 tabindex="0"
149 title={t.info.openMenu()}
150 aria-label={t.info.openMenu()}
151 >
152 <Icon name="material-symbols:menu-rounded" height="1.5rem" width="1.5rem" />
153 </label>
154 </div>
155 </div>
156 </div>
157 {siteConfig.banner === false && <div id="navbar-placeholder" class="pt-20" />}
158 <!-- Page Content -->
159 <slot />
160 </div>
161 <div class="drawer-side z-50">
162 <!-- Sidebar -->
163 <label
164 for="sidebar-drawer"
165 class="drawer-overlay"
166 title={t.info.closeMenu()}
167 aria-label={t.info.closeMenu()}></label>
168 <ul
169 role="navigation"
170 class="menu from-base-100 to-base-300 dark:from-base-300 dark:to-base-100 min-h-full w-[min(calc(100%-3rem),20rem)] bg-linear-150 p-4"
171 >
172 <li>
173 <DarkModeButton class="text-xl" showText={true} useDefaultBtnClass={false} />
174 </li>
175 <div class="divider text-lg font-bold">{t.navigation.menu()}</div>
176 {
177 navbarConfig.navbarCenterItems.map((item, index) => {
178 if ('items' in item) {
179 return (
180 <li class={drawerItemClass} style={`transition-delay: ${(index + 1) * 75}ms`}>
181 <details>
182 <summary class="text-xl">{item.title}</summary>
183 <ul>
184 {item.items.map((subItem) => (
185 <li>
186 <Button
187 {...('href' in subItem &&
188 subItem.href && {
189 href: subItem.href,
190 target: subItem.blank ? '_blank' : undefined,
191 })}
192 {...('onclick' in subItem &&
193 subItem.onclick &&
194 (typeof subItem.onclick === 'string'
195 ? { onclick: subItem.onclick }
196 : { id: 'side-' + subItem.onclick.id }))}
197 {...(subItem.extraAttr || {})}
198 title={subItem.text}
199 aria-label={subItem.text}
200 useDefaultClass={false}
201 >
202 <span class="text-xl">{subItem.text}</span>
203 </Button>
204 </li>
205 ))}
206 </ul>
207 </details>
208 </li>
209 );
210 } else {
211 return (
212 <li class={drawerItemClass} style={`transition-delay: ${(index + 1) * 75}ms`}>
213 <Button
214 {...('href' in item &&
215 item.href && {
216 href: item.href,
217 target: item.blank ? '_blank' : undefined,
218 })}
219 {...('onclick' in item &&
220 item.onclick &&
221 (typeof item.onclick === 'string'
222 ? { onclick: item.onclick }
223 : { id: 'side-' + item.onclick.id }))}
224 {...(item.extraAttr || {})}
225 title={item.text}
226 aria-label={item.text}
227 useDefaultClass={false}
228 >
229 <span class="text-xl">{item.text}</span>
230 </Button>
231 </li>
232 );
233 }
234 })
235 }
236 {
237 navbarConfig.navbarRightItems.onlyWide.map((item, index) => (
238 <li
239 class={drawerItemClass}
240 style={`transition-delay: ${(index + navbarConfig.navbarCenterItems.length + 1) * 75}ms`}
241 >
242 <Button
243 {...('href' in item &&
244 item.href && { href: item.href, target: item.blank ? '_blank' : undefined })}
245 title={item.text}
246 aria-label={item.text}
247 {...('onclick' in item &&
248 item.onclick &&
249 (typeof item.onclick === 'string'
250 ? { onclick: item.onclick }
251 : { id: 'side-' + item.onclick.id }))}
252 {...(item.extraAttr || {})}
253 useDefaultClass={false}
254 >
255 <Icon name={item.icon} height="1.5rem" width="1.5rem" />
256 <span class="text-xl">{item.text}</span>
257 </Button>
258 </li>
259 ))
260 }
261 </ul>
262 </div>
263</div>
264
265<script>
266 import { navbarConfig } from '@/config';
267 const rightItems = [
268 navbarConfig.navbarRightItems.onlyWide,
269 navbarConfig.navbarRightItems.always,
270 ].flat();
271
272 function setup() {
273 rightItems.forEach((item) => {
274 if ('onclick' in item && item.onclick && typeof item.onclick !== 'string') {
275 const navEl = document.getElementById('nav-' + item.onclick.id);
276 if (navEl) navEl.addEventListener('click', item.onclick.function);
277 const sideEl = document.getElementById('side-' + item.onclick.id);
278 if (sideEl) sideEl.addEventListener('click', item.onclick.function);
279 }
280 });
281 }
282
283 setup();
284 document.addEventListener('astro:page-load', setup);
285</script>