import cx from 'clsx';
import * as React from 'react';
import { Search } from '~/components/shared/FeatherIcons';
import s from './PageHeader.module.scss';
/**
* 页面顶栏的一套零件,代理 / 连接 / 规则 / 日志四个页面共用,
* 样式和断点行为全在 PageHeader.module.scss 里。
*
* 每个零件都接 className,页面自己的模块可以往上叠特有的东西
* (比如连接页窄屏下要重排 order)。
*/
export function PageHeader({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
return ;
}
export function HeaderTitle({ children }: { children: React.ReactNode }) {
return
{children}
;
}
/** 窄屏下强制换行,让它后面的东西独占下一行 */
export function HeaderRowBreak({ className }: { className?: string }) {
return ;
}
export function HeaderTabs({
label,
children,
className,
}: {
label: string;
children: React.ReactNode;
className?: string;
}) {
return (
{children}
);
}
/** 计数超过三位就收成 999+,否则标签宽度会跟着数字跳 */
function formatQty(n: number) {
return n < 1000 ? String(n) : '999+';
}
export function HeaderTab({
active,
label,
count,
onClick,
}: {
active: boolean;
label: string;
/** 省略则不显示计数 */
count?: number;
onClick: () => void;
}) {
return (
);
}
/**
* 搜索框。传 value/onChange 就是受控输入,传 children 则由调用方
* 自己塞输入组件(比如挂在 jotai atom 上的 TextFilter)。
*/
export function HeaderSearch({
placeholder,
value,
onChange,
children,
className,
}: {
placeholder?: string;
value?: string;
onChange?: (value: string) => void;
children?: React.ReactNode;
className?: string;
}) {
return (
{children ?? (
onChange?.(e.target.value)}
/>
)}
);
}
export function HeaderActions({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
return {children}
;
}
type ButtonVariant = 'ghost' | 'primary' | 'danger' | 'paused';
const variantClass: Record = {
ghost: s.btnGhost,
primary: s.btnPrimary,
danger: s.btnDanger,
paused: s.btnPaused,
};
export function HeaderButton({
variant = 'ghost',
icon,
label,
/** 悬停提示,省略则用 label。展开说明按钮作用时才需要单独给 */
title,
/**
* 文字收起的宽度门槛。'sm' 只在窄屏收(默认,适合主要操作),
* 'md' 中等宽度就收(次要操作,给主要操作腾地方)
*/
hideLabelAt = 'sm',
busy,
disabled,
onClick,
}: {
variant?: ButtonVariant;
icon: React.ReactNode;
label: string;
title?: string;
hideLabelAt?: 'sm' | 'md';
busy?: boolean;
disabled?: boolean;
onClick: () => void;
}) {
return (
);
}
export function HeaderIconButton({
icon,
label,
active,
expanded,
onClick,
}: {
icon: React.ReactNode;
label: string;
active?: boolean;
expanded?: boolean;
onClick: () => void;
}) {
return (
);
}
/** 顶栏里的