抽取组件确认弹窗公共结构

统一替换与移除操作的确认弹窗布局

保留各操作独立文案与按钮样式
This commit is contained in:
2026-09-12 13:19:59 +08:00
parent 206b4830a1
commit ef04a19d5a
@@ -1,5 +1,5 @@
import { ChevronDown, ChevronRight, Plus, Trash2 } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { type ReactNode, useEffect, useRef, useState } from 'react';
import { ThemedModal } from '../../../../../components/modal/ThemedModal';
import type { Component } from '../../../../../features/ui-editor/types/Component';
@@ -139,64 +139,78 @@ export function ComponentPanel(props: ComponentPanelProps) {
</p>
)}
{error && <p className="m-0 text-[10px] text-red-600">{error}</p>}
<ThemedModal
<ConfirmationModal
open={replaceConfirmationOpen}
ariaLabel="确认替换组件"
title="确认替换组件?"
description="当前组件的配置将被新的默认组件覆盖。"
confirmLabel="替换"
confirmClassName="bg-blue-600"
onClose={() => setReplaceConfirmationOpen(false)}
panelClassName="w-full max-w-sm rounded-2xl p-5"
>
<h2 className="m-0 text-base font-semibold"></h2>
<p className="mt-3 text-sm text-(--platform-text-soft)">
</p>
<div className="mt-5 flex justify-end gap-2">
<button
type="button"
className="rounded-lg border border-(--platform-subpanel-border) px-3 py-2 text-xs"
onClick={() => setReplaceConfirmationOpen(false)}
>
</button>
<button
type="button"
className="rounded-lg bg-blue-600 px-3 py-2 text-xs font-semibold text-white"
onClick={applyReplacement}
>
</button>
</div>
</ThemedModal>
<ThemedModal
onConfirm={applyReplacement}
/>
<ConfirmationModal
open={removeConfirmationOpen}
ariaLabel="确认移除组件"
title="确认移除组件?"
description="当前组件配置将被移除。"
confirmLabel="移除"
confirmClassName="bg-red-600"
onClose={() => setRemoveConfirmationOpen(false)}
panelClassName="w-full max-w-sm rounded-2xl p-5"
>
<h2 className="m-0 text-base font-semibold"></h2>
<p className="mt-3 text-sm text-(--platform-text-soft)">
</p>
<div className="mt-5 flex justify-end gap-2">
<button
type="button"
className="rounded-lg border border-(--platform-subpanel-border) px-3 py-2 text-xs"
onClick={() => setRemoveConfirmationOpen(false)}
>
</button>
<button
type="button"
className="rounded-lg bg-red-600 px-3 py-2 text-xs font-semibold text-white"
onClick={removeComponent}
>
</button>
</div>
</ThemedModal>
onConfirm={removeComponent}
/>
</section>
);
}
function ConfirmationModal({
open,
ariaLabel,
title,
description,
confirmLabel,
confirmClassName,
onClose,
onConfirm,
}: {
open: boolean;
ariaLabel: string;
title: string;
description: ReactNode;
confirmLabel: string;
confirmClassName: string;
onClose: () => void;
onConfirm: () => void;
}) {
return (
<ThemedModal
open={open}
ariaLabel={ariaLabel}
onClose={onClose}
panelClassName="w-full max-w-sm rounded-2xl p-5"
>
<h2 className="m-0 text-base font-semibold">{title}</h2>
<p className="mt-3 text-sm text-(--platform-text-soft)">{description}</p>
<div className="mt-5 flex justify-end gap-2">
<button
type="button"
className="rounded-lg border border-(--platform-subpanel-border) px-3 py-2 text-xs"
onClick={onClose}
>
</button>
<button
type="button"
className={`rounded-lg ${confirmClassName} px-3 py-2 text-xs font-semibold text-white`}
onClick={onConfirm}
>
{confirmLabel}
</button>
</div>
</ThemedModal>
);
}
function componentKind(component: Component): string {
if ('Image' in component) return '图片';
if ('Text' in component) return '文本';