110 lines
3.7 KiB
TypeScript
110 lines
3.7 KiB
TypeScript
import { Button, Card, CardGroup } from '@douyinfe/semi-ui';
|
||
import type { ReactNode } from 'react';
|
||
|
||
export type WorkspaceMetricRailItem = {
|
||
label: string;
|
||
value: ReactNode;
|
||
note: string;
|
||
className?: string;
|
||
};
|
||
|
||
export type WorkspaceQueueMetricRailItem = {
|
||
label: string;
|
||
value: ReactNode;
|
||
note?: ReactNode;
|
||
tone?: 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
|
||
emphasis?: 'primary' | 'secondary';
|
||
active?: boolean;
|
||
ariaLabel?: string;
|
||
onClick?: () => void;
|
||
};
|
||
|
||
type WorkspaceSummaryMetricRailProps = {
|
||
ariaLabel: string;
|
||
primary: WorkspaceMetricRailItem;
|
||
secondary: WorkspaceMetricRailItem[];
|
||
className: string;
|
||
cardClassName: string;
|
||
secondaryClassName: string;
|
||
primaryValueClassName?: string;
|
||
};
|
||
|
||
type WorkspaceQueueMetricRailProps = {
|
||
variant: 'queue';
|
||
ariaLabel: string;
|
||
className: string;
|
||
items: WorkspaceQueueMetricRailItem[];
|
||
context?: ReactNode;
|
||
};
|
||
|
||
export function WorkspaceMetricRail(props: WorkspaceSummaryMetricRailProps | WorkspaceQueueMetricRailProps) {
|
||
if ('items' in props) {
|
||
const hasContext = props.context != null;
|
||
return <Card className={`v2-workspace-metric-rail is-queue${hasContext ? ' has-context' : ''} ${props.className}`} bodyStyle={{ padding: 0 }}>
|
||
<div className="v2-workspace-metric-list" role="list" aria-label={props.ariaLabel}>
|
||
{props.items.map((item, index) => {
|
||
const toneClassName = `is-${item.tone ?? 'neutral'}`;
|
||
const emphasisClassName = `is-${item.emphasis ?? (index < 2 ? 'primary' : 'secondary')}`;
|
||
const classes = [
|
||
toneClassName,
|
||
emphasisClassName !== toneClassName ? emphasisClassName : '',
|
||
item.onClick ? 'is-action' : '',
|
||
item.active ? 'is-active' : ''
|
||
].filter(Boolean).join(' ');
|
||
const content = <>
|
||
<small>{item.label}</small>
|
||
<strong>{item.value}</strong>
|
||
{item.note ? <em>{item.note}</em> : null}
|
||
</>;
|
||
return <span
|
||
className={classes}
|
||
role="listitem"
|
||
key={item.label}
|
||
>
|
||
{item.onClick ? <Button
|
||
className="v2-workspace-metric-action"
|
||
theme="borderless"
|
||
type="tertiary"
|
||
htmlType="button"
|
||
aria-label={item.ariaLabel}
|
||
aria-pressed={item.active}
|
||
onClick={item.onClick}
|
||
>{content}</Button> : content}
|
||
</span>;
|
||
})}
|
||
</div>
|
||
{hasContext ? <div className="v2-workspace-metric-context">{props.context}</div> : null}
|
||
</Card>;
|
||
}
|
||
|
||
const {
|
||
ariaLabel,
|
||
primary,
|
||
secondary,
|
||
className,
|
||
cardClassName,
|
||
secondaryClassName,
|
||
primaryValueClassName
|
||
} = props;
|
||
const cardClass = (item: WorkspaceMetricRailItem, primaryCard = false) => [
|
||
cardClassName,
|
||
primaryCard ? 'is-primary' : '',
|
||
item.className ?? ''
|
||
].filter(Boolean).join(' ');
|
||
|
||
return <Card className={className} aria-label={ariaLabel} bodyStyle={{ padding: 0 }}>
|
||
<Card className={cardClass(primary, true)} bodyStyle={{ padding: 0 }} aria-label={`${primary.label}:${primary.value};${primary.note}`}>
|
||
<small>{primary.label}</small>
|
||
<strong className={primaryValueClassName}>{primary.value}</strong>
|
||
<span title={primary.note}>{primary.note}</span>
|
||
</Card>
|
||
<CardGroup className={secondaryClassName} type="grid" spacing={0}>
|
||
{secondary.map((item) => <Card className={cardClass(item)} bodyStyle={{ padding: 0 }} aria-label={`${item.label}:${item.value};${item.note}`} key={item.label}>
|
||
<small>{item.label}</small>
|
||
<strong>{item.value}</strong>
|
||
<span title={item.note}>{item.note}</span>
|
||
</Card>)}
|
||
</CardGroup>
|
||
</Card>;
|
||
}
|