wwf
2025-05-20 938c3e5a587ce950a94964ea509b9e7f8834dfae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import type { FC } from 'react'
import { createPortal } from 'react-dom'
import 'react-pdf-highlighter/dist/style.css'
import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter'
import { t } from 'i18next'
import { RiCloseLine, RiZoomInLine, RiZoomOutLine } from '@remixicon/react'
import React, { useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import Loading from '@/app/components/base/loading'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Tooltip from '@/app/components/base/tooltip'
import { noop } from 'lodash-es'
 
type PdfPreviewProps = {
  url: string
  onCancel: () => void
}
 
const PdfPreview: FC<PdfPreviewProps> = ({
  url,
  onCancel,
}) => {
  const media = useBreakpoints()
  const [scale, setScale] = useState(1)
  const [position, setPosition] = useState({ x: 0, y: 0 })
  const isMobile = media === MediaType.mobile
 
  const zoomIn = () => {
    setScale(prevScale => Math.min(prevScale * 1.2, 15))
    setPosition({ x: position.x - 50, y: position.y - 50 })
  }
 
  const zoomOut = () => {
    setScale((prevScale) => {
      const newScale = Math.max(prevScale / 1.2, 0.5)
      if (newScale === 1)
        setPosition({ x: 0, y: 0 })
      else
        setPosition({ x: position.x + 50, y: position.y + 50 })
 
      return newScale
    })
  }
 
  useHotkeys('esc', onCancel)
  useHotkeys('up', zoomIn)
  useHotkeys('down', zoomOut)
 
  return createPortal(
    <div
      className={`fixed inset-0 z-[1000] flex items-center justify-center bg-black/80 ${!isMobile && 'p-8'}`}
      onClick={e => e.stopPropagation()}
      tabIndex={-1}
    >
      <div
        className='h-[95vh] max-h-full w-[100vw] max-w-full overflow-hidden'
        style={{ transform: `scale(${scale})`, transformOrigin: 'center', scrollbarWidth: 'none', msOverflowStyle: 'none' }}
      >
        <PdfLoader
          workerSrc='/pdf.worker.min.mjs'
          url={url}
          beforeLoad={<div className='flex h-64 items-center justify-center'><Loading type='app' /></div>}
        >
          {(pdfDocument) => {
            return (
              <PdfHighlighter
                pdfDocument={pdfDocument}
                enableAreaSelection={event => event.altKey}
                scrollRef={noop}
                onScrollChange={noop}
                onSelectionFinished={() => null}
                highlightTransform={() => { return <div/> }}
                highlights={[]}
              />
            )
          }}
        </PdfLoader>
      </div>
      <Tooltip popupContent={t('common.operation.zoomOut')}>
        <div className='absolute right-24 top-6 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg'
          onClick={zoomOut}>
          <RiZoomOutLine className='h-4 w-4 text-gray-500'/>
        </div>
      </Tooltip>
      <Tooltip popupContent={t('common.operation.zoomIn')}>
        <div className='absolute right-16 top-6 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg'
          onClick={zoomIn}>
          <RiZoomInLine className='h-4 w-4 text-gray-500'/>
        </div>
      </Tooltip>
      <Tooltip popupContent={t('common.operation.cancel')}>
        <div
          className='absolute right-6 top-6 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-white/8 backdrop-blur-[2px]'
          onClick={onCancel}>
          <RiCloseLine className='h-4 w-4 text-gray-500'/>
        </div>
      </Tooltip>
    </div>,
    document.body,
  )
}
 
export default PdfPreview