BlockSlashMenu.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import React, { useCallback, useContext, useMemo } from 'react';
  2. import MenuItem from '$app/components/document/BlockSideToolbar/MenuItem';
  3. import {
  4. ArrowRight,
  5. Check,
  6. DataObject,
  7. FormatListBulleted,
  8. FormatListNumbered,
  9. FormatQuote,
  10. Lightbulb,
  11. TextFields,
  12. Title,
  13. } from '@mui/icons-material';
  14. import { List } from '@mui/material';
  15. import { BlockData, BlockType } from '$app/interfaces/document';
  16. import { useAppDispatch } from '$app/stores/store';
  17. import { DocumentControllerContext } from '$app/stores/effects/document/document_controller';
  18. import { triggerSlashCommandActionThunk } from '$app_reducers/document/async-actions/menu';
  19. function BlockSlashMenu({ id, onClose, searchText }: { id: string; onClose?: () => void; searchText?: string }) {
  20. const dispatch = useAppDispatch();
  21. const controller = useContext(DocumentControllerContext);
  22. const handleInsert = useCallback(
  23. async (type: BlockType, data?: BlockData<any>) => {
  24. if (!controller) return;
  25. await dispatch(
  26. triggerSlashCommandActionThunk({
  27. controller,
  28. id,
  29. props: {
  30. type,
  31. data,
  32. },
  33. })
  34. );
  35. onClose?.();
  36. },
  37. [controller, dispatch, id, onClose]
  38. );
  39. const optionColumns = useMemo(
  40. () => [
  41. [
  42. {
  43. type: BlockType.TextBlock,
  44. title: 'Text',
  45. icon: <TextFields />,
  46. },
  47. {
  48. type: BlockType.HeadingBlock,
  49. title: 'Heading 1',
  50. icon: <Title />,
  51. props: {
  52. level: 1,
  53. },
  54. },
  55. {
  56. type: BlockType.HeadingBlock,
  57. title: 'Heading 2',
  58. icon: <Title />,
  59. props: {
  60. level: 2,
  61. },
  62. },
  63. {
  64. type: BlockType.HeadingBlock,
  65. title: 'Heading 3',
  66. icon: <Title />,
  67. props: {
  68. level: 3,
  69. },
  70. },
  71. {
  72. type: BlockType.TodoListBlock,
  73. title: 'To-do list',
  74. icon: <Check />,
  75. },
  76. {
  77. type: BlockType.BulletedListBlock,
  78. title: 'Bulleted list',
  79. icon: <FormatListBulleted />,
  80. },
  81. {
  82. type: BlockType.NumberedListBlock,
  83. title: 'Numbered list',
  84. icon: <FormatListNumbered />,
  85. },
  86. ],
  87. [
  88. {
  89. type: BlockType.ToggleListBlock,
  90. title: 'Toggle list',
  91. icon: <ArrowRight />,
  92. },
  93. {
  94. type: BlockType.CodeBlock,
  95. title: 'Code',
  96. icon: <DataObject />,
  97. },
  98. {
  99. type: BlockType.QuoteBlock,
  100. title: 'Quote',
  101. icon: <FormatQuote />,
  102. },
  103. {
  104. type: BlockType.CalloutBlock,
  105. title: 'Callout',
  106. icon: <Lightbulb />,
  107. },
  108. ],
  109. ],
  110. []
  111. );
  112. return (
  113. <div
  114. onMouseDown={(e) => {
  115. e.preventDefault();
  116. e.stopPropagation();
  117. }}
  118. className={'flex'}
  119. >
  120. {optionColumns.map((column, index) => (
  121. <List key={index} className={'flex-1'}>
  122. {column.map((option) => {
  123. return (
  124. <MenuItem
  125. key={option.title}
  126. title={option.title}
  127. icon={option.icon}
  128. onClick={() => {
  129. handleInsert(option.type, option.props);
  130. }}
  131. />
  132. );
  133. })}
  134. </List>
  135. ))}
  136. </div>
  137. );
  138. }
  139. export default BlockSlashMenu;