mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-01 05:23:47 +08:00
Refactor: Remove ant design component (#13143)
### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [x] Refactoring
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
// src/components/ui/modal.tsx
|
||||
import { cn } from '@/lib/utils';
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { Loader, X } from 'lucide-react';
|
||||
import { AlertCircle, CheckCircle, Info, Loader, X } from 'lucide-react';
|
||||
import { FC, ReactNode, useCallback, useEffect, useMemo } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DialogDescription } from '../dialog';
|
||||
import { createPortalModal } from './modal-manage';
|
||||
@@ -12,13 +13,15 @@ export interface ModalProps {
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
title?: ReactNode;
|
||||
titleClassName?: string;
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
content?: ReactNode;
|
||||
footer?: ReactNode;
|
||||
footerClassName?: string;
|
||||
showfooter?: boolean;
|
||||
className?: string;
|
||||
size?: 'small' | 'default' | 'large';
|
||||
closable?: boolean;
|
||||
showCancel?: boolean;
|
||||
closeIcon?: ReactNode;
|
||||
maskClosable?: boolean;
|
||||
destroyOnClose?: boolean;
|
||||
@@ -33,25 +36,42 @@ export interface ModalProps {
|
||||
disabled?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
zIndex?: number;
|
||||
type?: 'warning' | 'info' | 'success' | 'error' | 'confirm';
|
||||
}
|
||||
|
||||
export interface ModalType extends FC<ModalProps> {
|
||||
show: typeof modalIns.show;
|
||||
hide: typeof modalIns.hide;
|
||||
destroy: typeof modalIns.destroy;
|
||||
warning: (props: Omit<ModalProps, 'open' | 'type'>) => void;
|
||||
info: (props: Omit<ModalProps, 'open' | 'type'>) => void;
|
||||
success: (props: Omit<ModalProps, 'open' | 'type'>) => void;
|
||||
error: (props: Omit<ModalProps, 'open' | 'type'>) => void;
|
||||
confirm: (props: Omit<ModalProps, 'open' | 'type'>) => void;
|
||||
}
|
||||
|
||||
const typeIcons = {
|
||||
warning: <AlertCircle className="w-6 h-6 text-yellow-500" />,
|
||||
info: <Info className="w-6 h-6 text-blue-500" />,
|
||||
success: <CheckCircle className="w-6 h-6 text-green-500" />,
|
||||
error: <AlertCircle className="w-6 h-6 text-red-500" />,
|
||||
confirm: <AlertCircle className="w-6 h-6 text-yellow-500" />,
|
||||
};
|
||||
|
||||
const Modal: ModalType = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
title,
|
||||
titleClassName,
|
||||
children,
|
||||
content,
|
||||
footer,
|
||||
footerClassName,
|
||||
showfooter = true,
|
||||
className = '',
|
||||
size = 'default',
|
||||
closable = true,
|
||||
showCancel = true,
|
||||
closeIcon = <X className="w-4 h-4" />,
|
||||
maskClosable = true,
|
||||
destroyOnClose = false,
|
||||
@@ -66,6 +86,7 @@ const Modal: ModalType = ({
|
||||
disabled = false,
|
||||
style,
|
||||
zIndex = 50,
|
||||
type,
|
||||
}) => {
|
||||
const sizeClasses = {
|
||||
small: 'max-w-md',
|
||||
@@ -74,7 +95,6 @@ const Modal: ModalType = ({
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
// Handle ESC key close
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && maskClosable) {
|
||||
@@ -99,7 +119,6 @@ const Modal: ModalType = ({
|
||||
return;
|
||||
}
|
||||
onOpenChange?.(open);
|
||||
console.log('open', open, onOpenChange);
|
||||
if (open && !disabled) {
|
||||
onOk?.();
|
||||
}
|
||||
@@ -117,16 +136,18 @@ const Modal: ModalType = ({
|
||||
} else {
|
||||
footerTemp = (
|
||||
<div className="flex justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCancel()}
|
||||
className={cn(
|
||||
'px-2 py-1 border border-border-button rounded-md hover:bg-bg-card hover:text-text-primary ',
|
||||
cancelButtonClassName,
|
||||
)}
|
||||
>
|
||||
{cancelText ?? t('modal.cancelText')}
|
||||
</button>
|
||||
{showCancel && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCancel()}
|
||||
className={cn(
|
||||
'px-2 py-1 border border-border-button rounded-md hover:bg-bg-card hover:text-text-primary ',
|
||||
cancelButtonClassName,
|
||||
)}
|
||||
>
|
||||
{cancelText ?? t('modal.cancelText')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
disabled={confirmLoading || disabled}
|
||||
@@ -168,7 +189,28 @@ const Modal: ModalType = ({
|
||||
footerClassName,
|
||||
okButtonClassName,
|
||||
cancelButtonClassName,
|
||||
showCancel,
|
||||
]);
|
||||
|
||||
const contentEl = useMemo(() => {
|
||||
if (type && typeIcons[type]) {
|
||||
return (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 mt-0.5">{typeIcons[type]}</div>
|
||||
<div className="flex-1">
|
||||
{title && (
|
||||
<DialogPrimitive.Title className="text-lg font-medium text-foreground mb-2">
|
||||
{title}
|
||||
</DialogPrimitive.Title>
|
||||
)}
|
||||
<div className="text-text-secondary">{content || children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return children;
|
||||
}, [type, title, content, children]);
|
||||
|
||||
return (
|
||||
<DialogPrimitive.Root open={open} onOpenChange={handleChange}>
|
||||
<DialogPrimitive.Portal>
|
||||
@@ -180,22 +222,16 @@ const Modal: ModalType = ({
|
||||
<DialogPrimitive.Content
|
||||
className={cn(
|
||||
`relative w-[700px] ${full ? 'max-w-full' : sizeClasses[size]} ${className} bg-bg-base rounded-lg shadow-lg border border-border-default transition-all focus-visible:!outline-none`,
|
||||
{ 'pt-10': closable && !title },
|
||||
{ 'pt-10': closable && !title && !type },
|
||||
)}
|
||||
style={style}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DialogDescription></DialogDescription>
|
||||
{/* title */}
|
||||
{title && (
|
||||
{title && !type && (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-start px-6 py-4 justify-start',
|
||||
// {
|
||||
// 'justify-end': closable && !title,
|
||||
// 'justify-between': closable && title,
|
||||
// 'justify-start': !closable,
|
||||
// },
|
||||
titleClassName,
|
||||
)}
|
||||
>
|
||||
@@ -218,12 +254,10 @@ const Modal: ModalType = ({
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
|
||||
{/* content */}
|
||||
<div className="py-2 px-6 overflow-y-auto scrollbar-auto max-h-[calc(100vh-280px)] focus-visible:!outline-none">
|
||||
{destroyOnClose && !open ? null : children}
|
||||
{destroyOnClose && !open ? null : contentEl}
|
||||
</div>
|
||||
|
||||
{/* footer */}
|
||||
{footEl}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPrimitive.Overlay>
|
||||
@@ -242,4 +276,49 @@ Modal.show = modalIns
|
||||
Modal.hide = modalIns.hide;
|
||||
Modal.destroy = modalIns.destroy;
|
||||
|
||||
const createStaticModal = (type: ModalProps['type']) => {
|
||||
return (props: Omit<ModalProps, 'open' | 'type'>) => {
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
|
||||
const destroy = () => {
|
||||
root.unmount();
|
||||
container.remove();
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
props.onOk?.();
|
||||
destroy();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
props.onCancel?.();
|
||||
destroy();
|
||||
};
|
||||
|
||||
root.render(
|
||||
<Modal
|
||||
{...props}
|
||||
open={true}
|
||||
type={type}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
handleCancel();
|
||||
}
|
||||
}}
|
||||
maskClosable={false}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
Modal.warning = createStaticModal('warning');
|
||||
Modal.info = createStaticModal('info');
|
||||
Modal.success = createStaticModal('success');
|
||||
Modal.error = createStaticModal('error');
|
||||
Modal.confirm = createStaticModal('confirm');
|
||||
|
||||
export { Modal };
|
||||
|
||||
Reference in New Issue
Block a user