125 lines
4.1 KiB
TypeScript
125 lines
4.1 KiB
TypeScript
import { Button, SideSheet, Tag } from '@douyinfe/semi-ui';
|
|
import { useId, useLayoutEffect, type ReactNode } from 'react';
|
|
|
|
export type WorkspaceSideSheetAction = {
|
|
label: string;
|
|
onClick: () => void;
|
|
disabled?: boolean;
|
|
loading?: boolean;
|
|
theme?: 'solid' | 'light' | 'borderless';
|
|
type?: 'primary' | 'secondary' | 'tertiary' | 'warning' | 'danger';
|
|
};
|
|
|
|
export type WorkspaceSideSheetSummaryItem = {
|
|
label: string;
|
|
value: ReactNode;
|
|
detail?: ReactNode;
|
|
tone?: 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
|
|
};
|
|
|
|
export type WorkspaceSideSheetBadgeColor = 'blue' | 'green' | 'orange' | 'red' | 'grey';
|
|
|
|
export type WorkspaceSideSheetProps = {
|
|
className?: string;
|
|
variant?: 'config' | 'detail';
|
|
visible: boolean;
|
|
ariaLabel: string;
|
|
closeLabel?: string;
|
|
title: ReactNode;
|
|
description: ReactNode;
|
|
badge?: ReactNode;
|
|
badgeColor?: WorkspaceSideSheetBadgeColor;
|
|
placement?: 'left' | 'right' | 'top' | 'bottom';
|
|
width?: string | number;
|
|
height?: string | number;
|
|
summaryItems?: WorkspaceSideSheetSummaryItem[];
|
|
footerNote?: ReactNode;
|
|
secondaryActions?: WorkspaceSideSheetAction[];
|
|
primaryAction?: WorkspaceSideSheetAction;
|
|
onCancel: () => void;
|
|
children: ReactNode;
|
|
};
|
|
|
|
export function WorkspaceSideSheet({
|
|
className = '',
|
|
variant = 'config',
|
|
visible,
|
|
ariaLabel,
|
|
closeLabel = `关闭${ariaLabel}`,
|
|
title,
|
|
description,
|
|
badge,
|
|
badgeColor = 'blue',
|
|
placement = 'right',
|
|
width = 460,
|
|
height,
|
|
summaryItems = [],
|
|
footerNote,
|
|
secondaryActions = [],
|
|
primaryAction,
|
|
onCancel,
|
|
children
|
|
}: WorkspaceSideSheetProps) {
|
|
const sheetId = useId();
|
|
const sheetClassName = [`v2-workspace-${variant}-sidesheet`, className].filter(Boolean).join(' ');
|
|
const hasFooter = Boolean(footerNote || secondaryActions.length || primaryAction);
|
|
|
|
useLayoutEffect(() => {
|
|
if (!visible) return;
|
|
const apply = () => {
|
|
const dialog = document.querySelector(`[data-workspace-sheet-id="${sheetId}"] .semi-sidesheet-inner`);
|
|
dialog?.setAttribute('aria-label', ariaLabel);
|
|
dialog?.querySelector('.semi-sidesheet-close')?.setAttribute('aria-label', closeLabel);
|
|
};
|
|
apply();
|
|
const frame = window.requestAnimationFrame(apply);
|
|
return () => window.cancelAnimationFrame(frame);
|
|
}, [ariaLabel, closeLabel, sheetId, visible]);
|
|
|
|
return <SideSheet
|
|
className={sheetClassName}
|
|
data-workspace-sheet-id={sheetId}
|
|
visible={visible}
|
|
aria-label={ariaLabel}
|
|
placement={placement}
|
|
width={placement === 'left' || placement === 'right' ? width : undefined}
|
|
height={placement === 'top' || placement === 'bottom' ? height : undefined}
|
|
title={<div className="v2-workspace-config-title">
|
|
<span><strong>{title}</strong><small>{description}</small></span>
|
|
{badge ? <Tag color={badgeColor} type="light" size="small">{badge}</Tag> : null}
|
|
</div>}
|
|
onCancel={onCancel}
|
|
footer={hasFooter ? <div className="v2-workspace-config-footer">
|
|
<span>{footerNote}</span>
|
|
<div>
|
|
{secondaryActions.map((action) => <Button
|
|
key={action.label}
|
|
theme={action.theme ?? 'light'}
|
|
type={action.type}
|
|
disabled={action.disabled}
|
|
loading={action.loading}
|
|
onClick={action.onClick}
|
|
>{action.label}</Button>)}
|
|
{primaryAction ? <Button
|
|
theme={primaryAction.theme ?? 'solid'}
|
|
type={primaryAction.type ?? 'primary'}
|
|
disabled={primaryAction.disabled}
|
|
loading={primaryAction.loading}
|
|
onClick={primaryAction.onClick}
|
|
>{primaryAction.label}</Button> : null}
|
|
</div>
|
|
</div> : null}
|
|
>
|
|
<div className="v2-workspace-config-body">
|
|
{summaryItems.length ? <div className="v2-workspace-config-summary" role="list" aria-label={`${ariaLabel}摘要`}>
|
|
{summaryItems.map((item) => <span className={`is-${item.tone ?? 'neutral'}`} role="listitem" key={item.label}>
|
|
<small>{item.label}</small>
|
|
<strong>{item.value}</strong>
|
|
{item.detail ? <em>{item.detail}</em> : null}
|
|
</span>)}
|
|
</div> : null}
|
|
<div className="v2-workspace-config-content">{children}</div>
|
|
</div>
|
|
</SideSheet>;
|
|
}
|