功能修改
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { X } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import { type ReactNode, useEffect, useId, useRef } from "react";
|
||||
|
||||
interface ConfirmDialogProps {
|
||||
open: boolean;
|
||||
@@ -14,14 +14,26 @@ interface ConfirmDialogProps {
|
||||
}
|
||||
|
||||
export function ConfirmDialog({ open, title, description, confirmLabel, danger, busy, onConfirm, onCancel, children }: ConfirmDialogProps) {
|
||||
const titleId = useId();
|
||||
const descriptionId = useId();
|
||||
const panelRef = useDialogLifecycle(open, onCancel, !busy);
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="confirm-backdrop" role="presentation" onClick={onCancel}>
|
||||
<div className="confirm-panel" role="dialog" aria-modal="true" aria-label={title} onClick={(event) => event.stopPropagation()}>
|
||||
<h2>{title}</h2>
|
||||
<p>{description}</p>
|
||||
<div className="confirm-backdrop" role="presentation" onClick={() => !busy && onCancel()}>
|
||||
<div
|
||||
ref={panelRef}
|
||||
className="confirm-panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
aria-describedby={descriptionId}
|
||||
aria-busy={busy}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<h2 id={titleId}>{title}</h2>
|
||||
<p id={descriptionId}>{description}</p>
|
||||
{children}
|
||||
<div className="confirm-actions">
|
||||
<button type="button" onClick={onCancel} disabled={busy}>
|
||||
@@ -46,26 +58,66 @@ interface ManagementDialogProps {
|
||||
}
|
||||
|
||||
export function ManagementDialog({ open, title, description, wide, onClose, children }: ManagementDialogProps) {
|
||||
const titleId = useId();
|
||||
const descriptionId = useId();
|
||||
const panelRef = useDialogLifecycle(open, onClose, true);
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="confirm-backdrop management-dialog-backdrop" role="presentation" onClick={onClose}>
|
||||
<div className={`drawer-panel management-dialog-panel${wide ? " management-dialog-wide" : ""}`} role="dialog" aria-modal="true" aria-label={title} onClick={(event) => event.stopPropagation()}>
|
||||
<div
|
||||
ref={panelRef}
|
||||
className={`drawer-panel management-dialog-panel${wide ? " management-dialog-wide" : ""}`}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
aria-describedby={description ? descriptionId : undefined}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-header">
|
||||
<h2>{title}</h2>
|
||||
<button type="button" className="theme-upload drawer-close" onClick={onClose}>
|
||||
<h2 id={titleId}>{title}</h2>
|
||||
<button type="button" className="theme-upload drawer-close" aria-label={`关闭${title}`} onClick={onClose}>
|
||||
<X size={14} />
|
||||
<span>关闭</span>
|
||||
</button>
|
||||
</div>
|
||||
{description && <p className="dialog-description">{description}</p>}
|
||||
{description && <p id={descriptionId} className="dialog-description">{description}</p>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function useDialogLifecycle(open: boolean, onClose: () => void, canClose: boolean) {
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
||||
const panel = panelRef.current;
|
||||
const focusTarget = panel?.querySelector<HTMLElement>("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])");
|
||||
focusTarget?.focus();
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === "Escape" && canClose) {
|
||||
event.preventDefault();
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
previousFocus?.focus();
|
||||
};
|
||||
}, [canClose, onClose, open]);
|
||||
|
||||
return panelRef;
|
||||
}
|
||||
|
||||
interface UsageMeterProps {
|
||||
label: string;
|
||||
percent?: number;
|
||||
|
||||
Reference in New Issue
Block a user