feat: redesign header and footer
This commit is contained in:
parent
9734f0c704
commit
fd506ba474
@ -16,9 +16,10 @@ export default function Footer({ url, debug }: FooterProps) {
|
|||||||
<footer
|
<footer
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed bottom-0 left-0 z-40 w-full h-14',
|
'fixed bottom-0 left-0 z-40 w-full h-14',
|
||||||
'bg-ui-100 dark:bg-ui-900 text-ui-500',
|
'flex flex-col justify-center gap-1 shadow-inner',
|
||||||
'flex flex-col justify-center gap-1',
|
'bg-headplane-100 dark:bg-headplane-950',
|
||||||
'border-t border-ui-200 dark:border-ui-800',
|
'text-headplane-800 dark:text-headplane-200',
|
||||||
|
'dark:border-t dark:border-headplane-800',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<p className="container text-xs">
|
<p className="container text-xs">
|
||||||
@ -33,7 +34,7 @@ export default function Footer({ url, debug }: FooterProps) {
|
|||||||
</p>
|
</p>
|
||||||
<p className="container text-xs opacity-75">
|
<p className="container text-xs opacity-75">
|
||||||
Version: {__VERSION__}
|
Version: {__VERSION__}
|
||||||
{' | '}
|
{' — '}
|
||||||
Connecting to
|
Connecting to
|
||||||
{' '}
|
{' '}
|
||||||
<strong className="blur-xs hover:blur-none">
|
<strong className="blur-xs hover:blur-none">
|
||||||
|
|||||||
@ -2,19 +2,19 @@ import {
|
|||||||
GearIcon,
|
GearIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
PaperAirplaneIcon,
|
|
||||||
PeopleIcon,
|
PeopleIcon,
|
||||||
PersonIcon,
|
PersonIcon,
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
} from '@primer/octicons-react';
|
} from '@primer/octicons-react';
|
||||||
import { Form } from 'react-router';
|
import { CircleUser, PlaneTakeoff } from 'lucide-react';
|
||||||
|
import { Form, NavLink } from 'react-router';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
import { cn } from '~/utils/cn';
|
import { cn } from '~/utils/cn';
|
||||||
import type { HeadplaneContext } from '~/utils/config/headplane';
|
import type { HeadplaneContext } from '~/utils/config/headplane';
|
||||||
import type { SessionData } from '~/utils/sessions.server';
|
import type { SessionData } from '~/utils/sessions.server';
|
||||||
|
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
import TabLink from './TabLink';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
config: HeadplaneContext['config'];
|
config: HeadplaneContext['config'];
|
||||||
@ -24,19 +24,43 @@ interface Props {
|
|||||||
interface LinkProps {
|
interface LinkProps {
|
||||||
href: string;
|
href: string;
|
||||||
text: string;
|
text: string;
|
||||||
isMenu?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Link({ href, text, isMenu }: LinkProps) {
|
interface TabLinkProps {
|
||||||
|
name: string;
|
||||||
|
to: string;
|
||||||
|
icon: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabLink({ name, to, icon }: TabLinkProps) {
|
||||||
|
return (
|
||||||
|
<div className="relative py-2">
|
||||||
|
<NavLink
|
||||||
|
to={to}
|
||||||
|
prefetch="intent"
|
||||||
|
className={({ isActive }) =>
|
||||||
|
cn(
|
||||||
|
'px-3 py-2 flex items-center rounded-md text-nowrap gap-x-2',
|
||||||
|
'after:absolute after:bottom-0 after:left-3 after:right-3',
|
||||||
|
'after:h-0.5 after:bg-headplane-900 dark:after:bg-headplane-200',
|
||||||
|
'hover:bg-headplane-200 dark:hover:bg-headplane-900',
|
||||||
|
isActive ? 'after:visible' : 'after:invisible',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{icon} {name}
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Link({ href, text }: LinkProps) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={cn(
|
className="hidden sm:block hover:underline text-sm"
|
||||||
!isMenu &&
|
|
||||||
'text-ui-300 hover:text-ui-50 hover:underline hidden sm:block',
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</a>
|
</a>
|
||||||
@ -45,11 +69,16 @@ function Link({ href, text, isMenu }: LinkProps) {
|
|||||||
|
|
||||||
export default function Header(data: Props) {
|
export default function Header(data: Props) {
|
||||||
return (
|
return (
|
||||||
<header className="bg-main-700 dark:bg-main-800 text-ui-50">
|
<header className={cn(
|
||||||
|
'bg-headplane-100 dark:bg-headplane-950',
|
||||||
|
'text-headplane-800 dark:text-headplane-200',
|
||||||
|
'dark:border-b dark:border-headplane-800',
|
||||||
|
'shadow-inner',
|
||||||
|
)}>
|
||||||
<div className="container flex items-center justify-between py-4">
|
<div className="container flex items-center justify-between py-4">
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
<PaperAirplaneIcon className="w-6 h-6" />
|
<PlaneTakeoff />
|
||||||
<h1 className="text-2xl">Headplane</h1>
|
<h1 className="text-2xl font-semibold">headplane</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-x-4">
|
<div className="flex items-center gap-x-4">
|
||||||
<Link href="https://tailscale.com/download" text="Download" />
|
<Link href="https://tailscale.com/download" text="Download" />
|
||||||
@ -59,13 +88,11 @@ export default function Header(data: Props) {
|
|||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Button
|
<Menu.Button
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-full h-9 w-9',
|
'rounded-full h-8 w-8',
|
||||||
'border border-main-600 dark:border-main-700',
|
'hover:bg-headplane-200 dark:hover:bg-headplane-800',
|
||||||
'hover:bg-main-600 dark:hover:bg-main-700',
|
|
||||||
'flex items-center justify-center',
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<PersonIcon className="h-5 w-5 mt-0.5" />
|
<CircleUser className="w-full h-full" />
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
<Menu.Items>
|
<Menu.Items>
|
||||||
<Menu.Item className="text-right">
|
<Menu.Item className="text-right">
|
||||||
@ -105,7 +132,7 @@ export default function Header(data: Props) {
|
|||||||
) : undefined}
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav className="container flex items-center gap-x-4 overflow-x-auto">
|
<nav className="container flex items-center gap-x-4 overflow-x-auto font-semibold">
|
||||||
<TabLink
|
<TabLink
|
||||||
to="/machines"
|
to="/machines"
|
||||||
name="Machines"
|
name="Machines"
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
import { NavLink } from 'react-router';
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import { cn } from '~/utils/cn';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
name: string;
|
|
||||||
to: string;
|
|
||||||
icon: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TabLink({ name, to, icon }: Props) {
|
|
||||||
return (
|
|
||||||
<NavLink
|
|
||||||
to={to}
|
|
||||||
prefetch="intent"
|
|
||||||
className={({ isActive }) =>
|
|
||||||
cn(
|
|
||||||
'border-b-2 py-1.5',
|
|
||||||
isActive ? 'border-white' : 'border-transparent',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'flex items-center gap-x-2 px-2.5 py-1.5 text-md text-nowrap',
|
|
||||||
'hover:bg-ui-100/5 dark:hover:bg-ui-900/40 rounded-md',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{icon} {name}
|
|
||||||
</div>
|
|
||||||
</NavLink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user