useGlobalHotkey.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import { onMounted, onUnmounted } from 'vue'
  2. import { storeToRefs } from 'pinia'
  3. import { useMainStore, useSlidesStore, useKeyboardStore } from '@/store'
  4. import { ElementOrderCommands } from '@/types/edit'
  5. import { KEYS } from '@/configs/hotkey'
  6. import useSlideHandler from './useSlideHandler'
  7. import useLockElement from './useLockElement'
  8. import useDeleteElement from './useDeleteElement'
  9. import useCombineElement from './useCombineElement'
  10. import useCopyAndPasteElement from './useCopyAndPasteElement'
  11. import useSelectElement from './useSelectElement'
  12. import useMoveElement from './useMoveElement'
  13. import useOrderElement from './useOrderElement'
  14. import useHistorySnapshot from './useHistorySnapshot'
  15. import useScreening from './useScreening'
  16. import useScaleCanvas from './useScaleCanvas'
  17. export default () => {
  18. const mainStore = useMainStore()
  19. const keyboardStore = useKeyboardStore()
  20. const {
  21. activeElementIdList,
  22. disableHotkeys,
  23. handleElement,
  24. handleElementId,
  25. editorAreaFocus,
  26. thumbnailsFocus,
  27. showSearchPanel,
  28. } = storeToRefs(mainStore)
  29. const { currentSlide } = storeToRefs(useSlidesStore())
  30. const { ctrlKeyState, shiftKeyState, spaceKeyState } = storeToRefs(keyboardStore)
  31. const {
  32. updateSlideIndex,
  33. copySlide,
  34. createSlide,
  35. deleteSlide,
  36. cutSlide,
  37. copyAndPasteSlide,
  38. selectAllSlide,
  39. } = useSlideHandler()
  40. const { combineElements, uncombineElements } = useCombineElement()
  41. const { deleteElement } = useDeleteElement()
  42. const { lockElement } = useLockElement()
  43. const { copyElement, cutElement, quickCopyElement } = useCopyAndPasteElement()
  44. const { selectAllElements } = useSelectElement()
  45. const { moveElement } = useMoveElement()
  46. const { orderElement } = useOrderElement()
  47. const { redo, undo } = useHistorySnapshot()
  48. const { enterScreening, enterScreeningFromStart } = useScreening()
  49. const { scaleCanvas, resetCanvas } = useScaleCanvas()
  50. const copy = () => {
  51. if (activeElementIdList.value.length) copyElement()
  52. else if (thumbnailsFocus.value) copySlide()
  53. }
  54. const cut = () => {
  55. if (activeElementIdList.value.length) cutElement()
  56. else if (thumbnailsFocus.value) cutSlide()
  57. }
  58. const quickCopy = () => {
  59. if (activeElementIdList.value.length) quickCopyElement()
  60. else if (thumbnailsFocus.value) copyAndPasteSlide()
  61. }
  62. const selectAll = () => {
  63. if (editorAreaFocus.value) selectAllElements()
  64. if (thumbnailsFocus.value) selectAllSlide()
  65. }
  66. const lock = () => {
  67. if (!editorAreaFocus.value) return
  68. lockElement()
  69. }
  70. const combine = () => {
  71. if (!editorAreaFocus.value) return
  72. combineElements()
  73. }
  74. const uncombine = () => {
  75. if (!editorAreaFocus.value) return
  76. uncombineElements()
  77. }
  78. const remove = () => {
  79. if (activeElementIdList.value.length) deleteElement()
  80. else if (thumbnailsFocus.value) deleteSlide()
  81. }
  82. const move = (key: string) => {
  83. if (activeElementIdList.value.length) moveElement(key)
  84. else if (key === KEYS.UP || key === KEYS.DOWN) updateSlideIndex(key)
  85. }
  86. const moveSlide = (key: string) => {
  87. if (key === KEYS.PAGEUP) updateSlideIndex(KEYS.UP)
  88. else if (key === KEYS.PAGEDOWN) updateSlideIndex(KEYS.DOWN)
  89. }
  90. const order = (command: ElementOrderCommands) => {
  91. if (!handleElement.value) return
  92. orderElement(handleElement.value, command)
  93. }
  94. const create = () => {
  95. if (!thumbnailsFocus.value) return
  96. createSlide()
  97. }
  98. const tabActiveElement = () => {
  99. if (!currentSlide.value.elements.length) return
  100. if (!handleElementId.value) {
  101. const firstElement = currentSlide.value.elements[0]
  102. mainStore.setActiveElementIdList([firstElement.id])
  103. return
  104. }
  105. const currentIndex = currentSlide.value.elements.findIndex(el => el.id === handleElementId.value)
  106. const nextIndex = currentIndex >= currentSlide.value.elements.length - 1 ? 0 : currentIndex + 1
  107. const nextElementId = currentSlide.value.elements[nextIndex].id
  108. mainStore.setActiveElementIdList([nextElementId])
  109. }
  110. const keydownListener = (e: KeyboardEvent) => {
  111. const { ctrlKey, shiftKey, altKey, metaKey } = e
  112. const ctrlOrMetaKeyActive = ctrlKey || metaKey
  113. const key = e.key.toUpperCase()
  114. if (ctrlOrMetaKeyActive && !ctrlKeyState.value) keyboardStore.setCtrlKeyState(true)
  115. if (shiftKey && !shiftKeyState.value) keyboardStore.setShiftKeyState(true)
  116. if (!disableHotkeys.value && key === KEYS.SPACE) keyboardStore.setSpaceKeyState(true)
  117. if (ctrlOrMetaKeyActive && key === KEYS.P) {
  118. e.preventDefault()
  119. mainStore.setDialogForExport('pdf')
  120. return
  121. }
  122. if (shiftKey && key === KEYS.F5) {
  123. e.preventDefault()
  124. enterScreening()
  125. keyboardStore.setShiftKeyState(false)
  126. return
  127. }
  128. if (key === KEYS.F5) {
  129. e.preventDefault()
  130. enterScreeningFromStart()
  131. return
  132. }
  133. if (ctrlKey && key === KEYS.F) {
  134. e.preventDefault()
  135. mainStore.setSearchPanelState(!showSearchPanel.value)
  136. return
  137. }
  138. if (ctrlKey && key === KEYS.MINUS) {
  139. e.preventDefault()
  140. scaleCanvas('-')
  141. return
  142. }
  143. if (ctrlKey && key === KEYS.EQUAL) {
  144. e.preventDefault()
  145. scaleCanvas('+')
  146. return
  147. }
  148. if (ctrlKey && key === KEYS.DIGIT_0) {
  149. e.preventDefault()
  150. resetCanvas()
  151. return
  152. }
  153. if (!editorAreaFocus.value && !thumbnailsFocus.value) return
  154. if (ctrlOrMetaKeyActive && key === KEYS.C) {
  155. if (disableHotkeys.value) return
  156. e.preventDefault()
  157. copy()
  158. }
  159. if (ctrlOrMetaKeyActive && key === KEYS.X) {
  160. if (disableHotkeys.value) return
  161. e.preventDefault()
  162. cut()
  163. }
  164. if (ctrlOrMetaKeyActive && key === KEYS.D) {
  165. if (disableHotkeys.value) return
  166. e.preventDefault()
  167. quickCopy()
  168. }
  169. if (ctrlOrMetaKeyActive && key === KEYS.Z) {
  170. if (disableHotkeys.value) return
  171. e.preventDefault()
  172. undo()
  173. }
  174. if (ctrlOrMetaKeyActive && key === KEYS.Y) {
  175. if (disableHotkeys.value) return
  176. e.preventDefault()
  177. redo()
  178. }
  179. if (ctrlOrMetaKeyActive && key === KEYS.A) {
  180. if (disableHotkeys.value) return
  181. e.preventDefault()
  182. selectAll()
  183. }
  184. if (ctrlOrMetaKeyActive && key === KEYS.L) {
  185. if (disableHotkeys.value) return
  186. e.preventDefault()
  187. lock()
  188. }
  189. if (!shiftKey && ctrlOrMetaKeyActive && key === KEYS.G) {
  190. if (disableHotkeys.value) return
  191. e.preventDefault()
  192. combine()
  193. }
  194. if (shiftKey && ctrlOrMetaKeyActive && key === KEYS.G) {
  195. if (disableHotkeys.value) return
  196. e.preventDefault()
  197. uncombine()
  198. }
  199. if (altKey && key === KEYS.F) {
  200. if (disableHotkeys.value) return
  201. e.preventDefault()
  202. order(ElementOrderCommands.TOP)
  203. }
  204. if (altKey && key === KEYS.B) {
  205. if (disableHotkeys.value) return
  206. e.preventDefault()
  207. order(ElementOrderCommands.BOTTOM)
  208. }
  209. if (key === KEYS.DELETE || key === KEYS.BACKSPACE) {
  210. if (disableHotkeys.value) return
  211. e.preventDefault()
  212. remove()
  213. }
  214. if (key === KEYS.UP) {
  215. if (disableHotkeys.value) return
  216. e.preventDefault()
  217. move(KEYS.UP)
  218. }
  219. if (key === KEYS.DOWN) {
  220. if (disableHotkeys.value) return
  221. e.preventDefault()
  222. move(KEYS.DOWN)
  223. }
  224. if (key === KEYS.LEFT) {
  225. if (disableHotkeys.value) return
  226. e.preventDefault()
  227. move(KEYS.LEFT)
  228. }
  229. if (key === KEYS.RIGHT) {
  230. if (disableHotkeys.value) return
  231. e.preventDefault()
  232. move(KEYS.RIGHT)
  233. }
  234. if (key === KEYS.PAGEUP) {
  235. if (disableHotkeys.value) return
  236. e.preventDefault()
  237. moveSlide(KEYS.PAGEUP)
  238. }
  239. if (key === KEYS.PAGEDOWN) {
  240. if (disableHotkeys.value) return
  241. e.preventDefault()
  242. moveSlide(KEYS.PAGEDOWN)
  243. }
  244. if (key === KEYS.ENTER) {
  245. if (disableHotkeys.value) return
  246. e.preventDefault()
  247. create()
  248. }
  249. if (key === KEYS.TAB) {
  250. if (disableHotkeys.value) return
  251. e.preventDefault()
  252. tabActiveElement()
  253. }
  254. if (editorAreaFocus.value && !shiftKey && !ctrlOrMetaKeyActive && !disableHotkeys.value) {
  255. if (key === KEYS.T) {
  256. mainStore.setCreatingElement({ type: 'text' })
  257. }
  258. else if (key === KEYS.R) {
  259. mainStore.setCreatingElement({ type: 'shape', data: {
  260. viewBox: [200, 200],
  261. path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
  262. }})
  263. }
  264. else if (key === KEYS.O) {
  265. mainStore.setCreatingElement({ type: 'shape', data: {
  266. viewBox: [200, 200],
  267. path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z',
  268. }})
  269. }
  270. else if (key === KEYS.L) {
  271. mainStore.setCreatingElement({ type: 'line', data: {
  272. path: 'M 0 0 L 20 20',
  273. style: 'solid',
  274. points: ['', ''],
  275. }})
  276. }
  277. }
  278. }
  279. const keyupListener = () => {
  280. if (ctrlKeyState.value) keyboardStore.setCtrlKeyState(false)
  281. if (shiftKeyState.value) keyboardStore.setShiftKeyState(false)
  282. if (spaceKeyState.value) keyboardStore.setSpaceKeyState(false)
  283. }
  284. onMounted(() => {
  285. document.addEventListener('keydown', keydownListener)
  286. document.addEventListener('keyup', keyupListener)
  287. window.addEventListener('blur', keyupListener)
  288. })
  289. onUnmounted(() => {
  290. document.removeEventListener('keydown', keydownListener)
  291. document.removeEventListener('keyup', keyupListener)
  292. window.removeEventListener('blur', keyupListener)
  293. })
  294. }