Skip to content

Commit e111091

Browse files
feat: change mobile navbar to bottom nav (#1610)
1 parent 1685702 commit e111091

File tree

1 file changed

+94
-88
lines changed

1 file changed

+94
-88
lines changed

src/components/Header.tsx

Lines changed: 94 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,36 @@ import {
22
IconFileStack,
33
IconGlobe,
44
IconHome,
5-
IconMenu,
65
IconNetwork,
76
IconPalette,
87
IconRuler,
98
IconSettings,
109
} from '@tabler/icons-solidjs'
11-
import type { ParentComponent } from 'solid-js'
10+
import type { JSX, ParentComponent } from 'solid-js'
1211
import { LogoText } from '~/components'
1312
import { ROUTES, themes } from '~/constants'
1413
import { useI18n } from '~/i18n'
1514
import { setCurTheme } from '~/signals'
1615

16+
interface NavItem {
17+
href: string
18+
name: string
19+
icon: JSX.Element
20+
}
21+
22+
const useNavItems = () => {
23+
const [t] = useI18n()
24+
25+
return [
26+
{ href: ROUTES.Overview, name: t('overview'), icon: <IconHome /> },
27+
{ href: ROUTES.Proxies, name: t('proxies'), icon: <IconGlobe /> },
28+
{ href: ROUTES.Rules, name: t('rules'), icon: <IconRuler /> },
29+
{ href: ROUTES.Conns, name: t('connections'), icon: <IconNetwork /> },
30+
{ href: ROUTES.Log, name: t('logs'), icon: <IconFileStack /> },
31+
{ href: ROUTES.Config, name: t('config'), icon: <IconSettings /> },
32+
] as NavItem[]
33+
}
34+
1735
const Nav: ParentComponent<{ href: string; tooltip: string }> = ({
1836
href,
1937
tooltip,
@@ -59,102 +77,90 @@ const ThemeSwitcher = () => (
5977
</div>
6078
)
6179

62-
export const Header = () => {
63-
const [t] = useI18n()
64-
const navs = () => [
65-
{
66-
href: ROUTES.Overview,
67-
name: t('overview'),
68-
icon: <IconHome />,
69-
},
70-
{
71-
href: ROUTES.Proxies,
72-
name: t('proxies'),
73-
icon: <IconGlobe />,
74-
},
75-
{
76-
href: ROUTES.Rules,
77-
name: t('rules'),
78-
icon: <IconRuler />,
79-
},
80-
{
81-
href: ROUTES.Conns,
82-
name: t('connections'),
83-
icon: <IconNetwork />,
84-
},
85-
{
86-
href: ROUTES.Log,
87-
name: t('logs'),
88-
icon: <IconFileStack />,
89-
},
90-
{
91-
href: ROUTES.Config,
92-
name: t('config'),
93-
icon: <IconSettings />,
94-
},
95-
]
96-
80+
const MobileBottomNav = () => {
81+
const navs = useNavItems()
9782
const location = useLocation()
9883

99-
const [openedDrawer, setOpenedDrawer] = createSignal(false)
84+
createEffect(() => {
85+
const shouldShow = location.pathname !== ROUTES.Setup
86+
document.body.style.paddingBottom =
87+
shouldShow && window.innerWidth < 1024 ? '4rem' : '0'
88+
})
89+
90+
onCleanup(() => {
91+
document.body.style.paddingBottom = '0'
92+
})
93+
94+
return (
95+
<Show when={location.pathname !== ROUTES.Setup}>
96+
<nav class="fixed inset-x-0 bottom-0 z-50 border-t border-base-content/10 bg-base-300/95 backdrop-blur-sm lg:hidden">
97+
<div class="grid h-16 grid-cols-6">
98+
<For each={navs}>
99+
{({ href, name, icon }) => {
100+
const isActive = () => location.pathname === href
101+
102+
return (
103+
<A
104+
href={href}
105+
class="relative flex flex-col items-center justify-center gap-1 transition-all duration-200 hover:bg-base-200/50"
106+
>
107+
<Show when={isActive()}>
108+
<div class="absolute top-0 left-1/2 h-1 w-8 -translate-x-1/2 transform rounded-b-full bg-primary" />
109+
</Show>
110+
<div
111+
class="text-xl transition-all duration-200"
112+
classList={{
113+
'text-primary scale-110': isActive(),
114+
'text-base-content/70': !isActive(),
115+
}}
116+
>
117+
{icon}
118+
</div>
119+
<Show when={isActive()}>
120+
<span class="animate-in fade-in slide-in-from-bottom-1 text-xs font-medium text-primary duration-200">
121+
{name}
122+
</span>
123+
</Show>
124+
</A>
125+
)
126+
}}
127+
</For>
128+
</div>
129+
</nav>
130+
</Show>
131+
)
132+
}
133+
134+
export const Header = () => {
135+
const navs = useNavItems()
136+
const location = useLocation()
100137

101138
return (
102-
<ul class="z-50 navbar flex w-auto items-center justify-center bg-base-300 px-4 shadow-lg">
103-
<div class="navbar-start gap-4">
104-
<div class="drawer w-auto lg:hidden">
105-
<input
106-
id="navs"
107-
type="checkbox"
108-
class="drawer-toggle"
109-
onChange={(e) => setOpenedDrawer(e.target.checked)}
110-
checked={openedDrawer()}
111-
/>
112-
113-
<div class="drawer-content flex w-6 items-center">
114-
<label for="navs" class="drawer-button btn btn-circle btn-sm">
115-
<IconMenu />
116-
</label>
117-
</div>
118-
119-
<div class="drawer-side">
120-
<label for="navs" class="drawer-overlay" />
121-
122-
<ul class="menu min-h-full min-w-2/5 gap-2 rounded-r-box bg-base-300 pt-20">
123-
<For each={navs()}>
139+
<>
140+
<header class="z-50 navbar flex w-auto items-center justify-center bg-base-300 px-4 shadow-lg">
141+
<div class="navbar-start">
142+
<LogoText />
143+
</div>
144+
145+
<Show when={location.pathname !== ROUTES.Setup}>
146+
<nav class="navbar-center hidden lg:flex">
147+
<ul class="menu menu-horizontal gap-2 menu-lg p-0">
148+
<For each={navs}>
124149
{({ href, name, icon }) => (
125-
<li onClick={() => setOpenedDrawer(false)}>
126-
<A href={href} activeClass="menu-active">
127-
{icon} {name}
128-
</A>
129-
</li>
150+
<Nav href={href} tooltip={name}>
151+
{icon}
152+
</Nav>
130153
)}
131154
</For>
132155
</ul>
133-
</div>
134-
</div>
135-
136-
<LogoText />
137-
</div>
138-
139-
<Show when={location.pathname !== ROUTES.Setup}>
140-
<div class="navbar-center hidden lg:flex">
141-
<ul class="menu menu-horizontal gap-2 menu-lg p-0">
142-
<For each={navs()}>
143-
{({ href, name, icon }) => (
144-
<Nav href={href} tooltip={name}>
145-
{icon}
146-
</Nav>
147-
)}
148-
</For>
149-
</ul>
150-
</div>
151-
</Show>
156+
</nav>
157+
</Show>
152158

153-
<div class="navbar-end">
154-
<div class="flex items-center gap-2">
159+
<div class="navbar-end">
155160
<ThemeSwitcher />
156161
</div>
157-
</div>
158-
</ul>
162+
</header>
163+
<MobileBottomNav />
164+
</>
159165
)
160166
}

0 commit comments

Comments
 (0)