Files
Genarrative/src/components/common/PlatformStatusDialog.tsx
T
kdletters 387a1c26e3 完成全量检查整改
清理已跟踪原始日志与个人本地配置
修复前后端有效门禁、测试和构建问题
补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束
收紧图片编辑器状态、附件与媒体引用测试
排除已下线旧创作入口测试并清理 warning
2026-07-17 16:56:46 +08:00

201 lines
5.2 KiB
TypeScript

import {
AlertCircle,
CheckCircle2,
Loader2,
XCircle,
} from 'lucide-react';
import type { ReactNode } from 'react';
import { PlatformActionButton } from './PlatformActionButton';
import type {
PlatformActionButtonSize,
PlatformActionButtonSurface,
PlatformActionButtonTone,
} from './platformActionButtonModel';
import { PlatformIconBadge } from './PlatformIconBadge';
import { UnifiedModal } from './UnifiedModal';
export type PlatformStatusDialogStatus =
| 'success'
| 'cancel'
| 'error'
| 'loading'
| 'confirming';
type PlatformStatusDialogAction = {
label: string;
onClick: () => void;
disabled?: boolean;
tone?: PlatformActionButtonTone;
surface?: PlatformActionButtonSurface;
size?: PlatformActionButtonSize;
fullWidth?: boolean;
className?: string;
};
type PlatformStatusDialogProps = {
open?: boolean;
status: PlatformStatusDialogStatus;
title: string;
description?: ReactNode;
children?: ReactNode;
onClose: () => void;
action?: PlatformStatusDialogAction;
showHeader?: boolean;
showBodyTitle?: boolean;
showCloseButton?: boolean;
closeOnBackdrop?: boolean;
closeOnEscape?: boolean;
closeLabel?: string;
closeDisabled?: boolean;
zIndexClassName?: string;
overlayClassName?: string;
panelClassName?: string;
bodyClassName?: string;
iconClassName?: string;
icon?: ReactNode;
iconLabel?: string;
};
type PlatformStatusVisualConfig = {
icon: ReactNode;
iconClassName: string;
};
const DEFAULT_OVERLAY_CLASS =
'platform-theme platform-theme--light platform-profile-modal-overlay bg-slate-950/72 backdrop-blur-xl';
const DEFAULT_PANEL_CLASS =
'platform-remap-surface !max-w-sm rounded-[1.4rem]';
const DEFAULT_BODY_CLASS = 'px-5 pb-5 pt-6 text-center';
function joinClassNames(...classNames: Array<string | undefined>) {
return classNames.filter(Boolean).join(' ');
}
function getStatusVisualConfig(
status: PlatformStatusDialogStatus,
): PlatformStatusVisualConfig {
switch (status) {
case 'success':
return {
icon: <CheckCircle2 className="h-8 w-8" aria-hidden="true" />,
iconClassName: 'text-[var(--platform-success-text)]',
};
case 'cancel':
return {
icon: <XCircle className="h-8 w-8" aria-hidden="true" />,
iconClassName: 'text-[var(--platform-text-soft)]',
};
case 'error':
return {
icon: <AlertCircle className="h-8 w-8" aria-hidden="true" />,
iconClassName: 'text-[var(--platform-button-danger-text)]',
};
case 'loading':
case 'confirming':
return {
icon: (
<Loader2 className="h-8 w-8 animate-spin" aria-hidden="true" />
),
iconClassName: 'text-[var(--platform-accent)]',
};
}
}
/**
* 平台状态弹窗。
* 收口个人中心这类“状态图标 + 标题正文 + 可选主动作”的无头弹窗模式。
*/
export function PlatformStatusDialog({
open = true,
status,
title,
description,
children,
onClose,
action,
showHeader = false,
showBodyTitle,
showCloseButton = false,
closeOnBackdrop = false,
closeOnEscape = false,
closeLabel,
closeDisabled = false,
zIndexClassName = 'z-[90]',
overlayClassName,
panelClassName = DEFAULT_PANEL_CLASS,
bodyClassName = DEFAULT_BODY_CLASS,
iconClassName,
icon,
iconLabel,
}: PlatformStatusDialogProps) {
const visualConfig = getStatusVisualConfig(status);
const badgeIcon = icon ?? visualConfig.icon;
const shouldRenderBodyTitle = showBodyTitle ?? !showHeader;
return (
<UnifiedModal
open={open}
title={title}
onClose={onClose}
showHeader={showHeader}
showCloseButton={showCloseButton}
closeLabel={closeLabel}
closeDisabled={closeDisabled}
closeOnBackdrop={closeOnBackdrop}
closeOnEscape={closeOnEscape}
portal={false}
size="sm"
zIndexClassName={zIndexClassName}
overlayClassName={joinClassNames(
DEFAULT_OVERLAY_CLASS,
overlayClassName,
)}
panelClassName={panelClassName}
bodyClassName={bodyClassName}
>
<PlatformIconBadge
icon={badgeIcon}
label={iconLabel}
size="xl"
tone="neutral"
className={[
'mx-auto bg-white/10',
visualConfig.iconClassName,
iconClassName,
]
.filter(Boolean)
.join(' ')}
/>
{shouldRenderBodyTitle ? (
<div className="mt-4 text-xl font-black text-[var(--platform-text-strong)]">
{title}
</div>
) : null}
{children ? (
<div className="mt-3 text-sm font-semibold leading-6 text-[var(--platform-text-base)]">
{children}
</div>
) : null}
{description ? (
<div className="mt-3 text-sm font-semibold leading-6 text-[var(--platform-text-soft)]">
{description}
</div>
) : null}
{action ? (
<PlatformActionButton
fullWidth={action.fullWidth ?? true}
size={action.size ?? 'md'}
tone={action.tone}
surface={action.surface ?? 'profile'}
className={['mt-5', action.className].filter(Boolean).join(' ')}
onClick={action.onClick}
disabled={action.disabled}
>
{action.label}
</PlatformActionButton>
) : null}
</UnifiedModal>
);
}