Make application shell components package-native
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
|
||||
import { cn } from "./cn";
|
||||
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
||||
|
||||
export interface AppHeaderProps extends HTMLAttributes<HTMLElement> {
|
||||
export interface AppHeaderProps {
|
||||
brand: ReactNode;
|
||||
brandHref?: string;
|
||||
brandLabel?: string;
|
||||
fixed?: boolean;
|
||||
left?: ReactNode;
|
||||
center?: ReactNode;
|
||||
right?: ReactNode;
|
||||
@@ -15,12 +13,9 @@ export function AppHeader({
|
||||
brand,
|
||||
brandHref,
|
||||
brandLabel = "NODE.DC",
|
||||
fixed = true,
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
className,
|
||||
...props
|
||||
}: AppHeaderProps) {
|
||||
const brandNode = brandHref ? (
|
||||
<a className="nodedc-header__brand" href={brandHref} aria-label={brandLabel}>{brand}</a>
|
||||
@@ -29,45 +24,87 @@ export function AppHeader({
|
||||
);
|
||||
|
||||
return (
|
||||
<header className={cn("nodedc-header-shell", className)} data-fixed={fixed ? "true" : undefined} {...props}>
|
||||
<header className="nodedc-header-shell" data-fixed="true" data-preset="launcher">
|
||||
<div className="nodedc-header">
|
||||
<div className="nodedc-header__left">{brandNode}{left}</div>
|
||||
<div className="nodedc-header__center">{center}</div>
|
||||
<div className="nodedc-header__right">{right}</div>
|
||||
<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 function HeaderProfile({ children, className, ...props }: HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn("nodedc-header__profile", className)} {...props}>{children}</div>;
|
||||
export interface HeaderNavigationItem<T extends string> {
|
||||
value: T;
|
||||
label: ReactNode;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function HeaderProfileButton({ className, children, type = "button", ...props }: ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||
return <button type={type} className={cn("nodedc-header__profile-button", className)} {...props}>{children}</button>;
|
||||
export interface HeaderNavigationProps<T extends string> {
|
||||
label: string;
|
||||
value?: T;
|
||||
items: readonly HeaderNavigationItem<T>[];
|
||||
onChange: (value: T) => void;
|
||||
}
|
||||
|
||||
export interface HeaderAvatarProps extends HTMLAttributes<HTMLSpanElement> {
|
||||
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, className, ...props }: HeaderAvatarProps) {
|
||||
export function HeaderAvatar({ label, imageUrl }: HeaderAvatarProps) {
|
||||
return (
|
||||
<span className={cn("nodedc-header__avatar", className)} title={label} {...props}>
|
||||
<span className="nodedc-header__avatar" title={label}>
|
||||
{imageUrl ? <img src={imageUrl} alt="" /> : label.slice(0, 2).toUpperCase()}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export interface HeaderWorkspaceProps extends HTMLAttributes<HTMLSpanElement> {
|
||||
export interface HeaderWorkspaceProps {
|
||||
label: string;
|
||||
imageUrl?: string;
|
||||
kind?: "mark" | "avatar";
|
||||
}
|
||||
|
||||
export function HeaderWorkspace({ label, imageUrl, className, ...props }: HeaderWorkspaceProps) {
|
||||
export function HeaderWorkspace({ label, imageUrl, kind = "mark" }: HeaderWorkspaceProps) {
|
||||
return (
|
||||
<span className={cn("nodedc-header__workspace", className)} title={label} {...props}>
|
||||
<span className="nodedc-header__workspace" data-kind={kind} title={label}>
|
||||
{imageUrl ? <img src={imageUrl} alt="" /> : label.slice(0, 2).toUpperCase()}
|
||||
</span>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user