dify/web/app/components/base/modal/modal.tsx
Novice 89b76d6c33
Merge commit '657eeb65' into sandboxed-agent-rebase
Made-with: Cursor

# Conflicts:
#	api/core/agent/cot_chat_agent_runner.py
#	api/core/agent/fc_agent_runner.py
#	api/core/memory/token_buffer_memory.py
#	api/core/variables/segments.py
#	api/core/workflow/file/file_manager.py
#	api/core/workflow/nodes/agent/agent_node.py
#	api/core/workflow/nodes/llm/llm_utils.py
#	api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
#	api/core/workflow/workflow_entry.py
#	api/factories/variable_factory.py
#	api/pyproject.toml
#	api/services/variable_truncator.py
#	api/uv.lock
#	web/app/components/app/app-publisher/index.tsx
#	web/app/components/app/overview/settings/index.tsx
#	web/app/components/apps/app-card.tsx
#	web/app/components/apps/index.tsx
#	web/app/components/apps/list.tsx
#	web/app/components/base/chat/chat-with-history/header-in-mobile.tsx
#	web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx
#	web/app/components/base/features/new-feature-panel/file-upload/setting-content.tsx
#	web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
#	web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx
#	web/app/components/base/message-log-modal/index.tsx
#	web/app/components/base/switch/index.tsx
#	web/app/components/base/tab-slider-plain/index.tsx
#	web/app/components/explore/try-app/app-info/index.tsx
#	web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx
#	web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx
#	web/app/components/workflow/nodes/llm/panel.tsx
#	web/contract/router.ts
#	web/eslint-suppressions.json
#	web/i18n/fa-IR/workflow.json
2026-03-19 17:38:56 +08:00

140 lines
4.1 KiB
TypeScript

import type { ButtonProps } from '@/app/components/base/button'
import { noop } from 'es-toolkit/function'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import {
PortalToFollowElem,
PortalToFollowElemContent,
} from '@/app/components/base/portal-to-follow-elem'
import { cn } from '@/utils/classnames'
type ModalProps = {
onClose?: () => void
size?: 'sm' | 'md'
title: string
subTitle?: string
children?: React.ReactNode
confirmButtonText?: string
onConfirm?: () => void
cancelButtonText?: string
onCancel?: () => void
showExtraButton?: boolean
extraButtonText?: string
extraButtonVariant?: ButtonProps['variant']
onExtraButtonClick?: () => void
footerSlot?: React.ReactNode
bottomSlot?: React.ReactNode
disabled?: boolean
containerClassName?: string
wrapperClassName?: string
clickOutsideNotClose?: boolean
}
const Modal = ({
onClose,
size = 'sm',
title,
subTitle,
children,
confirmButtonText,
onConfirm,
cancelButtonText,
onCancel,
showExtraButton,
extraButtonVariant = 'warning',
extraButtonText,
onExtraButtonClick,
footerSlot,
bottomSlot,
disabled,
containerClassName,
wrapperClassName,
clickOutsideNotClose = false,
}: ModalProps) => {
const { t } = useTranslation()
return (
<PortalToFollowElem open>
<PortalToFollowElemContent
className={cn('z-[9998] flex h-full w-full items-center justify-center bg-background-overlay', wrapperClassName)}
data-modal-root="true"
onClick={clickOutsideNotClose ? noop : onClose}
>
<div
className={cn(
'flex max-h-[80%] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xs',
size === 'sm' && 'w-[480px]',
size === 'md' && 'w-[640px]',
containerClassName,
)}
onClick={e => e.stopPropagation()}
>
<div className="relative shrink-0 p-6 pb-3 pr-14 text-text-primary title-2xl-semi-bold">
{title}
{
subTitle && (
<div className="mt-1 text-text-tertiary system-xs-regular">
{subTitle}
</div>
)
}
<div
className="absolute right-5 top-5 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg"
onClick={onClose}
>
<span className="i-ri-close-line h-5 w-5 text-text-tertiary" data-testid="close-icon" />
</div>
</div>
{
!!children && (
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-3">{children}</div>
)
}
<div className="flex shrink-0 justify-between p-6 pt-5">
<div>
{footerSlot}
</div>
<div className="flex items-center">
{
showExtraButton && (
<>
<Button
variant={extraButtonVariant}
onClick={onExtraButtonClick}
disabled={disabled}
>
{extraButtonText || t('operation.remove', { ns: 'common' })}
</Button>
<div className="mx-3 h-4 w-[1px] bg-divider-regular"></div>
</>
)
}
<Button
onClick={onCancel}
disabled={disabled}
>
{cancelButtonText || t('operation.cancel', { ns: 'common' })}
</Button>
<Button
className="ml-2"
variant="primary"
onClick={onConfirm}
disabled={disabled}
>
{confirmButtonText || t('operation.save', { ns: 'common' })}
</Button>
</div>
</div>
{!!bottomSlot && (
<div className="shrink-0">
{bottomSlot}
</div>
)}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default memo(Modal)