feat: unify configuration side sheets
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
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';
|
||||
};
|
||||
|
||||
type WorkspaceSideSheetProps = {
|
||||
className?: string;
|
||||
visible: boolean;
|
||||
ariaLabel: string;
|
||||
closeLabel?: string;
|
||||
title: ReactNode;
|
||||
description: ReactNode;
|
||||
badge?: ReactNode;
|
||||
badgeColor?: 'blue' | 'green' | 'orange' | 'red' | 'grey';
|
||||
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 = '',
|
||||
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-config-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>;
|
||||
}
|
||||
Reference in New Issue
Block a user