BlockSlashMenu.tsx 3.6 KB

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