mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
chore: initial Untitled UI Vite scaffold with FontAwesome Pro
This commit is contained in:
202
src/components/application/app-navigation/header-navigation.tsx
Normal file
202
src/components/application/app-navigation/header-navigation.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import type { FC, ReactNode } from "react";
|
||||
import { Bell01, LifeBuoy01, SearchLg, Settings01 } from "@untitledui/icons";
|
||||
import { Button as AriaButton, DialogTrigger, Popover } from "react-aria-components";
|
||||
import { Avatar } from "@/components/base/avatar/avatar";
|
||||
import { BadgeWithDot } from "@/components/base/badges/badges";
|
||||
import { Input } from "@/components/base/input/input";
|
||||
import { UntitledLogo } from "@/components/foundations/logo/untitledui-logo";
|
||||
import { cx } from "@/utils/cx";
|
||||
import { MobileNavigationHeader } from "./base-components/mobile-header";
|
||||
import { NavAccountCard, NavAccountMenu } from "./base-components/nav-account-card";
|
||||
import { NavItemBase } from "./base-components/nav-item";
|
||||
import { NavItemButton } from "./base-components/nav-item-button";
|
||||
import { NavList } from "./base-components/nav-list";
|
||||
|
||||
type NavItem = {
|
||||
/** Label text for the nav item. */
|
||||
label: string;
|
||||
/** URL to navigate to when the nav item is clicked. */
|
||||
href: string;
|
||||
/** Whether the nav item is currently active. */
|
||||
current?: boolean;
|
||||
/** Icon component to display. */
|
||||
icon?: FC<{ className?: string }>;
|
||||
/** Badge to display. */
|
||||
badge?: ReactNode;
|
||||
/** List of sub-items to display. */
|
||||
items?: NavItem[];
|
||||
};
|
||||
|
||||
interface HeaderNavigationBaseProps {
|
||||
/** URL of the currently active item. */
|
||||
activeUrl?: string;
|
||||
/** List of items to display. */
|
||||
items: NavItem[];
|
||||
/** List of sub-items to display. */
|
||||
subItems?: NavItem[];
|
||||
/** Content to display in the trailing position. */
|
||||
trailingContent?: ReactNode;
|
||||
/** Whether to show the avatar dropdown. */
|
||||
showAvatarDropdown?: boolean;
|
||||
/** Whether to hide the bottom border. */
|
||||
hideBorder?: boolean;
|
||||
}
|
||||
|
||||
export const HeaderNavigationBase = ({
|
||||
activeUrl,
|
||||
items,
|
||||
subItems,
|
||||
trailingContent,
|
||||
showAvatarDropdown = true,
|
||||
hideBorder = false,
|
||||
}: HeaderNavigationBaseProps) => {
|
||||
const activeSubNavItems = subItems || items.find((item) => item.current && item.items && item.items.length > 0)?.items;
|
||||
|
||||
const showSecondaryNav = activeSubNavItems && activeSubNavItems.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MobileNavigationHeader>
|
||||
<aside className="flex h-full max-w-full flex-col justify-between overflow-auto border-r border-secondary bg-primary pt-4 lg:pt-6">
|
||||
<div className="flex flex-col gap-5 px-4 lg:px-5">
|
||||
<UntitledLogo className="h-8" />
|
||||
<Input shortcut size="sm" aria-label="Search" placeholder="Search" icon={SearchLg} />
|
||||
</div>
|
||||
|
||||
<NavList items={items} />
|
||||
|
||||
<div className="mt-auto flex flex-col gap-4 px-2 py-4 lg:px-4 lg:py-6">
|
||||
<div className="flex flex-col gap-1">
|
||||
<NavItemBase type="link" href="#" icon={LifeBuoy01}>
|
||||
Support
|
||||
</NavItemBase>
|
||||
<NavItemBase
|
||||
type="link"
|
||||
href="#"
|
||||
icon={Settings01}
|
||||
badge={
|
||||
<BadgeWithDot color="success" type="modern" size="sm">
|
||||
Online
|
||||
</BadgeWithDot>
|
||||
}
|
||||
>
|
||||
Settings
|
||||
</NavItemBase>
|
||||
<NavItemBase type="link" href="https://www.untitledui.com/" icon={Settings01}>
|
||||
Open in browser
|
||||
</NavItemBase>
|
||||
</div>
|
||||
|
||||
<NavAccountCard />
|
||||
</div>
|
||||
</aside>
|
||||
</MobileNavigationHeader>
|
||||
|
||||
<header className="max-lg:hidden">
|
||||
<section
|
||||
className={cx(
|
||||
"flex h-16 w-full items-center justify-center bg-primary md:h-18",
|
||||
(!hideBorder || showSecondaryNav) && "border-b border-secondary",
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full max-w-container justify-between pr-3 pl-4 md:px-8">
|
||||
<div className="flex flex-1 items-center gap-4">
|
||||
<a
|
||||
aria-label="Go to homepage"
|
||||
href="/"
|
||||
className="rounded-xs outline-focus-ring focus-visible:outline-2 focus-visible:outline-offset-2"
|
||||
>
|
||||
<UntitledLogo className="h-8" />
|
||||
</a>
|
||||
|
||||
<nav>
|
||||
<ul className="flex items-center gap-0.5">
|
||||
{items.map((item) => (
|
||||
<li key={item.label} className="py-0.5">
|
||||
<NavItemBase icon={item.icon} href={item.href} current={item.current} badge={item.badge} type="link">
|
||||
{item.label}
|
||||
</NavItemBase>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
{trailingContent}
|
||||
|
||||
<div className="flex gap-0.5">
|
||||
<NavItemButton
|
||||
current={activeUrl === "/settings-01"}
|
||||
size="md"
|
||||
icon={Settings01}
|
||||
label="Settings"
|
||||
href="/settings-01"
|
||||
tooltipPlacement="bottom"
|
||||
/>
|
||||
<NavItemButton
|
||||
current={activeUrl === "/notifications-01"}
|
||||
size="md"
|
||||
icon={Bell01}
|
||||
label="Notifications"
|
||||
href="/notifications-01"
|
||||
tooltipPlacement="bottom"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showAvatarDropdown && (
|
||||
<DialogTrigger>
|
||||
<AriaButton
|
||||
className={({ isPressed, isFocused }) =>
|
||||
cx(
|
||||
"group relative inline-flex cursor-pointer",
|
||||
(isPressed || isFocused) && "rounded-full outline-2 outline-offset-2 outline-focus-ring",
|
||||
)
|
||||
}
|
||||
>
|
||||
<Avatar alt="Olivia Rhye" src="https://www.untitledui.com/images/avatars/olivia-rhye?bg=%23E0E0E0" size="md" />
|
||||
</AriaButton>
|
||||
<Popover
|
||||
placement="bottom right"
|
||||
offset={8}
|
||||
className={({ isEntering, isExiting }) =>
|
||||
cx(
|
||||
"will-change-transform",
|
||||
isEntering &&
|
||||
"duration-300 ease-out animate-in fade-in placement-right:slide-in-from-left-2 placement-top:slide-in-from-bottom-2 placement-bottom:slide-in-from-top-2",
|
||||
isExiting &&
|
||||
"duration-150 ease-in animate-out fade-out placement-right:slide-out-to-left-2 placement-top:slide-out-to-bottom-2 placement-bottom:slide-out-to-top-2",
|
||||
)
|
||||
}
|
||||
>
|
||||
<NavAccountMenu />
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{showSecondaryNav && (
|
||||
<section className={cx("flex h-16 w-full items-center justify-center bg-primary", !hideBorder && "border-b border-secondary")}>
|
||||
<div className="flex w-full max-w-container items-center justify-between gap-8 px-8">
|
||||
<nav>
|
||||
<ul className="flex items-center gap-0.5">
|
||||
{activeSubNavItems.map((item) => (
|
||||
<li key={item.label} className="py-0.5">
|
||||
<NavItemBase icon={item.icon} href={item.href} current={item.current} badge={item.badge} type="link">
|
||||
{item.label}
|
||||
</NavItemBase>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<Input shortcut aria-label="Search" placeholder="Search" icon={SearchLg} size="sm" className="max-w-xs" />
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user