Make application shell components package-native

This commit is contained in:
DCCONSTRUCTIONS
2026-07-10 12:41:12 +03:00
parent 9261d3af36
commit ebae662b52
52 changed files with 1036 additions and 185 deletions
+58 -21
View File
@@ -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>
);