verify-state-modal.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import React, { useEffect, useMemo, useRef, useState } from 'react'
  2. import { createPortal } from 'react-dom'
  3. import { useTranslation } from 'react-i18next'
  4. import {
  5. RiExternalLinkLine,
  6. } from '@remixicon/react'
  7. import Button from '@/app/components/base/button'
  8. import { getLocaleOnClient } from '@/i18n'
  9. export type IConfirm = {
  10. className?: string
  11. isShow: boolean
  12. title: string
  13. content?: React.ReactNode
  14. onConfirm: () => void
  15. onCancel: () => void
  16. maskClosable?: boolean
  17. email?: string
  18. showLink?: boolean
  19. }
  20. function Confirm({
  21. isShow,
  22. title,
  23. content,
  24. onConfirm,
  25. onCancel,
  26. maskClosable = true,
  27. showLink,
  28. email,
  29. }: IConfirm) {
  30. const { t } = useTranslation()
  31. const locale = getLocaleOnClient()
  32. const dialogRef = useRef<HTMLDivElement>(null)
  33. const [isVisible, setIsVisible] = useState(isShow)
  34. const docLink = useMemo(() => {
  35. if (locale === 'zh-Hans')
  36. return 'https://docs.dify.ai/zh-hans/getting-started/dify-for-education'
  37. if (locale === 'ja-JP')
  38. return 'https://docs.dify.ai/ja-jp/getting-started/dify-for-education'
  39. return 'https://docs.dify.ai/getting-started/dify-for-education'
  40. }, [locale])
  41. const handleClick = () => {
  42. window.open(docLink, '_blank', 'noopener,noreferrer')
  43. }
  44. useEffect(() => {
  45. const handleKeyDown = (event: KeyboardEvent) => {
  46. if (event.key === 'Escape')
  47. onCancel()
  48. }
  49. document.addEventListener('keydown', handleKeyDown)
  50. return () => {
  51. document.removeEventListener('keydown', handleKeyDown)
  52. }
  53. }, [onCancel])
  54. const handleClickOutside = (event: MouseEvent) => {
  55. if (maskClosable && dialogRef.current && !dialogRef.current.contains(event.target as Node))
  56. onCancel()
  57. }
  58. useEffect(() => {
  59. document.addEventListener('mousedown', handleClickOutside)
  60. return () => {
  61. document.removeEventListener('mousedown', handleClickOutside)
  62. }
  63. }, [maskClosable])
  64. useEffect(() => {
  65. if (isShow) {
  66. setIsVisible(true)
  67. }
  68. else {
  69. const timer = setTimeout(() => setIsVisible(false), 200)
  70. return () => clearTimeout(timer)
  71. }
  72. }, [isShow])
  73. if (!isVisible)
  74. return null
  75. return createPortal(
  76. <div className={'fixed inset-0 z-[10000000] flex items-center justify-center bg-background-overlay'}
  77. onClick={(e) => {
  78. e.preventDefault()
  79. e.stopPropagation()
  80. }}
  81. >
  82. <div ref={dialogRef} className={'relative w-full max-w-[481px] overflow-hidden'}>
  83. <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'>
  84. <div className='flex flex-col items-start gap-2 self-stretch pb-4 pl-6 pr-6 pt-6'>
  85. <div className='title-2xl-semi-bold text-text-primary'>{title}</div>
  86. <div className='system-md-regular w-full text-text-tertiary'>{content}</div>
  87. </div>
  88. {email && (
  89. <div className='w-full space-y-1 px-6 py-3'>
  90. <div className='system-sm-semibold py-1 text-text-secondary'>{t('education.emailLabel')}</div>
  91. <div className='system-sm-regular rounded-lg bg-components-input-bg-disabled px-3 py-2 text-components-input-text-filled-disabled'>{email}</div>
  92. </div>
  93. )}
  94. <div className='flex items-center justify-between gap-2 self-stretch p-6'>
  95. <div className='flex items-center gap-1'>
  96. {showLink && (
  97. <>
  98. <a onClick={handleClick} href={docLink} target='_blank' className='system-xs-regular cursor-pointer text-text-accent'>{t('education.learn')}</a>
  99. <RiExternalLinkLine className='h-3 w-3 text-text-accent' />
  100. </>
  101. )}
  102. </div>
  103. <Button variant='primary' className='!w-20' onClick={onConfirm}>{t('common.operation.ok')}</Button>
  104. </div>
  105. </div>
  106. </div>
  107. </div>, document.body,
  108. )
  109. }
  110. export default React.memo(Confirm)