@blocknote/core 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +99 -0
  2. package/dist/blocknote.js +4485 -0
  3. package/dist/blocknote.js.map +1 -0
  4. package/dist/blocknote.umd.cjs +90 -0
  5. package/dist/blocknote.umd.cjs.map +1 -0
  6. package/dist/style.css +1 -0
  7. package/package.json +109 -0
  8. package/src/BlockNoteExtensions.ts +90 -0
  9. package/src/EditorContent.tsx +1 -0
  10. package/src/assets/inter-v12-latin/inter-v12-latin-100.woff +0 -0
  11. package/src/assets/inter-v12-latin/inter-v12-latin-100.woff2 +0 -0
  12. package/src/assets/inter-v12-latin/inter-v12-latin-200.woff +0 -0
  13. package/src/assets/inter-v12-latin/inter-v12-latin-200.woff2 +0 -0
  14. package/src/assets/inter-v12-latin/inter-v12-latin-300.woff +0 -0
  15. package/src/assets/inter-v12-latin/inter-v12-latin-300.woff2 +0 -0
  16. package/src/assets/inter-v12-latin/inter-v12-latin-500.woff +0 -0
  17. package/src/assets/inter-v12-latin/inter-v12-latin-500.woff2 +0 -0
  18. package/src/assets/inter-v12-latin/inter-v12-latin-600.woff +0 -0
  19. package/src/assets/inter-v12-latin/inter-v12-latin-600.woff2 +0 -0
  20. package/src/assets/inter-v12-latin/inter-v12-latin-700.woff +0 -0
  21. package/src/assets/inter-v12-latin/inter-v12-latin-700.woff2 +0 -0
  22. package/src/assets/inter-v12-latin/inter-v12-latin-800.woff +0 -0
  23. package/src/assets/inter-v12-latin/inter-v12-latin-800.woff2 +0 -0
  24. package/src/assets/inter-v12-latin/inter-v12-latin-900.woff +0 -0
  25. package/src/assets/inter-v12-latin/inter-v12-latin-900.woff2 +0 -0
  26. package/src/assets/inter-v12-latin/inter-v12-latin-regular.woff +0 -0
  27. package/src/assets/inter-v12-latin/inter-v12-latin-regular.woff2 +0 -0
  28. package/src/editor.module.css +3 -0
  29. package/src/extensions/Blocks/OrderedListPlugin.ts +46 -0
  30. package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +146 -0
  31. package/src/extensions/Blocks/commands/joinBackward.ts +274 -0
  32. package/src/extensions/Blocks/helpers/findBlock.ts +3 -0
  33. package/src/extensions/Blocks/helpers/setBlockHeading.ts +30 -0
  34. package/src/extensions/Blocks/index.ts +15 -0
  35. package/src/extensions/Blocks/nodes/Block.module.css +226 -0
  36. package/src/extensions/Blocks/nodes/Block.ts +390 -0
  37. package/src/extensions/Blocks/nodes/BlockGroup.ts +28 -0
  38. package/src/extensions/Blocks/nodes/Content.ts +50 -0
  39. package/src/extensions/Blocks/nodes/README.md +26 -0
  40. package/src/extensions/Blocks/rule.ts +48 -0
  41. package/src/extensions/BubbleMenu/BubbleMenuExtension.tsx +28 -0
  42. package/src/extensions/BubbleMenu/BubbleMenuPlugin.ts +245 -0
  43. package/src/extensions/BubbleMenu/component/BubbleMenu.tsx +216 -0
  44. package/src/extensions/BubbleMenu/component/DropdownBlockItem.module.css +13 -0
  45. package/src/extensions/BubbleMenu/component/DropdownBlockItem.tsx +25 -0
  46. package/src/extensions/BubbleMenu/component/LinkToolbarButton.tsx +67 -0
  47. package/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +15 -0
  48. package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx +266 -0
  49. package/src/extensions/DraggableBlocks/components/DragHandle.module.css +33 -0
  50. package/src/extensions/DraggableBlocks/components/DragHandle.tsx +108 -0
  51. package/src/extensions/DraggableBlocks/components/DragHandleMenu.module.css +10 -0
  52. package/src/extensions/DraggableBlocks/components/DragHandleMenu.tsx +18 -0
  53. package/src/extensions/Hyperlinks/HyperlinkMark.tsx +16 -0
  54. package/src/extensions/Hyperlinks/HyperlinkMenuPlugin.tsx +200 -0
  55. package/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.tsx +59 -0
  56. package/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.tsx +72 -0
  57. package/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.tsx +173 -0
  58. package/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.ts +36 -0
  59. package/src/extensions/Hyperlinks/menus/atlaskit/README.md +1 -0
  60. package/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.tsx +61 -0
  61. package/src/extensions/Paragraph/FixedParagraph.ts +12 -0
  62. package/src/extensions/Placeholder/PlaceholderExtension.ts +127 -0
  63. package/src/extensions/SlashMenu/SlashMenuExtension.ts +43 -0
  64. package/src/extensions/SlashMenu/SlashMenuItem.ts +56 -0
  65. package/src/extensions/SlashMenu/defaultCommands.tsx +229 -0
  66. package/src/extensions/SlashMenu/index.ts +11 -0
  67. package/src/extensions/TrailingNode/TrailingNodeExtension.ts +70 -0
  68. package/src/extensions/UniqueID/UniqueID.ts +281 -0
  69. package/src/extensions/helpers/formatKeyboardShortcut.ts +9 -0
  70. package/src/fonts-inter.css +94 -0
  71. package/src/globals.css +28 -0
  72. package/src/index.ts +5 -0
  73. package/src/lib/atlaskit/browser.ts +47 -0
  74. package/src/root.module.css +19 -0
  75. package/src/shared/components/toolbar/SimpleToolbarButton.module.css +13 -0
  76. package/src/shared/components/toolbar/SimpleToolbarButton.tsx +56 -0
  77. package/src/shared/components/toolbar/Toolbar.module.css +10 -0
  78. package/src/shared/components/toolbar/Toolbar.tsx +5 -0
  79. package/src/shared/components/toolbar/ToolbarSeparator.module.css +13 -0
  80. package/src/shared/components/toolbar/ToolbarSeparator.tsx +7 -0
  81. package/src/shared/components/tooltip/TooltipContent.module.css +15 -0
  82. package/src/shared/components/tooltip/TooltipContent.tsx +23 -0
  83. package/src/shared/hooks/useEditorForceUpdate.tsx +30 -0
  84. package/src/shared/plugins/suggestion/SuggestionItem.ts +31 -0
  85. package/src/shared/plugins/suggestion/SuggestionListReactRenderer.ts +227 -0
  86. package/src/shared/plugins/suggestion/SuggestionPlugin.ts +365 -0
  87. package/src/shared/plugins/suggestion/components/SuggestionGroup.module.css +45 -0
  88. package/src/shared/plugins/suggestion/components/SuggestionGroup.tsx +134 -0
  89. package/src/shared/plugins/suggestion/components/SuggestionList.module.css +10 -0
  90. package/src/shared/plugins/suggestion/components/SuggestionList.tsx +91 -0
  91. package/src/style.css +7 -0
  92. package/src/useEditor.ts +47 -0
  93. package/src/vite-env.d.ts +1 -0
  94. package/types/src/BlockNoteExtensions.d.ts +4 -0
  95. package/types/src/EditorContent.d.ts +1 -0
  96. package/types/src/extensions/Blocks/OrderedListPlugin.d.ts +2 -0
  97. package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +13 -0
  98. package/types/src/extensions/Blocks/commands/joinBackward.d.ts +14 -0
  99. package/types/src/extensions/Blocks/helpers/findBlock.d.ts +6 -0
  100. package/types/src/extensions/Blocks/helpers/setBlockHeading.d.ts +5 -0
  101. package/types/src/extensions/Blocks/index.d.ts +1 -0
  102. package/types/src/extensions/Blocks/nodes/Block.d.ts +32 -0
  103. package/types/src/extensions/Blocks/nodes/BlockGroup.d.ts +2 -0
  104. package/types/src/extensions/Blocks/nodes/Content.d.ts +5 -0
  105. package/types/src/extensions/Blocks/rule.d.ts +16 -0
  106. package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +5 -0
  107. package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +46 -0
  108. package/types/src/extensions/BubbleMenu/component/BubbleMenu.d.ts +5 -0
  109. package/types/src/extensions/BubbleMenu/component/DropdownBlockItem.d.ts +10 -0
  110. package/types/src/extensions/BubbleMenu/component/LinkToolbarButton.d.ts +11 -0
  111. package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +7 -0
  112. package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +18 -0
  113. package/types/src/extensions/DraggableBlocks/components/DragHandle.d.ts +12 -0
  114. package/types/src/extensions/DraggableBlocks/components/DragHandleMenu.d.ts +6 -0
  115. package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +7 -0
  116. package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +2 -0
  117. package/types/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.d.ts +12 -0
  118. package/types/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.d.ts +10 -0
  119. package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.d.ts +39 -0
  120. package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.d.ts +1 -0
  121. package/types/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.d.ts +11 -0
  122. package/types/src/extensions/Paragraph/FixedParagraph.d.ts +1 -0
  123. package/types/src/extensions/Placeholder/PlaceholderExtension.d.ts +25 -0
  124. package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +10 -0
  125. package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +43 -0
  126. package/types/src/extensions/SlashMenu/defaultCommands.d.ts +8 -0
  127. package/types/src/extensions/SlashMenu/index.d.ts +5 -0
  128. package/types/src/extensions/TrailingNode/TrailingNodeExtension.d.ts +10 -0
  129. package/types/src/extensions/UniqueID/UniqueID.d.ts +3 -0
  130. package/types/src/extensions/helpers/formatKeyboardShortcut.d.ts +1 -0
  131. package/types/src/index.d.ts +4 -0
  132. package/types/src/lib/atlaskit/browser.d.ts +12 -0
  133. package/types/src/shared/components/toolbar/SimpleToolbarButton.d.ts +16 -0
  134. package/types/src/shared/components/toolbar/Toolbar.d.ts +4 -0
  135. package/types/src/shared/components/toolbar/ToolbarSeparator.d.ts +2 -0
  136. package/types/src/shared/components/tooltip/TooltipContent.d.ts +15 -0
  137. package/types/src/shared/hooks/useEditorForceUpdate.d.ts +2 -0
  138. package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +29 -0
  139. package/types/src/shared/plugins/suggestion/SuggestionListReactRenderer.d.ts +71 -0
  140. package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +74 -0
  141. package/types/src/shared/plugins/suggestion/components/SuggestionGroup.d.ts +23 -0
  142. package/types/src/shared/plugins/suggestion/components/SuggestionList.d.ts +26 -0
  143. package/types/src/useEditor.d.ts +8 -0
