@blocknote/core 0.38.0 → 0.39.0

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 (225) hide show
  1. package/dist/BlockNoteSchema-DmZ6UQfY.cjs +11 -0
  2. package/dist/BlockNoteSchema-DmZ6UQfY.cjs.map +1 -0
  3. package/dist/BlockNoteSchema-oR047ACf.js +4275 -0
  4. package/dist/BlockNoteSchema-oR047ACf.js.map +1 -0
  5. package/dist/blocknote.cjs +4 -12
  6. package/dist/blocknote.cjs.map +1 -1
  7. package/dist/blocknote.js +3296 -7187
  8. package/dist/blocknote.js.map +1 -1
  9. package/dist/blocks.cjs +2 -0
  10. package/dist/blocks.cjs.map +1 -0
  11. package/dist/blocks.js +71 -0
  12. package/dist/blocks.js.map +1 -0
  13. package/dist/style.css +1 -1
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/webpack-stats.json +1 -1
  16. package/package.json +19 -16
  17. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +1 -1
  18. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +2 -2
  19. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +34 -25
  20. package/src/api/blockManipulation/setupTestEnv.ts +0 -1
  21. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +4 -8
  22. package/src/api/clipboard/toClipboard/copyExtension.ts +1 -1
  23. package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +128 -28
  24. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +101 -41
  25. package/src/api/pmUtil.ts +1 -1
  26. package/src/api/positionMapping.test.ts +58 -15
  27. package/src/api/positionMapping.ts +2 -4
  28. package/src/blocks/Audio/block.ts +174 -0
  29. package/src/blocks/BlockNoteSchema.ts +59 -0
  30. package/src/blocks/Code/block.ts +303 -0
  31. package/src/blocks/Code/shiki.ts +73 -0
  32. package/src/blocks/File/block.ts +98 -0
  33. package/src/blocks/{FileBlockContent → File}/helpers/render/createAddFileButton.ts +5 -2
  34. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileBlockWrapper.ts +15 -6
  35. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.ts +15 -2
  36. package/src/blocks/{FileBlockContent → File}/helpers/render/createResizableFileBlockWrapper.ts +21 -2
  37. package/src/blocks/Heading/block.ts +138 -0
  38. package/src/blocks/Image/block.ts +190 -0
  39. package/src/blocks/ListItem/BulletListItem/block.ts +116 -0
  40. package/src/blocks/ListItem/CheckListItem/block.ts +175 -0
  41. package/src/blocks/ListItem/NumberedListItem/IndexingPlugin.ts +173 -0
  42. package/src/blocks/ListItem/NumberedListItem/block.ts +133 -0
  43. package/src/blocks/ListItem/ToggleListItem/block.ts +78 -0
  44. package/src/blocks/PageBreak/block.ts +72 -0
  45. package/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.ts +9 -7
  46. package/src/blocks/Paragraph/block.ts +80 -0
  47. package/src/blocks/Quote/block.ts +90 -0
  48. package/src/blocks/{TableBlockContent/TableBlockContent.ts → Table/block.ts} +169 -51
  49. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +1 -1
  50. package/src/blocks/Video/block.ts +143 -0
  51. package/src/blocks/defaultBlockHelpers.ts +2 -2
  52. package/src/blocks/defaultBlockTypeGuards.ts +143 -174
  53. package/src/blocks/defaultBlocks.ts +107 -35
  54. package/src/blocks/defaultProps.ts +145 -4
  55. package/src/blocks/index.ts +26 -0
  56. package/src/blocks/utils/listItemEnterHandler.ts +42 -0
  57. package/src/editor/Block.css +54 -18
  58. package/src/editor/BlockNoteEditor.ts +251 -210
  59. package/src/editor/BlockNoteExtension.ts +92 -0
  60. package/src/editor/BlockNoteExtensions.ts +18 -17
  61. package/src/editor/defaultColors.ts +2 -2
  62. package/src/exporter/Exporter.ts +1 -1
  63. package/src/exporter/mapping.ts +1 -1
  64. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +3 -20
  65. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +6 -8
  66. package/src/extensions/BlockChange/BlockChangePlugin.ts +2 -1
  67. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +2 -2
  68. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +2 -2
  69. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -1
  70. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -1
  71. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +52 -0
  72. package/src/extensions/Collaboration/schemaMigration/migrationRules/index.ts +4 -0
  73. package/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.ts +4 -0
  74. package/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.ts +78 -0
  75. package/src/extensions/Comments/CommentsPlugin.ts +1 -1
  76. package/src/extensions/FilePanel/FilePanelPlugin.ts +5 -10
  77. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +1 -1
  78. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +4 -3
  79. package/src/extensions/Placeholder/PlaceholderPlugin.ts +2 -2
  80. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +1 -23
  81. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +0 -5
  82. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +6 -2
  83. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +24 -17
  84. package/src/extensions/TableHandles/TableHandlesPlugin.ts +2 -2
  85. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +5 -11
  86. package/src/extensions/TextColor/TextColorExtension.ts +3 -17
  87. package/src/extensions/TextColor/TextColorMark.ts +4 -9
  88. package/src/extensions/UniqueID/UniqueID.ts +6 -13
  89. package/src/index.ts +2 -28
  90. package/src/schema/blocks/createSpec.ts +342 -169
  91. package/src/schema/blocks/internal.ts +77 -138
  92. package/src/schema/blocks/types.ts +264 -94
  93. package/src/schema/index.ts +1 -0
  94. package/src/schema/inlineContent/createSpec.ts +99 -21
  95. package/src/schema/inlineContent/internal.ts +16 -7
  96. package/src/schema/inlineContent/types.ts +24 -2
  97. package/src/schema/propTypes.ts +15 -9
  98. package/src/schema/schema.ts +209 -0
  99. package/src/schema/styles/createSpec.ts +79 -31
  100. package/src/schema/styles/internal.ts +61 -2
  101. package/src/schema/styles/types.ts +17 -3
  102. package/src/util/topo-sort.test.ts +125 -0
  103. package/src/util/topo-sort.ts +160 -0
  104. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.d.ts +2 -1
  105. package/types/src/api/blockManipulation/selections/selection.d.ts +1 -1
  106. package/types/src/api/blockManipulation/setupTestEnv.d.ts +29 -543
  107. package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +1 -1
  108. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
  109. package/types/src/api/pmUtil.d.ts +1 -1
  110. package/types/src/blocks/Audio/block.d.ts +58 -0
  111. package/types/src/blocks/BlockNoteSchema.d.ts +18 -0
  112. package/types/src/blocks/{CodeBlockContent/CodeBlockContent.d.ts → Code/block.d.ts} +25 -26
  113. package/types/src/blocks/Code/shiki.d.ts +4 -0
  114. package/types/src/blocks/File/block.d.ts +37 -0
  115. package/types/src/blocks/File/helpers/render/createAddFileButton.d.ts +6 -0
  116. package/types/src/blocks/File/helpers/render/createFileBlockWrapper.d.ts +25 -0
  117. package/types/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.d.ts +6 -2
  118. package/types/src/blocks/File/helpers/render/createResizableFileBlockWrapper.d.ts +31 -0
  119. package/types/src/blocks/Heading/block.d.ts +71 -0
  120. package/types/src/blocks/Image/block.d.ts +102 -0
  121. package/types/src/blocks/ListItem/BulletListItem/block.d.ts +25 -0
  122. package/types/src/blocks/ListItem/CheckListItem/block.d.ts +33 -0
  123. package/types/src/blocks/ListItem/NumberedListItem/IndexingPlugin.d.ts +8 -0
  124. package/types/src/blocks/ListItem/NumberedListItem/block.d.ts +33 -0
  125. package/types/src/blocks/ListItem/ToggleListItem/block.d.ts +25 -0
  126. package/types/src/blocks/PageBreak/block.d.ts +11 -0
  127. package/types/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.d.ts +4 -2
  128. package/types/src/blocks/Paragraph/block.d.ts +25 -0
  129. package/types/src/blocks/Quote/block.d.ts +17 -0
  130. package/types/src/blocks/Table/block.d.ts +21 -0
  131. package/types/src/blocks/Video/block.d.ts +67 -0
  132. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -1
  133. package/types/src/blocks/defaultBlockTypeGuards.d.ts +15 -36
  134. package/types/src/blocks/defaultBlocks.d.ts +221 -1060
  135. package/types/src/blocks/defaultProps.d.ts +17 -1
  136. package/types/src/blocks/index.d.ts +24 -0
  137. package/types/src/blocks/utils/listItemEnterHandler.d.ts +2 -0
  138. package/types/src/editor/BlockNoteEditor.d.ts +33 -66
  139. package/types/src/editor/BlockNoteExtension.d.ts +67 -0
  140. package/types/src/editor/BlockNoteExtensions.d.ts +1 -1
  141. package/types/src/editor/defaultColors.d.ts +8 -76
  142. package/types/src/exporter/Exporter.d.ts +1 -1
  143. package/types/src/exporter/mapping.d.ts +1 -1
  144. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +4 -1
  145. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +7 -0
  146. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/index.d.ts +3 -0
  147. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.d.ts +3 -0
  148. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.d.ts +2 -0
  149. package/types/src/extensions/Comments/CommentsPlugin.d.ts +1 -1
  150. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  151. package/types/src/extensions/TextColor/TextColorMark.d.ts +4 -1
  152. package/types/src/index.d.ts +2 -25
  153. package/types/src/schema/blocks/createSpec.d.ts +16 -36
  154. package/types/src/schema/blocks/internal.d.ts +11 -33
  155. package/types/src/schema/blocks/types.d.ts +181 -57
  156. package/types/src/schema/index.d.ts +1 -0
  157. package/types/src/schema/inlineContent/createSpec.d.ts +36 -2
  158. package/types/src/schema/inlineContent/internal.d.ts +7 -15
  159. package/types/src/schema/inlineContent/types.d.ts +15 -1
  160. package/types/src/schema/propTypes.d.ts +4 -4
  161. package/types/src/schema/schema.d.ts +40 -0
  162. package/types/src/schema/styles/createSpec.d.ts +6 -4
  163. package/types/src/schema/styles/internal.d.ts +6 -3
  164. package/types/src/schema/styles/types.d.ts +11 -2
  165. package/types/src/util/topo-sort.d.ts +18 -0
  166. package/types/src/util/topo-sort.test.d.ts +1 -0
  167. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +0 -144
  168. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +0 -445
  169. package/src/blocks/FileBlockContent/FileBlockContent.ts +0 -100
  170. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +0 -159
  171. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +0 -159
  172. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +0 -134
  173. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +0 -299
  174. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +0 -86
  175. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +0 -172
  176. package/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.ts +0 -104
  177. package/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts +0 -49
  178. package/src/blocks/PageBreakBlockContent/schema.ts +0 -40
  179. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +0 -78
  180. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +0 -121
  181. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +0 -158
  182. package/src/editor/BlockNoteSchema.ts +0 -107
  183. package/src/editor/BlockNoteTipTapEditor.ts +0 -335
  184. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +0 -99
  185. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +0 -90
  186. package/types/src/blocks/FileBlockContent/helpers/render/createAddFileButton.d.ts +0 -6
  187. package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +0 -9
  188. package/types/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.d.ts +0 -9
  189. package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +0 -67
  190. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +0 -131
  191. package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +0 -46
  192. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +0 -55
  193. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +0 -2
  194. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +0 -58
  195. package/types/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.d.ts +0 -46
  196. package/types/src/blocks/PageBreakBlockContent/PageBreakBlockContent.d.ts +0 -31
  197. package/types/src/blocks/PageBreakBlockContent/schema.d.ts +0 -86
  198. package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +0 -52
  199. package/types/src/blocks/QuoteBlockContent/QuoteBlockContent.d.ts +0 -52
  200. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +0 -39
  201. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +0 -131
  202. package/types/src/editor/BlockNoteSchema.d.ts +0 -34
  203. package/types/src/editor/BlockNoteTipTapEditor.d.ts +0 -43
  204. /package/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.ts +0 -0
  205. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.ts +0 -0
  206. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.ts +0 -0
  207. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.ts +0 -0
  208. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.ts +0 -0
  209. /package/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
  210. /package/src/blocks/{ImageBlockContent → Image}/parseImageElement.ts +0 -0
  211. /package/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.ts +0 -0
  212. /package/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.ts +0 -0
  213. /package/src/blocks/{TableBlockContent → Table}/TableExtension.ts +0 -0
  214. /package/src/blocks/{VideoBlockContent → Video}/parseVideoElement.ts +0 -0
  215. /package/types/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.d.ts +0 -0
  216. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.d.ts +0 -0
  217. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.d.ts +0 -0
  218. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.d.ts +0 -0
  219. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.d.ts +0 -0
  220. /package/types/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
  221. /package/types/src/blocks/{ImageBlockContent → Image}/parseImageElement.d.ts +0 -0
  222. /package/types/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.d.ts +0 -0
  223. /package/types/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.d.ts +0 -0
  224. /package/types/src/blocks/{TableBlockContent → Table}/TableExtension.d.ts +0 -0
  225. /package/types/src/blocks/{VideoBlockContent → Video}/parseVideoElement.d.ts +0 -0
