pdf-zoom-dropdown.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { useEffect, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import {
  4. Dropdown,
  5. DropdownDivider,
  6. DropdownHeader,
  7. DropdownItem,
  8. DropdownMenu,
  9. DropdownToggle,
  10. } from '@/shared/components/dropdown/dropdown-menu'
  11. import FormControl from '@/shared/components/form/form-control'
  12. import { isMac } from '@/shared/utils/os'
  13. import { Shortcut } from '@/shared/components/shortcut'
  14. const shortcuts = isMac
  15. ? {
  16. 'zoom-in': ['⌘', '+'],
  17. 'zoom-out': ['⌘', '-'],
  18. 'fit-to-width': ['⌘', '0'],
  19. 'fit-to-height': ['⌘', '9'],
  20. }
  21. : {
  22. 'zoom-in': ['Ctrl', '+'],
  23. 'zoom-out': ['Ctrl', '-'],
  24. 'fit-to-width': ['Ctrl', '0'],
  25. 'fit-to-height': ['Ctrl', '9'],
  26. }
  27. type PdfZoomDropdownProps = {
  28. requestPresentationMode: () => void
  29. setZoom: (zoom: string) => void
  30. rawScale: number
  31. }
  32. const zoomValues = ['0.5', '0.75', '1', '1.5', '2', '4']
  33. const rawScaleToPercentage = (rawScale: number) => {
  34. return `${Math.round(rawScale * 100)}%`
  35. }
  36. function PdfZoomDropdown({
  37. requestPresentationMode,
  38. setZoom,
  39. rawScale,
  40. }: PdfZoomDropdownProps) {
  41. const { t } = useTranslation()
  42. const [customZoomValue, setCustomZoomValue] = useState<string>(
  43. rawScaleToPercentage(rawScale)
  44. )
  45. useEffect(() => {
  46. setCustomZoomValue(rawScaleToPercentage(rawScale))
  47. }, [rawScale])
  48. const showPresentOption = document.fullscreenEnabled
  49. return (
  50. <Dropdown
  51. onSelect={eventKey => {
  52. if (eventKey === 'custom-zoom') {
  53. return
  54. }
  55. if (eventKey === 'present') {
  56. requestPresentationMode()
  57. return
  58. }
  59. setZoom(eventKey)
  60. }}
  61. align="end"
  62. >
  63. <DropdownToggle
  64. id="pdf-zoom-dropdown"
  65. variant="link"
  66. className="pdf-toolbar-btn pdfjs-zoom-dropdown-button small"
  67. aria-label={t('pdf_zoom_level')}
  68. >
  69. {rawScaleToPercentage(rawScale)}
  70. </DropdownToggle>
  71. <DropdownMenu className="pdfjs-zoom-dropdown-menu">
  72. <li role="none">
  73. <DropdownItem
  74. disabled
  75. as="div"
  76. className="pdfjs-custom-zoom-menu-item"
  77. eventKey="custom-zoom"
  78. >
  79. <FormControl
  80. onFocus={event => event.target.select()}
  81. value={customZoomValue}
  82. type="text"
  83. onKeyDown={event => {
  84. if (event.key === 'Enter') {
  85. const zoom = Number(customZoomValue.replace('%', '')) / 100
  86. // Only allow zoom values between 10% and 999%
  87. if (zoom < 0.1) {
  88. setZoom('0.1')
  89. } else if (zoom > 9.99) {
  90. setZoom('9.99')
  91. } else {
  92. setZoom(`${zoom}`)
  93. }
  94. }
  95. }}
  96. onChange={event => {
  97. const rawValue = event.target.value
  98. const parsedValue = rawValue.replace(/[^0-9%]/g, '')
  99. setCustomZoomValue(parsedValue)
  100. }}
  101. />
  102. </DropdownItem>
  103. </li>
  104. <DropdownDivider />
  105. <li role="none">
  106. <DropdownItem
  107. as="button"
  108. eventKey="zoom-in"
  109. trailingIcon={<Shortcut keys={shortcuts['zoom-in']} />}
  110. >
  111. {t('zoom_in')}
  112. </DropdownItem>
  113. </li>
  114. <li role="none">
  115. <DropdownItem
  116. as="button"
  117. eventKey="zoom-out"
  118. trailingIcon={<Shortcut keys={shortcuts['zoom-out']} />}
  119. >
  120. {t('zoom_out')}
  121. </DropdownItem>
  122. </li>
  123. <li role="none">
  124. <DropdownItem
  125. as="button"
  126. eventKey="page-width"
  127. trailingIcon={<Shortcut keys={shortcuts['fit-to-width']} />}
  128. >
  129. {t('fit_to_width')}
  130. </DropdownItem>
  131. </li>
  132. <li role="none">
  133. <DropdownItem
  134. as="button"
  135. eventKey="page-height"
  136. trailingIcon={<Shortcut keys={shortcuts['fit-to-height']} />}
  137. >
  138. {t('fit_to_height')}
  139. </DropdownItem>
  140. </li>
  141. {showPresentOption && <DropdownDivider />}
  142. {showPresentOption && (
  143. <li role="none">
  144. <DropdownItem as="button" eventKey="present">
  145. {t('presentation_mode')}
  146. </DropdownItem>
  147. </li>
  148. )}
  149. <DropdownDivider />
  150. <DropdownHeader aria-hidden="true">{t('zoom_to')}</DropdownHeader>
  151. {zoomValues.map(value => (
  152. <li role="none" key={value}>
  153. <DropdownItem as="button" eventKey={value}>
  154. {rawScaleToPercentage(Number(value))}
  155. </DropdownItem>
  156. </li>
  157. ))}
  158. </DropdownMenu>
  159. </Dropdown>
  160. )
  161. }
  162. export default PdfZoomDropdown