mirror of
https://github.com/langgenius/dify.git
synced 2026-05-13 08:57:28 +08:00
Made-with: Cursor # Conflicts: # api/controllers/console/app/workflow_draft_variable.py # api/core/agent/cot_agent_runner.py # api/core/agent/cot_chat_agent_runner.py # api/core/agent/cot_completion_agent_runner.py # api/core/agent/fc_agent_runner.py # api/core/app/apps/advanced_chat/app_generator.py # api/core/app/apps/advanced_chat/app_runner.py # api/core/app/apps/agent_chat/app_runner.py # api/core/app/apps/workflow/app_generator.py # api/core/app/apps/workflow/app_runner.py # api/core/app/entities/app_invoke_entities.py # api/core/app/entities/queue_entities.py # api/core/llm_generator/output_parser/structured_output.py # api/core/workflow/workflow_entry.py # api/dify_graph/context/__init__.py # api/dify_graph/entities/tool_entities.py # api/dify_graph/file/file_manager.py # api/dify_graph/graph_engine/response_coordinator/coordinator.py # api/dify_graph/graph_events/node.py # api/dify_graph/node_events/node.py # api/dify_graph/nodes/agent/agent_node.py # api/dify_graph/nodes/llm/entities.py # api/dify_graph/nodes/llm/llm_utils.py # api/dify_graph/nodes/llm/node.py # api/dify_graph/nodes/question_classifier/question_classifier_node.py # api/dify_graph/runtime/graph_runtime_state.py # api/dify_graph/variables/segments.py # api/factories/variable_factory.py # api/services/variable_truncator.py # api/tests/unit_tests/utils/structured_output_parser/test_structured_output_parser.py # api/uv.lock # web/app/components/app-sidebar/app-info.tsx # web/app/components/app-sidebar/app-sidebar-dropdown.tsx # web/app/components/app/create-app-modal/index.spec.tsx # web/app/components/apps/__tests__/list.spec.tsx # web/app/components/apps/app-card.tsx # web/app/components/apps/list.tsx # web/app/components/header/account-dropdown/compliance.tsx # web/app/components/header/account-dropdown/index.tsx # web/app/components/header/account-dropdown/support.tsx # web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx # web/app/components/workflow/panel/debug-and-preview/hooks.ts # web/contract/console/apps.ts # web/contract/router.ts # web/eslint-suppressions.json # web/next.config.ts # web/pnpm-lock.yaml
140 lines
4.2 KiB
TypeScript
140 lines
4.2 KiB
TypeScript
/**
|
|
* @deprecated Use `@/app/components/base/ui/alert-dialog` instead.
|
|
* See issue #32767 for migration details.
|
|
*/
|
|
|
|
import * as React from 'react'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import { createPortal } from 'react-dom'
|
|
import { useTranslation } from 'react-i18next'
|
|
import Button from '../button'
|
|
import Tooltip from '../tooltip'
|
|
|
|
/** @deprecated Use `@/app/components/base/ui/alert-dialog` instead. */
|
|
export type IConfirm = {
|
|
className?: string
|
|
isShow: boolean
|
|
type?: 'info' | 'warning' | 'danger'
|
|
title: string
|
|
content?: React.ReactNode
|
|
confirmText?: string | null
|
|
onConfirm: () => void
|
|
cancelText?: string
|
|
onCancel: () => void
|
|
isLoading?: boolean
|
|
isDisabled?: boolean
|
|
showConfirm?: boolean
|
|
showCancel?: boolean
|
|
maskClosable?: boolean
|
|
}
|
|
|
|
function Confirm({
|
|
isShow,
|
|
type = 'warning',
|
|
title,
|
|
content,
|
|
confirmText,
|
|
cancelText,
|
|
onConfirm,
|
|
onCancel,
|
|
showConfirm = true,
|
|
showCancel = true,
|
|
isLoading = false,
|
|
isDisabled = false,
|
|
maskClosable = true,
|
|
}: IConfirm) {
|
|
const { t } = useTranslation()
|
|
const dialogRef = useRef<HTMLDivElement>(null)
|
|
const titleRef = useRef<HTMLDivElement>(null)
|
|
const [isVisible, setIsVisible] = useState(isShow)
|
|
const [isTitleTruncated, setIsTitleTruncated] = useState(false)
|
|
|
|
const confirmTxt = confirmText || `${t('operation.confirm', { ns: 'common' })}`
|
|
const cancelTxt = cancelText || `${t('operation.cancel', { ns: 'common' })}`
|
|
|
|
useEffect(() => {
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
if (event.key === 'Escape')
|
|
onCancel()
|
|
if (event.key === 'Enter' && isShow) {
|
|
event.preventDefault()
|
|
onConfirm()
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', handleKeyDown)
|
|
return () => {
|
|
document.removeEventListener('keydown', handleKeyDown)
|
|
}
|
|
}, [onCancel, onConfirm, isShow])
|
|
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
if (maskClosable && dialogRef.current && !dialogRef.current.contains(event.target as Node))
|
|
onCancel()
|
|
}
|
|
|
|
useEffect(() => {
|
|
document.addEventListener('mousedown', handleClickOutside)
|
|
return () => {
|
|
document.removeEventListener('mousedown', handleClickOutside)
|
|
}
|
|
}, [maskClosable])
|
|
|
|
useEffect(() => {
|
|
if (isShow) {
|
|
setIsVisible(true)
|
|
}
|
|
else {
|
|
const timer = setTimeout(() => setIsVisible(false), 200)
|
|
return () => clearTimeout(timer)
|
|
}
|
|
}, [isShow])
|
|
|
|
useEffect(() => {
|
|
if (titleRef.current) {
|
|
const isOverflowing = titleRef.current.scrollWidth > titleRef.current.clientWidth
|
|
setIsTitleTruncated(isOverflowing)
|
|
}
|
|
}, [title, isVisible])
|
|
|
|
if (!isVisible)
|
|
return null
|
|
|
|
return createPortal(
|
|
<div
|
|
className="fixed inset-0 z-[10000000] flex items-center justify-center bg-background-overlay"
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
}}
|
|
data-testid="confirm-overlay"
|
|
>
|
|
<div ref={dialogRef} className="relative w-full max-w-[480px] overflow-hidden">
|
|
<div className="shadows-shadow-lg flex max-w-full flex-col items-start rounded-2xl border-[0.5px] border-solid border-components-panel-border bg-components-panel-bg">
|
|
<div className="flex flex-col items-start gap-2 self-stretch pb-4 pl-6 pr-6 pt-6">
|
|
<Tooltip
|
|
popupContent={title}
|
|
disabled={!isTitleTruncated}
|
|
portalContentClassName="!z-[10000001]"
|
|
asChild={false}
|
|
triggerClassName="w-full"
|
|
>
|
|
<div ref={titleRef} className="w-full truncate text-text-primary title-2xl-semi-bold">
|
|
{title}
|
|
</div>
|
|
</Tooltip>
|
|
<div className="w-full whitespace-pre-wrap break-words text-text-tertiary system-md-regular">{content}</div>
|
|
</div>
|
|
<div className="flex items-start justify-end gap-2 self-stretch p-6">
|
|
{showCancel && <Button onClick={onCancel}>{cancelTxt}</Button>}
|
|
{showConfirm && <Button variant="primary" destructive={type !== 'info'} loading={isLoading} disabled={isDisabled} onClick={onConfirm}>{confirmTxt}</Button>}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body,
|
|
)
|
|
}
|
|
|
|
export default React.memo(Confirm)
|