@@ -0,0 +1,245 @@
1
+ import {
2
+ Editor,
3
+ isNodeSelection,
4
+ isTextSelection,
5
+ posToDOMRect,
6
+ } from "@tiptap/core";
7
+ import { EditorState, Plugin, PluginKey } from "prosemirror-state";
8
+ import { EditorView } from "prosemirror-view";
9
+ import tippy, { Instance, Props } from "tippy.js";
10
+
11
+ // Same as TipTap bubblemenu plugin, but with these changes:
12
+ // https://github.com/ueberdosis/tiptap/pull/2596/files
13
+ export interface BubbleMenuPluginProps {
14
+ pluginKey: PluginKey | string;
15
+ editor: Editor;
16
+ element: HTMLElement;
17
+ tippyOptions?: Partial<Props>;
18
+ shouldShow?:
19
+ | ((props: {
20
+ editor: Editor;
21
+ view: EditorView;
22
+ state: EditorState;
23
+ oldState?: EditorState;
24
+ from: number;
25
+ to: number;
26
+ }) => boolean)
27
+ | null;
28
+ }
29
+
30
+ export type BubbleMenuViewProps = BubbleMenuPluginProps & {
31
+ view: EditorView;
32
+ };
33
+
34
+ export class BubbleMenuView {
35
+ public editor: Editor;
36
+
37
+ public element: HTMLElement;
38
+
39
+ public view: EditorView;
40
+
41
+ public preventHide = false;
42
+
43
+ public preventShow = false;
44
+
45
+ public tippy: Instance | undefined;
46
+
47
+ public tippyOptions?: Partial<Props>;
48
+
49
+ public shouldShow: Exclude<BubbleMenuPluginProps["shouldShow"], null> = ({
50
+ view,
51
+ state,
52
+ from,
53
+ to,
54
+ }) => {
55
+ const { doc, selection } = state;
56
+ const { empty } = selection;
57
+
58
+ // Sometime check for `empty` is not enough.
59
+ // Doubleclick an empty paragraph returns a node size of 2.
60
+ // So we check also for an empty text size.
61
+ const isEmptyTextBlock =
62
+ !doc.textBetween(from, to).length && isTextSelection(state.selection);
63
+
64
+ if (!view.hasFocus() || empty || isEmptyTextBlock) {
65
+ return false;
66
+ }
67
+
68
+ return true;
69
+ };
70
+
71
+ constructor({
72
+ editor,
73
+ element,
74
+ view,
75
+ tippyOptions = {},
76
+ shouldShow,
77
+ }: BubbleMenuViewProps) {
78
+ this.editor = editor;
79
+ this.element = element;
80
+ this.view = view;
81
+
82
+ if (shouldShow) {
83
+ this.shouldShow = shouldShow;
84
+ }
85
+
86
+ this.element.addEventListener("mousedown", this.mousedownHandler, {
87
+ capture: true,
88
+ });
89
+ this.view.dom.addEventListener("mousedown", this.viewMousedownHandler);
90
+ this.view.dom.addEventListener("mouseup", this.viewMouseupHandler);
91
+ this.view.dom.addEventListener("dragstart", this.dragstartHandler);
92
+
93
+ this.editor.on("focus", this.focusHandler);
94
+ this.editor.on("blur", this.blurHandler);
95
+ this.tippyOptions = tippyOptions;
96
+ // Detaches menu content from its current parent
97
+ this.element.remove();
98
+ this.element.style.visibility = "visible";
99
+ }
100
+
101
+ mousedownHandler = () => {
102
+ this.preventHide = true;
103
+ };
104
+
105
+ viewMousedownHandler = () => {
106
+ this.preventShow = true;
107
+ };
108
+
109
+ viewMouseupHandler = () => {
110
+ this.preventShow = false;
111
+ setTimeout(() => this.update(this.editor.view));
112
+ };
113
+
114
+ dragstartHandler = () => {
115
+ this.hide();
116
+ };
117
+
118
+ focusHandler = () => {
119
+ // we use `setTimeout` to make sure `selection` is already updated
120
+ setTimeout(() => this.update(this.editor.view));
121
+ };
122
+
123
+ blurHandler = ({ event }: { event: FocusEvent }) => {
124
+ if (this.preventHide) {
125
+ this.preventHide = false;
126
+
127
+ return;
128
+ }
129
+
130
+ if (
131
+ event?.relatedTarget &&
132
+ this.element.parentNode?.contains(event.relatedTarget as Node)
133
+ ) {
134
+ return;
135
+ }
136
+
137
+ this.hide();
138
+ };
139
+
140
+ createTooltip() {
141
+ const { element: editorElement } = this.editor.options;
142
+ const editorIsAttached = !!editorElement.parentElement;
143
+
144
+ if (this.tippy || !editorIsAttached) {
145
+ return;
146
+ }
147
+
148
+ this.tippy = tippy(editorElement, {
149
+ duration: 0,
150
+ getReferenceClientRect: null,
151
+ content: this.element,
152
+ interactive: true,
153
+ trigger: "manual",
154
+ placement: "top",
155
+ hideOnClick: "toggle",
156
+ ...this.tippyOptions,
157
+ });
158
+
159
+ // maybe we have to hide tippy on its own blur event as well
160
+ if (this.tippy.popper.firstChild) {
161
+ (this.tippy.popper.firstChild as HTMLElement).addEventListener(
162
+ "blur",
163
+ (event) => {
164
+ this.blurHandler({ event });
165
+ }
166
+ );
167
+ }
168
+ }
169
+
170
+ update(view: EditorView, oldState?: EditorState) {
171
+ const { state, composing } = view;
172
+ const { doc, selection } = state;
173
+ const isSame =
174
+ oldState && oldState.doc.eq(doc) && oldState.selection.eq(selection);
175
+
176
+ if (composing || isSame) {
177
+ return;
178
+ }
179
+
180
+ this.createTooltip();
181
+
182
+ // support for CellSelections
183
+ const { ranges } = selection;
184
+ const from = Math.min(...ranges.map((range) => range.$from.pos));
185
+ const to = Math.max(...ranges.map((range) => range.$to.pos));
186
+
187
+ const shouldShow = this.shouldShow?.({
188
+ editor: this.editor,
189
+ view,
190
+ state,
191
+ oldState,
192
+ from,
193
+ to,
194
+ });
195
+
196
+ if (!shouldShow || this.preventShow) {
197
+ this.hide();
198
+
199
+ return;
200
+ }
201
+
202
+ this.tippy?.setProps({
203
+ getReferenceClientRect: () => {
204
+ if (isNodeSelection(state.selection)) {
205
+ const node = view.nodeDOM(from) as HTMLElement;
206
+
207
+ if (node) {
208
+ return node.getBoundingClientRect();
209
+ }
210
+ }
211
+
212
+ return posToDOMRect(view, from, to);
213
+ },
214
+ });
215
+
216
+ this.show();
217
+ }
218
+
219
+ show() {
220
+ this.tippy?.show();
221
+ }
222
+
223
+ hide() {
224
+ this.tippy?.hide();
225
+ }
226
+
227
+ destroy() {
228
+ this.tippy?.destroy();
229
+ this.element.removeEventListener("mousedown", this.mousedownHandler, {
230
+ capture: true,
231
+ });
232
+ this.view.dom.removeEventListener("mousedown", this.viewMousedownHandler);
233
+ this.view.dom.removeEventListener("mouseup", this.viewMouseupHandler);
234
+ this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
235
+ this.editor.off("focus", this.focusHandler);
236
+ this.editor.off("blur", this.blurHandler);
237
+ }
238
+ }
239
+
240
+ export const createBubbleMenuPlugin = (options: BubbleMenuPluginProps) => {
241
+ return new Plugin({
242
+ key: new PluginKey("BubbleMenuPlugin"),
243
+ view: (view) => new BubbleMenuView({ view, ...options }),
244
+ });
245
+ };
@@ -0,0 +1,216 @@
1
+ import { Editor } from "@tiptap/core";
2
+ import {
3
+ RiBold,
4
+ RiH1,
5
+ RiH2,
6
+ RiH3,
7
+ RiItalic,
8
+ RiLink,
9
+ RiStrikethrough,
10
+ RiUnderline,
11
+ RiIndentIncrease,
12
+ RiIndentDecrease,
13
+ RiText,
14
+ RiListOrdered,
15
+ RiListUnordered,
16
+ } from "react-icons/ri";
17
+ import { SimpleToolbarButton } from "../../../shared/components/toolbar/SimpleToolbarButton";
18
+ import { Toolbar } from "../../../shared/components/toolbar/Toolbar";
19
+ import { useEditorForceUpdate } from "../../../shared/hooks/useEditorForceUpdate";
20
+ import { findBlock } from "../../Blocks/helpers/findBlock";
21
+ import formatKeyboardShortcut from "../../helpers/formatKeyboardShortcut";
22
+ import LinkToolbarButton from "./LinkToolbarButton";
23
+ import DropdownMenu, { DropdownItemGroup } from "@atlaskit/dropdown-menu";
24
+ import DropdownBlockItem from "./DropdownBlockItem";
25
+
26
+ type ListType = "li" | "oli";
27
+
28
+ function getBlockName(
29
+ currentBlockHeading: number | undefined,
30
+ currentBlockListType: ListType | undefined
31
+ ) {
32
+ const headings = ["Heading 1", "Heading 2", "Heading 3"];
33
+ const lists = {
34
+ li: "Bullet List",
35
+ oli: "Numbered List",
36
+ };
37
+ // A heading that's also a list, should show as Heading
38
+ if (currentBlockHeading) {
39
+ return headings[currentBlockHeading - 1];
40
+ } else if (currentBlockListType) {
41
+ return lists[currentBlockListType];
42
+ } else {
43
+ return "Text";
44
+ }
45
+ }
46
+
47
+ // TODO: add list options, indentation
48
+ export const BubbleMenu = (props: { editor: Editor }) => {
49
+ useEditorForceUpdate(props.editor);
50
+
51
+ const currentBlock = findBlock(props.editor.state.selection);
52
+ const currentBlockHeading: number | undefined =
53
+ currentBlock?.node.attrs.headingType;
54
+ const currentBlockListType: ListType | undefined =
55
+ currentBlock?.node.attrs.listType;
56
+
57
+ const currentBlockName = getBlockName(
58
+ currentBlockHeading,
59
+ currentBlockListType
60
+ );
61
+
62
+ return (
63
+ <Toolbar>
64
+ <DropdownMenu trigger={currentBlockName}>
65
+ <DropdownItemGroup>
66
+ <DropdownBlockItem
67
+ title="Text"
68
+ icon={RiText}
69
+ isSelected={currentBlockName === "Paragraph"}
70
+ onClick={() =>
71
+ props.editor.chain().focus().unsetBlockHeading().unsetList().run()
72
+ }
73
+ />
74
+ <DropdownBlockItem
75
+ title="Heading 1"
76
+ icon={RiH1}
77
+ isSelected={currentBlockName === "Heading 1"}
78
+ onClick={() =>
79
+ props.editor
80
+ .chain()
81
+ .focus()
82
+ .unsetList()
83
+ .setBlockHeading({ level: 1 })
84
+ .run()
85
+ }
86
+ />
87
+ <DropdownBlockItem
88
+ title="Heading 2"
89
+ icon={RiH2}
90
+ isSelected={currentBlockName === "Heading 2"}
91
+ onClick={() =>
92
+ props.editor
93
+ .chain()
94
+ .focus()
95
+ .unsetList()
96
+ .setBlockHeading({ level: 2 })
97
+ .run()
98
+ }
99
+ />
100
+ <DropdownBlockItem
101
+ title="Heading 3"
102
+ icon={RiH3}
103
+ isSelected={currentBlockName === "Heading 3"}
104
+ onClick={() =>
105
+ props.editor
106
+ .chain()
107
+ .focus()
108
+ .unsetList()
109
+ .setBlockHeading({ level: 3 })
110
+ .run()
111
+ }
112
+ />
113
+ <DropdownBlockItem
114
+ title="Bullet List"
115
+ icon={RiListUnordered}
116
+ isSelected={currentBlockName === "Bullet List"}
117
+ onClick={() =>
118
+ props.editor
119
+ .chain()
120
+ .focus()
121
+ .unsetBlockHeading()
122
+ .setBlockList("li")
123
+ .run()
124
+ }
125
+ />
126
+ <DropdownBlockItem
127
+ title="Numbered List"
128
+ icon={RiListOrdered}
129
+ isSelected={currentBlockName === "Numbered List"}
130
+ onClick={() =>
131
+ props.editor
132
+ .chain()
133
+ .focus()
134
+ .unsetBlockHeading()
135
+ .setBlockList("oli")
136
+ .run()
137
+ }
138
+ />
139
+ </DropdownItemGroup>
140
+ </DropdownMenu>
141
+ <SimpleToolbarButton
142
+ onClick={() => props.editor.chain().focus().toggleBold().run()}
143
+ isSelected={props.editor.isActive("bold")}
144
+ mainTooltip="Bold"
145
+ secondaryTooltip={formatKeyboardShortcut("Mod+B")}
146
+ icon={RiBold}
147
+ />
148
+ <SimpleToolbarButton
149
+ onClick={() => props.editor.chain().focus().toggleItalic().run()}
150
+ isSelected={props.editor.isActive("italic")}
151
+ mainTooltip="Italic"
152
+ secondaryTooltip={formatKeyboardShortcut("Mod+I")}
153
+ icon={RiItalic}
154
+ />
155
+ <SimpleToolbarButton
156
+ onClick={() => props.editor.chain().focus().toggleUnderline().run()}
157
+ isSelected={props.editor.isActive("underline")}
158
+ mainTooltip="Underline"
159
+ secondaryTooltip={formatKeyboardShortcut("Mod+U")}
160
+ icon={RiUnderline}
161
+ />
162
+ <SimpleToolbarButton
163
+ onClick={() => props.editor.chain().focus().toggleStrike().run()}
164
+ isDisabled={props.editor.isActive("strike")}
165
+ mainTooltip="Strike-through"
166
+ secondaryTooltip={formatKeyboardShortcut("Mod+Shift+X")}
167
+ icon={RiStrikethrough}
168
+ />
169
+ <SimpleToolbarButton
170
+ onClick={() =>
171
+ props.editor.chain().focus().sinkListItem("tcblock").run()
172
+ }
173
+ isDisabled={!props.editor.can().sinkListItem("tcblock")}
174
+ mainTooltip="Indent"
175
+ secondaryTooltip={formatKeyboardShortcut("Tab")}
176
+ icon={RiIndentIncrease}
177
+ />
178
+
179
+ <SimpleToolbarButton
180
+ onClick={() =>
181
+ props.editor.chain().focus().liftListItem("tcblock").run()
182
+ }
183
+ isDisabled={
184
+ !props.editor.can().command(({ state }) => {
185
+ const block = findBlock(state.selection);
186
+ if (!block) {
187
+ return false;
188
+ }
189
+ // If the depth is greater than 2 you can lift
190
+ return block.depth > 2;
191
+ })
192
+ }
193
+ mainTooltip="Decrease Indent"
194
+ secondaryTooltip={formatKeyboardShortcut("Shift+Tab")}
195
+ icon={RiIndentDecrease}
196
+ />
197
+
198
+ <LinkToolbarButton
199
+ // editor={props.editor}
200
+ isSelected={props.editor.isActive("link")}
201
+ mainTooltip="Link"
202
+ secondaryTooltip={formatKeyboardShortcut("Mod+K")}
203
+ icon={RiLink}
204
+ editor={props.editor}
205
+ />
206
+ {/* <SimpleBubbleMenuButton
207
+ editor={props.editor}
208
+ onClick={() => {
209
+ const comment = this.props.commentStore.createComment();
210
+ props.editor.chain().focus().setComment(comment.id).run();
211
+ }}
212
+ styleDetails={comment}
213
+ /> */}
214
+ </Toolbar>
215
+ );
216
+ };
@@ -0,0 +1,13 @@
1
+ .item_container {
2
+ display: flex;
3
+ flex-direction: row;
4
+ justify-content: space-between;
5
+ min-width: 100px;
6
+ gap: 1em;
7
+ }
8
+ .logo_and_item_container {
9
+ display: flex;
10
+ flex-direction: row;
11
+ justify-content: flex-start;
12
+ gap: 0.5rem;
13
+ }
@@ -0,0 +1,25 @@
1
+ import { DropdownItem } from "@atlaskit/dropdown-menu";
2
+ import { IconType } from "react-icons/lib";
3
+ import { TiTick } from "react-icons/ti";
4
+ import styles from "./DropdownBlockItem.module.css";
5
+
6
+ interface DropdownBlockItemProps {
7
+ title: string;
8
+ icon: IconType;
9
+ isSelected?: boolean;
10
+ onClick?: () => void;
11
+ }
12
+ export default function DropdownBlockItem(props: DropdownBlockItemProps) {
13
+ const ItemIcon = props.icon;
14
+ return (
15
+ <DropdownItem onClick={props.onClick}>
16
+ <div className={`${styles.item_container}`}>
17
+ <div className={`${styles.logo_and_item_container}`}>
18
+ <ItemIcon />
19
+ {props.title}
20
+ </div>
21
+ {props.isSelected && <TiTick />}
22
+ </div>
23
+ </DropdownItem>
24
+ );
25
+ }
@@ -0,0 +1,67 @@
1
+ import Tippy from "@tippyjs/react";
2
+ import { Editor } from "@tiptap/core";
3
+ import { useCallback, useState } from "react";
4
+ import {
5
+ SimpleToolbarButton,
6
+ SimpleToolbarButtonProps,
7
+ } from "../../../shared/components/toolbar/SimpleToolbarButton";
8
+ import { HyperlinkEditMenu } from "../../Hyperlinks/menus/HyperlinkEditMenu";
9
+
10
+ type Props = SimpleToolbarButtonProps & {
11
+ editor: Editor;
12
+ };
13
+
14
+ /**
15
+ * The link menu button opens a tooltip on click
16
+ */
17
+ export const LinkToolbarButton = (props: Props) => {
18
+ const [creationMenu, setCreationMenu] = useState<any>();
19
+
20
+ // TODO: review code; does this pattern still make sense?
21
+ const updateCreationMenu = useCallback(() => {
22
+ const onSubmit = (url: string, text: string) => {
23
+ if (url === "") {
24
+ return;
25
+ }
26
+ const mark = props.editor.schema.mark("link", { href: url });
27
+ let { from, to } = props.editor.state.selection;
28
+ props.editor.view.dispatch(
29
+ props.editor.view.state.tr
30
+ .insertText(text, from, to)
31
+ .addMark(from, from + text.length, mark)
32
+ );
33
+ };
34
+
35
+ // get the currently selected text and url from the document, and use it to
36
+ // create a new creation menu
37
+ const { from, to } = props.editor.state.selection;
38
+ const selectedText = props.editor.state.doc.textBetween(from, to);
39
+ const activeUrl = props.editor.isActive("link")
40
+ ? props.editor.getAttributes("link").href || ""
41
+ : "";
42
+
43
+ setCreationMenu(
44
+ <HyperlinkEditMenu
45
+ key={Math.random() + ""} // Math.random to prevent old element from being re-used
46
+ url={activeUrl}
47
+ text={selectedText}
48
+ onSubmit={onSubmit}
49
+ />
50
+ );
51
+ }, [props.editor]);
52
+
53
+ return (
54
+ <Tippy
55
+ content={creationMenu}
56
+ trigger={"click"}
57
+ onShow={(_) => {
58
+ updateCreationMenu();
59
+ }}
60
+ interactive={true}
61
+ maxWidth={500}>
62
+ <SimpleToolbarButton {...props} />
63
+ </Tippy>
64
+ );
65
+ };
66
+
67
+ export default LinkToolbarButton;
@@ -0,0 +1,15 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { createDraggableBlocksPlugin } from "./DraggableBlocksPlugin";
3
+
4
+ /**
5
+ * This extension adds a drag handle in front of all nodes with a "data-id" attribute
6
+ *
7
+ * code based on https://github.com/ueberdosis/tiptap/issues/323#issuecomment-506637799
8
+ */
9
+ export const DraggableBlocksExtension = Extension.create<{}>({
10
+ name: "DraggableBlocksExtension",
11
+ priority: 1000, // Need to be high, in order to hide draghandle when typing slash
12
+ addProseMirrorPlugins() {
13
+ return [createDraggableBlocksPlugin()];
14
+ },
15
+ });