@@ -0,0 +1,138 @@
1
+ import { createBlockConfig, createBlockSpec } from "../../schema/index.js";
2
+ import { createBlockNoteExtension } from "../../editor/BlockNoteExtension.js";
3
+ import {
4
+ addDefaultPropsExternalHTML,
5
+ defaultProps,
6
+ parseDefaultProps,
7
+ } from "../defaultProps.js";
8
+ import { createToggleWrapper } from "../ToggleWrapper/createToggleWrapper.js";
9
+
10
+ const HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;
11
+
12
+ export interface HeadingOptions {
13
+ defaultLevel?: (typeof HEADING_LEVELS)[number];
14
+ levels?: readonly number[];
15
+ // TODO should probably use composition instead of this
16
+ allowToggleHeadings?: boolean;
17
+ }
18
+
19
+ export type HeadingBlockConfig = ReturnType<typeof createHeadingBlockConfig>;
20
+
21
+ export const createHeadingBlockConfig = createBlockConfig(
22
+ ({
23
+ defaultLevel = 1,
24
+ levels = HEADING_LEVELS,
25
+ allowToggleHeadings = true,
26
+ }: HeadingOptions = {}) =>
27
+ ({
28
+ type: "heading" as const,
29
+ propSchema: {
30
+ ...defaultProps,
31
+ level: { default: defaultLevel, values: levels },
32
+ ...(allowToggleHeadings
33
+ ? { isToggleable: { default: false, optional: true } as const }
34
+ : {}),
35
+ },
36
+ content: "inline",
37
+ }) as const,
38
+ );
39
+
40
+ export const createHeadingBlockSpec = createBlockSpec(
41
+ createHeadingBlockConfig,
42
+ ({ allowToggleHeadings = true }: HeadingOptions = {}) => ({
43
+ meta: {
44
+ isolating: false,
45
+ },
46
+ parse(e) {
47
+ let level: number;
48
+ switch (e.tagName) {
49
+ case "H1":
50
+ level = 1;
51
+ break;
52
+ case "H2":
53
+ level = 2;
54
+ break;
55
+ case "H3":
56
+ level = 3;
57
+ break;
58
+ case "H4":
59
+ level = 4;
60
+ break;
61
+ case "H5":
62
+ level = 5;
63
+ break;
64
+ case "H6":
65
+ level = 6;
66
+ break;
67
+ default:
68
+ return undefined;
69
+ }
70
+
71
+ return {
72
+ ...parseDefaultProps(e),
73
+ level,
74
+ };
75
+ },
76
+ render(block, editor) {
77
+ const dom = document.createElement(`h${block.props.level}`);
78
+
79
+ if (allowToggleHeadings) {
80
+ const toggleWrapper = createToggleWrapper(block, editor, dom);
81
+ return { ...toggleWrapper, contentDOM: dom };
82
+ }
83
+
84
+ return {
85
+ dom,
86
+ contentDOM: dom,
87
+ };
88
+ },
89
+ toExternalHTML(block) {
90
+ const dom = document.createElement(`h${block.props.level}`);
91
+ addDefaultPropsExternalHTML(block.props, dom);
92
+
93
+ return {
94
+ dom,
95
+ contentDOM: dom,
96
+ };
97
+ },
98
+ }),
99
+ ({ levels = HEADING_LEVELS }: HeadingOptions = {}) => [
100
+ createBlockNoteExtension({
101
+ key: "heading-shortcuts",
102
+ keyboardShortcuts: Object.fromEntries(
103
+ levels.map((level) => [
104
+ `Mod-Alt-${level}`,
105
+ ({ editor }) => {
106
+ const cursorPosition = editor.getTextCursorPosition();
107
+
108
+ if (
109
+ editor.schema.blockSchema[cursorPosition.block.type].content !==
110
+ "inline"
111
+ ) {
112
+ return false;
113
+ }
114
+
115
+ editor.updateBlock(cursorPosition.block, {
116
+ type: "heading",
117
+ props: {
118
+ level: level as any,
119
+ },
120
+ });
121
+ return true;
122
+ },
123
+ ]) ?? [],
124
+ ),
125
+ inputRules: levels.map((level) => ({
126
+ find: new RegExp(`^(#{${level}})\\s$`),
127
+ replace({ match }: { match: RegExpMatchArray }) {
128
+ return {
129
+ type: "heading",
130
+ props: {
131
+ level: match[1].length,
132
+ },
133
+ };
134
+ },
135
+ })),
136
+ }),
137
+ ],
138
+ );
@@ -0,0 +1,190 @@
1
+ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
2
+ import {
3
+ BlockFromConfig,
4
+ createBlockConfig,
5
+ createBlockSpec,
6
+ } from "../../schema/index.js";
7
+ import { defaultProps, parseDefaultProps } from "../defaultProps.js";
8
+ import { parseFigureElement } from "../File/helpers/parse/parseFigureElement.js";
9
+ import { createResizableFileBlockWrapper } from "../File/helpers/render/createResizableFileBlockWrapper.js";
10
+ import { createFigureWithCaption } from "../File/helpers/toExternalHTML/createFigureWithCaption.js";
11
+ import { createLinkWithCaption } from "../File/helpers/toExternalHTML/createLinkWithCaption.js";
12
+ import { parseImageElement } from "./parseImageElement.js";
13
+
14
+ export const FILE_IMAGE_ICON_SVG =
15
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 11.1005L7 9.1005L12.5 14.6005L16 11.1005L19 14.1005V5H5V11.1005ZM4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3ZM15.5 10C14.6716 10 14 9.32843 14 8.5C14 7.67157 14.6716 7 15.5 7C16.3284 7 17 7.67157 17 8.5C17 9.32843 16.3284 10 15.5 10Z"></path></svg>';
16
+
17
+ export interface ImageOptions {
18
+ icon?: string;
19
+ }
20
+
21
+ export type ImageBlockConfig = ReturnType<typeof createImageBlockConfig>;
22
+
23
+ export const createImageBlockConfig = createBlockConfig(
24
+ (_ctx: ImageOptions = {}) =>
25
+ ({
26
+ type: "image" as const,
27
+ propSchema: {
28
+ textAlignment: defaultProps.textAlignment,
29
+ backgroundColor: defaultProps.backgroundColor,
30
+ // File name.
31
+ name: {
32
+ default: "" as const,
33
+ },
34
+ // File url.
35
+ url: {
36
+ default: "" as const,
37
+ },
38
+ // File caption.
39
+ caption: {
40
+ default: "" as const,
41
+ },
42
+
43
+ showPreview: {
44
+ default: true,
45
+ },
46
+ // File preview width in px.
47
+ previewWidth: {
48
+ default: undefined,
49
+ type: "number" as const,
50
+ },
51
+ },
52
+ content: "none" as const,
53
+ }) as const,
54
+ );
55
+
56
+ export const imageParse =
57
+ (_config: ImageOptions = {}) =>
58
+ (element: HTMLElement) => {
59
+ if (element.tagName === "IMG") {
60
+ // Ignore if parent figure has already been parsed.
61
+ if (element.closest("figure")) {
62
+ return undefined;
63
+ }
64
+
65
+ const { backgroundColor } = parseDefaultProps(element);
66
+
67
+ return {
68
+ ...parseImageElement(element as HTMLImageElement),
69
+ backgroundColor,
70
+ };
71
+ }
72
+
73
+ if (element.tagName === "FIGURE") {
74
+ const parsedFigure = parseFigureElement(element, "img");
75
+ if (!parsedFigure) {
76
+ return undefined;
77
+ }
78
+
79
+ const { targetElement, caption } = parsedFigure;
80
+
81
+ const { backgroundColor } = parseDefaultProps(element);
82
+
83
+ return {
84
+ ...parseImageElement(targetElement as HTMLImageElement),
85
+ backgroundColor,
86
+ caption,
87
+ };
88
+ }
89
+
90
+ return undefined;
91
+ };
92
+
93
+ export const imageRender =
94
+ (config: ImageOptions = {}) =>
95
+ (
96
+ block: BlockFromConfig<ReturnType<typeof createImageBlockConfig>, any, any>,
97
+ editor: BlockNoteEditor<
98
+ Record<"image", ReturnType<typeof createImageBlockConfig>>,
99
+ any,
100
+ any
101
+ >,
102
+ ) => {
103
+ const icon = document.createElement("div");
104
+ icon.innerHTML = config.icon ?? FILE_IMAGE_ICON_SVG;
105
+
106
+ const imageWrapper = document.createElement("div");
107
+ imageWrapper.className = "bn-visual-media-wrapper";
108
+
109
+ const image = document.createElement("img");
110
+ image.className = "bn-visual-media";
111
+ if (editor.resolveFileUrl) {
112
+ editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
113
+ image.src = downloadUrl;
114
+ });
115
+ } else {
116
+ image.src = block.props.url;
117
+ }
118
+
119
+ image.alt = block.props.name || block.props.caption || "BlockNote image";
120
+ image.contentEditable = "false";
121
+ image.draggable = false;
122
+ imageWrapper.appendChild(image);
123
+
124
+ return createResizableFileBlockWrapper(
125
+ block,
126
+ editor,
127
+ { dom: imageWrapper },
128
+ imageWrapper,
129
+ icon.firstElementChild as HTMLElement,
130
+ );
131
+ };
132
+
133
+ export const imageToExternalHTML =
134
+ (_config: ImageOptions = {}) =>
135
+ (
136
+ block: BlockFromConfig<ReturnType<typeof createImageBlockConfig>, any, any>,
137
+ _editor: BlockNoteEditor<
138
+ Record<"image", ReturnType<typeof createImageBlockConfig>>,
139
+ any,
140
+ any
141
+ >,
142
+ ) => {
143
+ if (!block.props.url) {
144
+ const div = document.createElement("p");
145
+ div.textContent = "Add image";
146
+
147
+ return {
148
+ dom: div,
149
+ };
150
+ }
151
+
152
+ let image;
153
+ if (block.props.showPreview) {
154
+ image = document.createElement("img");
155
+ image.src = block.props.url;
156
+ image.alt = block.props.name || block.props.caption || "BlockNote image";
157
+ if (block.props.previewWidth) {
158
+ image.width = block.props.previewWidth;
159
+ }
160
+ } else {
161
+ image = document.createElement("a");
162
+ image.href = block.props.url;
163
+ image.textContent = block.props.name || block.props.url;
164
+ }
165
+
166
+ if (block.props.caption) {
167
+ if (block.props.showPreview) {
168
+ return createFigureWithCaption(image, block.props.caption);
169
+ } else {
170
+ return createLinkWithCaption(image, block.props.caption);
171
+ }
172
+ }
173
+
174
+ return {
175
+ dom: image,
176
+ };
177
+ };
178
+
179
+ export const createImageBlockSpec = createBlockSpec(
180
+ createImageBlockConfig,
181
+ (config) => ({
182
+ meta: {
183
+ fileBlockAccept: ["image/*"],
184
+ },
185
+ parse: imageParse(config),
186
+ render: imageRender(config),
187
+ toExternalHTML: imageToExternalHTML(config),
188
+ runsBefore: ["file"],
189
+ }),
190
+ );
@@ -0,0 +1,116 @@
1
+ import { createBlockNoteExtension } from "../../../editor/BlockNoteExtension.js";
2
+ import { createBlockConfig, createBlockSpec } from "../../../schema/index.js";
3
+ import {
4
+ addDefaultPropsExternalHTML,
5
+ defaultProps,
6
+ parseDefaultProps,
7
+ } from "../../defaultProps.js";
8
+ import { handleEnter } from "../../utils/listItemEnterHandler.js";
9
+ import { getListItemContent } from "../getListItemContent.js";
10
+
11
+ export type BulletListItemBlockConfig = ReturnType<
12
+ typeof createBulletListItemBlockConfig
13
+ >;
14
+
15
+ export const createBulletListItemBlockConfig = createBlockConfig(
16
+ () =>
17
+ ({
18
+ type: "bulletListItem" as const,
19
+ propSchema: {
20
+ ...defaultProps,
21
+ },
22
+ content: "inline",
23
+ }) as const,
24
+ );
25
+
26
+ export const createBulletListItemBlockSpec = createBlockSpec(
27
+ createBulletListItemBlockConfig,
28
+ {
29
+ meta: {
30
+ isolating: false,
31
+ },
32
+ parse(element) {
33
+ if (element.tagName !== "LI") {
34
+ return undefined;
35
+ }
36
+
37
+ const parent = element.parentElement;
38
+
39
+ if (parent === null) {
40
+ return undefined;
41
+ }
42
+
43
+ if (
44
+ parent.tagName === "UL" ||
45
+ (parent.tagName === "DIV" && parent.parentElement?.tagName === "UL")
46
+ ) {
47
+ return parseDefaultProps(element);
48
+ }
49
+
50
+ return undefined;
51
+ },
52
+ // As `li` elements can contain multiple paragraphs, we need to merge their contents
53
+ // into a single one so that ProseMirror can parse everything correctly.
54
+ parseContent: ({ el, schema }) =>
55
+ getListItemContent(el, schema, "bulletListItem"),
56
+ render() {
57
+ // We use a <p> tag, because for <li> tags we'd need a <ul> element to put
58
+ // them in to be semantically correct, which we can't have due to the
59
+ // schema.
60
+ const dom = document.createElement("p");
61
+
62
+ return {
63
+ dom,
64
+ contentDOM: dom,
65
+ };
66
+ },
67
+ toExternalHTML(block) {
68
+ const li = document.createElement("li");
69
+ const p = document.createElement("p");
70
+ addDefaultPropsExternalHTML(block.props, li);
71
+ li.appendChild(p);
72
+
73
+ return {
74
+ dom: li,
75
+ contentDOM: p,
76
+ };
77
+ },
78
+ },
79
+ [
80
+ createBlockNoteExtension({
81
+ key: "bullet-list-item-shortcuts",
82
+ keyboardShortcuts: {
83
+ Enter: ({ editor }) => {
84
+ return handleEnter(editor, "bulletListItem");
85
+ },
86
+ "Mod-Shift-8": ({ editor }) => {
87
+ const cursorPosition = editor.getTextCursorPosition();
88
+
89
+ if (
90
+ editor.schema.blockSchema[cursorPosition.block.type].content !==
91
+ "inline"
92
+ ) {
93
+ return false;
94
+ }
95
+
96
+ editor.updateBlock(cursorPosition.block, {
97
+ type: "bulletListItem",
98
+ props: {},
99
+ });
100
+ return true;
101
+ },
102
+ },
103
+ inputRules: [
104
+ {
105
+ find: new RegExp(`^[-+*]\\s$`),
106
+ replace() {
107
+ return {
108
+ type: "bulletListItem",
109
+ props: {},
110
+ };
111
+ },
112
+ },
113
+ ],
114
+ }),
115
+ ],
116
+ );
@@ -0,0 +1,175 @@
1
+ import { createBlockNoteExtension } from "../../../editor/BlockNoteExtension.js";
2
+ import { createBlockConfig, createBlockSpec } from "../../../schema/index.js";
3
+ import {
4
+ addDefaultPropsExternalHTML,
5
+ defaultProps,
6
+ parseDefaultProps,
7
+ } from "../../defaultProps.js";
8
+ import { handleEnter } from "../../utils/listItemEnterHandler.js";
9
+ import { getListItemContent } from "../getListItemContent.js";
10
+
11
+ export type CheckListItemBlockConfig = ReturnType<
12
+ typeof createCheckListItemConfig
13
+ >;
14
+
15
+ export const createCheckListItemConfig = createBlockConfig(
16
+ () =>
17
+ ({
18
+ type: "checkListItem" as const,
19
+ propSchema: {
20
+ ...defaultProps,
21
+ checked: { default: false, type: "boolean" },
22
+ },
23
+ content: "inline",
24
+ }) as const,
25
+ );
26
+
27
+ export const createCheckListItemBlockSpec = createBlockSpec(
28
+ createCheckListItemConfig,
29
+ {
30
+ meta: {
31
+ isolating: false,
32
+ },
33
+ parse(element) {
34
+ if (element.tagName === "input") {
35
+ // Ignore if we already parsed an ancestor list item to avoid double-parsing.
36
+ if (element.closest("[data-content-type]") || element.closest("li")) {
37
+ return undefined;
38
+ }
39
+
40
+ if ((element as HTMLInputElement).type === "checkbox") {
41
+ return { checked: (element as HTMLInputElement).checked };
42
+ }
43
+ return undefined;
44
+ }
45
+ if (element.tagName !== "LI") {
46
+ return undefined;
47
+ }
48
+
49
+ const parent = element.parentElement;
50
+
51
+ if (parent === null) {
52
+ return undefined;
53
+ }
54
+
55
+ if (
56
+ parent.tagName === "UL" ||
57
+ (parent.tagName === "DIV" && parent.parentElement?.tagName === "UL")
58
+ ) {
59
+ const checkbox =
60
+ (element.querySelector("input[type=checkbox]") as HTMLInputElement) ||
61
+ null;
62
+
63
+ if (checkbox === null) {
64
+ return undefined;
65
+ }
66
+
67
+ return { ...parseDefaultProps(element), checked: checkbox.checked };
68
+ }
69
+
70
+ return;
71
+ },
72
+ // As `li` elements can contain multiple paragraphs, we need to merge their contents
73
+ // into a single one so that ProseMirror can parse everything correctly.
74
+ parseContent: ({ el, schema }) =>
75
+ getListItemContent(el, schema, "checkListItem"),
76
+ render(block, editor) {
77
+ const dom = document.createDocumentFragment();
78
+ const checkbox = document.createElement("input");
79
+ checkbox.type = "checkbox";
80
+ checkbox.checked = block.props.checked;
81
+ if (block.props.checked) {
82
+ checkbox.setAttribute("checked", "");
83
+ }
84
+ checkbox.addEventListener("change", () => {
85
+ editor.updateBlock(block, { props: { checked: !block.props.checked } });
86
+ });
87
+ // We use a <p> tag, because for <li> tags we'd need a <ul> element to put
88
+ // them in to be semantically correct, which we can't have due to the
89
+ // schema.
90
+ const paragraph = document.createElement("p");
91
+
92
+ dom.appendChild(checkbox);
93
+ dom.appendChild(paragraph);
94
+
95
+ return {
96
+ dom,
97
+ contentDOM: paragraph,
98
+ };
99
+ },
100
+ toExternalHTML(block) {
101
+ const dom = document.createElement("li");
102
+ const checkbox = document.createElement("input");
103
+ checkbox.type = "checkbox";
104
+ checkbox.checked = block.props.checked;
105
+ if (block.props.checked) {
106
+ checkbox.setAttribute("checked", "");
107
+ }
108
+ // We use a <p> tag, because for <li> tags we'd need a <ul> element to put
109
+ // them in to be semantically correct, which we can't have due to the
110
+ // schema.
111
+ const paragraph = document.createElement("p");
112
+ addDefaultPropsExternalHTML(block.props, dom);
113
+
114
+ dom.appendChild(checkbox);
115
+ dom.appendChild(paragraph);
116
+
117
+ return {
118
+ dom,
119
+ contentDOM: paragraph,
120
+ };
121
+ },
122
+ runsBefore: ["bulletListItem"],
123
+ },
124
+ [
125
+ createBlockNoteExtension({
126
+ key: "check-list-item-shortcuts",
127
+ keyboardShortcuts: {
128
+ Enter: ({ editor }) => {
129
+ return handleEnter(editor, "checkListItem");
130
+ },
131
+ "Mod-Shift-9": ({ editor }) => {
132
+ const cursorPosition = editor.getTextCursorPosition();
133
+
134
+ if (
135
+ editor.schema.blockSchema[cursorPosition.block.type].content !==
136
+ "inline"
137
+ ) {
138
+ return false;
139
+ }
140
+
141
+ editor.updateBlock(cursorPosition.block, {
142
+ type: "checkListItem",
143
+ props: {},
144
+ });
145
+ return true;
146
+ },
147
+ },
148
+ inputRules: [
149
+ {
150
+ find: new RegExp(`\\[\\s*\\]\\s$`),
151
+ replace() {
152
+ return {
153
+ type: "checkListItem",
154
+ props: {
155
+ checked: false,
156
+ },
157
+ content: [],
158
+ };
159
+ },
160
+ },
161
+ {
162
+ find: new RegExp(`\\[[Xx]\\]\\s$`),
163
+ replace() {
164
+ return {
165
+ type: "checkListItem",
166
+ props: {
167
+ checked: true,
168
+ },
169
+ };
170
+ },
171
+ },
172
+ ],
173
+ }),
174
+ ],
175
+ );