mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
112 lines
4.1 KiB
TypeScript
112 lines
4.1 KiB
TypeScript
import type { FC, MouseEventHandler, ReactNode } from "react";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { faChevronDown, faArrowUpRightFromSquare } from "@fortawesome/pro-duotone-svg-icons";
|
|
import { Link as AriaLink } from "react-aria-components";
|
|
import { Badge } from "@/components/base/badges/badges";
|
|
import { cx, sortCx } from "@/utils/cx";
|
|
|
|
const styles = sortCx({
|
|
root: "group relative flex w-full cursor-pointer items-center rounded-md outline-focus-ring transition duration-100 ease-linear select-none focus-visible:z-10 focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
rootSelected: "bg-sidebar-active hover:bg-sidebar-active border-l-2 border-l-brand-600",
|
|
});
|
|
|
|
interface NavItemBaseProps {
|
|
/** Whether the nav item shows only an icon. */
|
|
iconOnly?: boolean;
|
|
/** Whether the collapsible nav item is open. */
|
|
open?: boolean;
|
|
/** URL to navigate to when the nav item is clicked. */
|
|
href?: string;
|
|
/** Type of the nav item. */
|
|
type: "link" | "collapsible" | "collapsible-child";
|
|
/** Icon component to display. */
|
|
icon?: FC<Record<string, any>>;
|
|
/** Badge to display. */
|
|
badge?: ReactNode;
|
|
/** Whether the nav item is currently active. */
|
|
current?: boolean;
|
|
/** Whether to truncate the label text. */
|
|
truncate?: boolean;
|
|
/** Handler for click events. */
|
|
onClick?: MouseEventHandler;
|
|
/** Content to display. */
|
|
children?: ReactNode;
|
|
}
|
|
|
|
export const NavItemBase = ({ current, type, badge, href, icon: Icon, children, truncate = true, onClick }: NavItemBaseProps) => {
|
|
const iconElement = Icon && <Icon aria-hidden="true" className="mr-2 size-5 shrink-0 text-fg-quaternary transition-inherit-all" />;
|
|
|
|
const badgeElement =
|
|
badge && (typeof badge === "string" || typeof badge === "number") ? (
|
|
<Badge className="ml-3" color="gray" type="pill-color" size="sm">
|
|
{badge}
|
|
</Badge>
|
|
) : (
|
|
badge
|
|
);
|
|
|
|
const labelElement = (
|
|
<span
|
|
className={cx(
|
|
"flex-1 text-md font-semibold text-white transition-inherit-all",
|
|
truncate && "truncate",
|
|
current ? "text-sidebar-active" : "group-hover:text-sidebar-hover",
|
|
)}
|
|
>
|
|
{children}
|
|
</span>
|
|
);
|
|
|
|
const isExternal = href && href.startsWith("http");
|
|
const externalIcon = isExternal && <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="size-4 text-fg-quaternary" />;
|
|
|
|
if (type === "collapsible") {
|
|
return (
|
|
<summary
|
|
className={cx("px-3 py-2 bg-sidebar", !current && "hover:bg-sidebar-hover", styles.root, current && styles.rootSelected)}
|
|
onClick={onClick}>
|
|
{iconElement}
|
|
|
|
{labelElement}
|
|
|
|
{badgeElement}
|
|
|
|
<FontAwesomeIcon icon={faChevronDown} aria-hidden="true" className="ml-3 size-4 shrink-0 text-fg-quaternary in-open:-scale-y-100" />
|
|
</summary>
|
|
);
|
|
}
|
|
|
|
if (type === "collapsible-child") {
|
|
return (
|
|
<AriaLink
|
|
href={href!}
|
|
target={isExternal ? "_blank" : "_self"}
|
|
rel="noopener noreferrer"
|
|
className={cx("py-2 pr-3 pl-10 bg-sidebar", !current && "hover:bg-sidebar-hover", styles.root, current && styles.rootSelected)}
|
|
onClick={onClick}
|
|
aria-current={current ? "page" : undefined}
|
|
>
|
|
{labelElement}
|
|
{externalIcon}
|
|
{badgeElement}
|
|
</AriaLink>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<AriaLink
|
|
href={href!}
|
|
target={isExternal ? "_blank" : "_self"}
|
|
rel="noopener noreferrer"
|
|
className={cx("px-3 py-2 bg-sidebar", !current && "hover:bg-sidebar-hover", styles.root, current && styles.rootSelected)}
|
|
onClick={onClick}
|
|
aria-current={current ? "page" : undefined}
|
|
>
|
|
{iconElement}
|
|
{labelElement}
|
|
{externalIcon}
|
|
{badgeElement}
|
|
</AriaLink>
|
|
);
|
|
};
|