Files
NODEDC_DESIGN_GUIDELINE/packages/ui-react/src/AppHeader.tsx
T

116 lines
3.4 KiB
TypeScript

import type { ButtonHTMLAttributes, ReactNode } from "react";
export interface AppHeaderProps {
brand: ReactNode;
brandHref?: string;
brandLabel?: string;
/** Opt in only for a light, single-color mark, never a full-color image. */
brandMonochrome?: boolean;
left?: ReactNode;
center?: ReactNode;
right?: ReactNode;
}
export function AppHeader({
brand,
brandHref,
brandLabel = "NODE.DC",
brandMonochrome = false,
left,
center,
right,
}: AppHeaderProps) {
const brandNode = brandHref ? (
<a className="nodedc-header__brand" data-monochrome={brandMonochrome || undefined} href={brandHref} aria-label={brandLabel}>{brand}</a>
) : (
<span className="nodedc-header__brand" data-monochrome={brandMonochrome || undefined} aria-label={brandLabel}>{brand}</span>
);
return (
<header className="nodedc-header-shell" data-fixed="true" data-preset="launcher">
<div className="nodedc-header">
<div className="nodedc-header__row">
<div className="nodedc-header__left">{brandNode}{left}</div>
<div className="nodedc-header__center">{center}</div>
<div className="nodedc-header__right">{right}</div>
</div>
</div>
</header>
);
}
export interface HeaderNavigationItem<T extends string> {
value: T;
label: ReactNode;
disabled?: boolean;
}
export interface HeaderNavigationProps<T extends string> {
label: string;
value?: T;
items: readonly HeaderNavigationItem<T>[];
onChange: (value: T) => void;
}
export function HeaderNavigation<T extends string>({ label, value, items, onChange }: HeaderNavigationProps<T>) {
return (
<nav className="nodedc-header-navigation" aria-label={label}>
{items.map((item) => (
<button
key={item.value}
type="button"
className="nodedc-header__nav-item"
data-active={item.value === value ? "true" : undefined}
aria-current={item.value === value ? "page" : undefined}
disabled={item.disabled}
onClick={() => onChange(item.value)}
>
{item.label}
</button>
))}
</nav>
);
}
export interface HeaderProfileProps {
children: ReactNode;
}
export function HeaderProfile({ children }: HeaderProfileProps) {
return <div className="nodedc-header__profile">{children}</div>;
}
export interface HeaderProfileButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className" | "style"> {}
export function HeaderProfileButton({ children, type = "button", ...props }: HeaderProfileButtonProps) {
return <button type={type} className="nodedc-header__profile-button" {...props}>{children}</button>;
}
export interface HeaderAvatarProps {
label: string;
imageUrl?: string;
}
export function HeaderAvatar({ label, imageUrl }: HeaderAvatarProps) {
return (
<span className="nodedc-header__avatar" title={label}>
{imageUrl ? <img src={imageUrl} alt="" /> : label.slice(0, 2).toUpperCase()}
</span>
);
}
export interface HeaderWorkspaceProps {
label: string;
imageUrl?: string;
kind?: "mark" | "avatar";
monochrome?: boolean;
}
export function HeaderWorkspace({ label, imageUrl, kind = "mark", monochrome = false }: HeaderWorkspaceProps) {
return (
<span className="nodedc-header__workspace" data-kind={kind} data-monochrome={monochrome || undefined} title={label}>
{imageUrl ? <img src={imageUrl} alt="" /> : label.slice(0, 2).toUpperCase()}
</span>
);
}