CodeBlock.hooks.ts 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { useTextInput } from '$app/components/document/_shared/Text/TextInput.hooks';
  2. import isHotkey from 'is-hotkey';
  3. import { useCallback, useContext, useMemo } from 'react';
  4. import { Editor } from 'slate';
  5. import { BlockType, NestedBlock, TextBlockKeyEventHandlerParams } from '$app/interfaces/document';
  6. import { keyBoardEventKeyMap } from '$app/constants/document/text_block';
  7. import { useAppDispatch } from '$app/stores/store';
  8. import { DocumentControllerContext } from '$app/stores/effects/document/document_controller';
  9. import { splitNodeThunk } from '$app_reducers/document/async-actions';
  10. import { useDefaultTextInputEvents } from '$app/components/document/_shared/Text/useTextEvents';
  11. import { indent, outdent } from '$app/utils/document/blocks/code';
  12. export function useCodeBlock(node: NestedBlock<BlockType.CodeBlock>) {
  13. const id = node.id;
  14. const dispatch = useAppDispatch();
  15. const controller = useContext(DocumentControllerContext);
  16. const { editor, onChange, value, onDOMBeforeInput } = useTextInput(id);
  17. const defaultTextInputEvents = useDefaultTextInputEvents(id);
  18. const customEvents = useMemo(() => {
  19. return [
  20. {
  21. // Here custom tab key event for TextBlock to insert 2 spaces
  22. triggerEventKey: keyBoardEventKeyMap.Tab,
  23. canHandle: (...args: TextBlockKeyEventHandlerParams) => isHotkey('tab', args[0]),
  24. handler: (...args: TextBlockKeyEventHandlerParams) => {
  25. const [e, editor] = args;
  26. e.preventDefault();
  27. indent(editor, 2);
  28. },
  29. },
  30. {
  31. // Here custom shift+tab key event for TextBlock to delete 2 spaces
  32. triggerEventKey: keyBoardEventKeyMap.Tab,
  33. canHandle: (...args: TextBlockKeyEventHandlerParams) => isHotkey('shift+tab', args[0]),
  34. handler: (...args: TextBlockKeyEventHandlerParams) => {
  35. const [e, editor] = args;
  36. e.preventDefault();
  37. outdent(editor, 2);
  38. },
  39. },
  40. {
  41. // Here custom enter key event for TextBlock
  42. triggerEventKey: keyBoardEventKeyMap.Enter,
  43. canHandle: (...args: TextBlockKeyEventHandlerParams) => isHotkey('enter', args[0]),
  44. handler: (...args: TextBlockKeyEventHandlerParams) => {
  45. const [e, editor] = args;
  46. e.preventDefault();
  47. Editor.insertText(editor, '\n');
  48. },
  49. },
  50. {
  51. // Here custom shift+enter key event for TextBlock
  52. triggerEventKey: keyBoardEventKeyMap.Enter,
  53. canHandle: (...args: TextBlockKeyEventHandlerParams) => isHotkey('shift+enter', args[0]),
  54. handler: (...args: TextBlockKeyEventHandlerParams) => {
  55. const [e, editor] = args;
  56. e.preventDefault();
  57. void (async () => {
  58. if (!controller) return;
  59. await dispatch(splitNodeThunk({ id, controller, editor }));
  60. })();
  61. },
  62. },
  63. ];
  64. }, [controller, dispatch, id]);
  65. const onKeyDown = useCallback<React.KeyboardEventHandler<HTMLDivElement>>(
  66. (e) => {
  67. const keyEvents = [...defaultTextInputEvents, ...customEvents];
  68. keyEvents.forEach((keyEvent) => {
  69. // Here we check if the key event can be handled by the current key event
  70. if (keyEvent.canHandle(e, editor)) {
  71. keyEvent.handler(e, editor);
  72. }
  73. });
  74. },
  75. [defaultTextInputEvents, customEvents, editor]
  76. );
  77. return {
  78. editor,
  79. onKeyDown,
  80. onChange,
  81. value,
  82. onDOMBeforeInput,
  83. };
  84. }