387a1c26e3
清理已跟踪原始日志与个人本地配置 修复前后端有效门禁、测试和构建问题 补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束 收紧图片编辑器状态、附件与媒体引用测试 排除已下线旧创作入口测试并清理 warning
201 lines
5.2 KiB
TypeScript
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>
|
|
);
|
|
}
|