@blocknote/core 0.1.0 → 0.1.2
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.
- package/README.md +1 -1
- package/dist/blocknote.js +3454 -2426
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +35 -71
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +7 -6
- package/src/BlockNoteTheme.ts +150 -0
- package/src/extensions/Blocks/BlockAttributes.ts +12 -0
- package/src/extensions/Blocks/MultipleNodeSelection.ts +87 -0
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +8 -2
- package/src/extensions/Blocks/nodes/Block.module.css +37 -37
- package/src/extensions/Blocks/nodes/Block.ts +79 -32
- package/src/extensions/Blocks/nodes/BlockGroup.ts +18 -1
- package/src/extensions/Blocks/nodes/Content.ts +14 -1
- package/src/extensions/BubbleMenu/BubbleMenuExtension.tsx +8 -1
- package/src/extensions/BubbleMenu/component/BubbleMenu.tsx +116 -88
- package/src/extensions/BubbleMenu/component/LinkToolbarButton.tsx +8 -8
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx +143 -33
- package/src/extensions/DraggableBlocks/components/DragHandle.tsx +14 -19
- package/src/extensions/DraggableBlocks/components/DragHandleMenu.tsx +8 -7
- package/src/extensions/Hyperlinks/HyperlinkMenuPlugin.tsx +31 -66
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.tsx +44 -0
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.tsx +34 -0
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.tsx +31 -0
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.tsx +40 -0
- package/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.tsx +37 -0
- package/src/extensions/Hyperlinks/menus/HyperlinkMenu.tsx +63 -0
- package/src/extensions/SlashMenu/SlashMenuItem.ts +3 -1
- package/src/extensions/SlashMenu/defaultCommands.tsx +4 -4
- package/src/extensions/UniqueID/UniqueID.ts +0 -11
- package/src/shared/components/toolbar/Toolbar.tsx +8 -3
- package/src/shared/components/toolbar/ToolbarButton.tsx +57 -0
- package/src/shared/components/toolbar/ToolbarDropdown.tsx +35 -0
- package/src/shared/components/toolbar/ToolbarDropdownItem.tsx +35 -0
- package/src/shared/components/toolbar/ToolbarDropdownTarget.tsx +31 -0
- package/src/shared/plugins/suggestion/SuggestionItem.ts +3 -1
- package/src/shared/plugins/suggestion/{SuggestionListReactRenderer.ts → SuggestionListReactRenderer.tsx} +13 -4
- package/src/shared/plugins/suggestion/components/SuggestionGroup.tsx +6 -93
- package/src/shared/plugins/suggestion/components/SuggestionGroupItem.tsx +82 -0
- package/src/shared/plugins/suggestion/components/SuggestionList.tsx +24 -23
- package/src/utils.ts +12 -0
- package/types/src/BlockNoteTheme.d.ts +2 -0
- package/types/src/commands/indentation.d.ts +2 -0
- package/types/src/extensions/Blocks/BlockAttributes.d.ts +2 -0
- package/types/src/extensions/Blocks/MultipleNodeSelection.d.ts +24 -0
- package/types/src/extensions/Blocks/nodes/Block.d.ts +1 -1
- package/types/src/extensions/BubbleMenu/component/LinkToolbarButton.d.ts +2 -2
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.d.ts +11 -0
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.d.ts +13 -0
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.d.ts +8 -0
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.d.ts +9 -0
- package/types/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.d.ts +12 -0
- package/types/src/extensions/Hyperlinks/menus/HyperlinkMenu.d.ts +21 -0
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInput.d.ts +39 -0
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInputStyles.d.ts +3 -0
- package/types/src/extensions/Hyperlinks/menus/helpers/ToolbarComponent.d.ts +13 -0
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +4 -7
- package/types/src/nodes/ChildgroupNode.d.ts +28 -0
- package/types/src/nodes/patchNodes.d.ts +1 -0
- package/types/src/plugins/TreeViewPlugin/index.d.ts +2 -0
- package/types/src/plugins/animation.d.ts +2 -0
- package/types/src/react/BlockNoteComposer.d.ts +17 -0
- package/types/src/react/BlockNotePlugin.d.ts +1 -0
- package/types/src/react/index.d.ts +3 -0
- package/types/src/react/useBlockNoteSetup.d.ts +2 -0
- package/types/src/registerBlockNote.d.ts +2 -0
- package/types/src/shared/components/toolbar/SimpleToolbarButton.d.ts +2 -3
- package/types/src/shared/components/toolbar/SimpleToolbarDropdown.d.ts +11 -0
- package/types/src/shared/components/toolbar/SimpleToolbarDropdownItem.d.ts +11 -0
- package/types/src/shared/components/toolbar/Toolbar.d.ts +2 -2
- package/types/src/shared/components/toolbar/ToolbarButton.d.ts +15 -0
- package/types/src/shared/components/toolbar/ToolbarDropdown.d.ts +17 -0
- package/types/src/shared/components/toolbar/ToolbarDropdownItem.d.ts +11 -0
- package/types/src/shared/components/toolbar/ToolbarDropdownTarget.d.ts +8 -0
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +2 -4
- package/types/src/shared/plugins/suggestion/components/SuggestionGroupItem.d.ts +9 -0
- package/types/src/shared/plugins/suggestion/components/SuggestionList.d.ts +0 -15
- package/types/src/themes/BlockNoteEditorTheme.d.ts +11 -0
- package/types/src/utils.d.ts +2 -0
- package/src/extensions/BubbleMenu/component/DropdownBlockItem.module.css +0 -13
- package/src/extensions/BubbleMenu/component/DropdownBlockItem.tsx +0 -25
- package/src/extensions/DraggableBlocks/components/DragHandle.module.css +0 -33
- package/src/extensions/DraggableBlocks/components/DragHandleMenu.module.css +0 -10
- package/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.tsx +0 -59
- package/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.tsx +0 -72
- package/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.tsx +0 -173
- package/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.ts +0 -36
- package/src/extensions/Hyperlinks/menus/atlaskit/README.md +0 -1
- package/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.tsx +0 -61
- package/src/extensions/helpers/formatKeyboardShortcut.ts +0 -9
- package/src/lib/atlaskit/browser.ts +0 -47
- package/src/shared/components/toolbar/SimpleToolbarButton.module.css +0 -13
- package/src/shared/components/toolbar/SimpleToolbarButton.tsx +0 -56
- package/src/shared/components/toolbar/Toolbar.module.css +0 -10
- package/src/shared/components/toolbar/ToolbarSeparator.module.css +0 -13
- package/src/shared/components/toolbar/ToolbarSeparator.tsx +0 -7
- package/src/shared/plugins/suggestion/components/SuggestionGroup.module.css +0 -45
- package/src/shared/plugins/suggestion/components/SuggestionList.module.css +0 -10
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://github.com/yousefed/blocknote",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.2",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"types",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"lint": "eslint src --max-warnings 0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
48
|
+
"@emotion/cache": "^11.10.5",
|
|
49
|
+
"@emotion/serialize": "^1.1.1",
|
|
50
|
+
"@emotion/utils": "^1.2.0",
|
|
51
|
+
"@mantine/core": "^5.6.1",
|
|
52
52
|
"@tippyjs/react": "^4.2.6",
|
|
53
53
|
"@tiptap/core": "^2.0.0-beta.182",
|
|
54
54
|
"@tiptap/extension-bold": "^2.0.0-beta.28",
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
"@types/uuid": "^8.3.4",
|
|
90
90
|
"eslint": "^8.10.0",
|
|
91
91
|
"eslint-config-react-app": "^7.0.0",
|
|
92
|
+
"prettier": "^2.7.1",
|
|
92
93
|
"typescript": "^4.5.4",
|
|
93
94
|
"vite": "^3.0.5",
|
|
94
95
|
"vite-plugin-eslint": "^1.7.0"
|
|
@@ -106,5 +107,5 @@
|
|
|
106
107
|
"access": "public",
|
|
107
108
|
"registry": "https://registry.npmjs.org/"
|
|
108
109
|
},
|
|
109
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "085650a27c92af2b54df6d8b0e73673180821ebe"
|
|
110
111
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { MantineThemeOverride } from "@mantine/core";
|
|
2
|
+
|
|
3
|
+
export const BlockNoteTheme: MantineThemeOverride = {
|
|
4
|
+
activeStyles: {
|
|
5
|
+
// Removes button press effect.
|
|
6
|
+
transform: "none",
|
|
7
|
+
},
|
|
8
|
+
colorScheme: "light",
|
|
9
|
+
colors: {
|
|
10
|
+
brandFinal: [
|
|
11
|
+
"#F6F6F8",
|
|
12
|
+
"#ECEDF0",
|
|
13
|
+
"#DFE1E6",
|
|
14
|
+
"#C2C7D0",
|
|
15
|
+
"#A6ADBA",
|
|
16
|
+
"#8993A4",
|
|
17
|
+
"#6D798F",
|
|
18
|
+
"#505F79",
|
|
19
|
+
"#344563",
|
|
20
|
+
"#172B4D",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
components: {
|
|
24
|
+
Menu: {
|
|
25
|
+
styles: (theme) => ({
|
|
26
|
+
dropdown: {
|
|
27
|
+
backgroundColor: "white",
|
|
28
|
+
boxShadow: `0px 4px 8px ${theme.colors.brandFinal[2]}, 0px 0px 1px ${theme.colors.brandFinal[2]}`,
|
|
29
|
+
border: `1px solid ${theme.colors.brandFinal[1]}`,
|
|
30
|
+
borderRadius: "6px",
|
|
31
|
+
padding: "2px",
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
DragHandleMenu: {
|
|
36
|
+
styles: (theme) => ({
|
|
37
|
+
root: {
|
|
38
|
+
".mantine-Menu-item": {
|
|
39
|
+
color: theme.colors.brandFinal,
|
|
40
|
+
fontSize: "12px",
|
|
41
|
+
height: "34px",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
EditHyperlinkMenu: {
|
|
47
|
+
styles: (theme) => ({
|
|
48
|
+
root: {
|
|
49
|
+
backgroundColor: "white",
|
|
50
|
+
boxShadow: `0px 4px 8px ${theme.colors.brandFinal[2]}, 0px 0px 1px ${theme.colors.brandFinal[2]}`,
|
|
51
|
+
border: `1px solid ${theme.colors.brandFinal[1]}`,
|
|
52
|
+
borderRadius: "6px",
|
|
53
|
+
gap: "4px",
|
|
54
|
+
minWidth: "145px",
|
|
55
|
+
padding: "2px",
|
|
56
|
+
// Row
|
|
57
|
+
".mantine-Group-root": {
|
|
58
|
+
flexWrap: "nowrap",
|
|
59
|
+
gap: "8px",
|
|
60
|
+
paddingInline: "6px",
|
|
61
|
+
// Row icon
|
|
62
|
+
".mantine-Container-root": {
|
|
63
|
+
color: theme.colors.brandFinal,
|
|
64
|
+
display: "flex",
|
|
65
|
+
justifyContent: "center",
|
|
66
|
+
padding: "0",
|
|
67
|
+
width: "fit-content",
|
|
68
|
+
},
|
|
69
|
+
// Row input field
|
|
70
|
+
".mantine-TextInput-root": {
|
|
71
|
+
background: "transparent",
|
|
72
|
+
width: "300px",
|
|
73
|
+
".mantine-TextInput-wrapper": {
|
|
74
|
+
".mantine-TextInput-input": {
|
|
75
|
+
fontSize: "12px",
|
|
76
|
+
border: 0,
|
|
77
|
+
padding: 0,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
Toolbar: {
|
|
86
|
+
styles: (theme) => ({
|
|
87
|
+
root: {
|
|
88
|
+
backgroundColor: "white",
|
|
89
|
+
boxShadow: `0px 4px 8px ${theme.colors.brandFinal[2]}, 0px 0px 1px ${theme.colors.brandFinal[2]}`,
|
|
90
|
+
border: `1px solid ${theme.colors.brandFinal[1]}`,
|
|
91
|
+
borderRadius: "6px",
|
|
92
|
+
flexWrap: "nowrap",
|
|
93
|
+
gap: "2px",
|
|
94
|
+
padding: "2px",
|
|
95
|
+
width: "fit-content",
|
|
96
|
+
// Button (including dropdown target)
|
|
97
|
+
".mantine-UnstyledButton-root": {
|
|
98
|
+
borderRadius: "4px",
|
|
99
|
+
},
|
|
100
|
+
// Dropdown
|
|
101
|
+
".mantine-Menu-dropdown": {
|
|
102
|
+
// Dropdown item
|
|
103
|
+
".mantine-Menu-item": {
|
|
104
|
+
color: theme.colors.brandFinal,
|
|
105
|
+
fontSize: "12px",
|
|
106
|
+
height: "34px",
|
|
107
|
+
".mantine-Menu-itemRightSection": {
|
|
108
|
+
paddingLeft: "5px",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
SuggestionList: {
|
|
116
|
+
styles: (theme) => ({
|
|
117
|
+
root: {
|
|
118
|
+
// ...theme.other.defaultMenuStyles(theme),
|
|
119
|
+
".mantine-Menu-item": {
|
|
120
|
+
// Icon
|
|
121
|
+
".mantine-Menu-itemIcon": {
|
|
122
|
+
padding: "8px",
|
|
123
|
+
border: `1px solid ${theme.colors.brandFinal[2]}`,
|
|
124
|
+
backgroundColor: theme.colors.brandFinal[0],
|
|
125
|
+
borderRadius: "4px",
|
|
126
|
+
color: theme.colors.brandFinal,
|
|
127
|
+
},
|
|
128
|
+
// Text
|
|
129
|
+
".mantine-Menu-itemLabel": {
|
|
130
|
+
color: theme.colors.brandFinal,
|
|
131
|
+
paddingRight: "16px",
|
|
132
|
+
".mantine-Stack-root": {
|
|
133
|
+
gap: "0",
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
// Badge (keyboard shortcut)
|
|
137
|
+
".mantine-Menu-itemRightSection": {
|
|
138
|
+
".mantine-Badge-root": {
|
|
139
|
+
border: `1px solid ${theme.colors.brandFinal[2]}`,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
fontFamily: "Inter",
|
|
148
|
+
primaryColor: "brandFinal",
|
|
149
|
+
primaryShade: 9,
|
|
150
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Object containing all possible block attributes.
|
|
2
|
+
const BlockAttributes: Record<string, string> = {
|
|
3
|
+
listType: "data-list-type",
|
|
4
|
+
blockColor: "data-block-color",
|
|
5
|
+
blockStyle: "data-block-style",
|
|
6
|
+
headingType: "data-heading-type",
|
|
7
|
+
id: "data-id",
|
|
8
|
+
depth: "data-depth",
|
|
9
|
+
depthChange: "data-depth-change",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default BlockAttributes;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Selection } from "prosemirror-state";
|
|
2
|
+
import { Fragment, Node, ResolvedPos, Slice } from "prosemirror-model";
|
|
3
|
+
import { Mappable } from "prosemirror-transform";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This class represents an editor selection which spans multiple nodes/blocks. It's currently only used to allow users
|
|
7
|
+
* to drag multiple blocks at the same time. Expects the selection anchor and head to be between nodes, i.e. just before
|
|
8
|
+
* the first target node and just after the last, and that anchor and head are at the same nesting level.
|
|
9
|
+
*
|
|
10
|
+
* Partially based on ProseMirror's NodeSelection implementation:
|
|
11
|
+
* (https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.ts)
|
|
12
|
+
* MultipleNodeSelection differs from NodeSelection in the following ways:
|
|
13
|
+
* 1. Stores which nodes are included in the selection instead of just a single node.
|
|
14
|
+
* 2. Already expects the selection to start just before the first target node and ends just after the last, while a
|
|
15
|
+
* NodeSelection automatically sets both anchor and head to just before the single target node.
|
|
16
|
+
*/
|
|
17
|
+
export class MultipleNodeSelection extends Selection {
|
|
18
|
+
nodes: Array<Node>;
|
|
19
|
+
|
|
20
|
+
constructor($anchor: ResolvedPos, $head: ResolvedPos) {
|
|
21
|
+
super($anchor, $head);
|
|
22
|
+
|
|
23
|
+
// Parent is at the same nesting level as anchor/head since they are just before/ just after target nodes.
|
|
24
|
+
const parentNode = $anchor.node();
|
|
25
|
+
|
|
26
|
+
this.nodes = [];
|
|
27
|
+
$anchor.doc.nodesBetween($anchor.pos, $head.pos, (node, _pos, parent) => {
|
|
28
|
+
if (parent !== null && parent.eq(parentNode)) {
|
|
29
|
+
this.nodes.push(node);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static create(doc: Node, from: number, to = from): MultipleNodeSelection {
|
|
37
|
+
return new MultipleNodeSelection(doc.resolve(from), doc.resolve(to));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
content(): Slice {
|
|
41
|
+
return new Slice(Fragment.from(this.nodes), 0, 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
eq(selection: Selection): boolean {
|
|
45
|
+
if (!(selection instanceof MultipleNodeSelection)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (this.nodes.length !== selection.nodes.length) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (this.from !== selection.from || this.to !== selection.to) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
58
|
+
if (!this.nodes[i].eq(selection.nodes[i])) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
map(doc: Node, mapping: Mappable): Selection {
|
|
67
|
+
let fromResult = mapping.mapResult(this.from);
|
|
68
|
+
let toResult = mapping.mapResult(this.to);
|
|
69
|
+
|
|
70
|
+
if (toResult.deleted) {
|
|
71
|
+
return Selection.near(doc.resolve(fromResult.pos));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (fromResult.deleted) {
|
|
75
|
+
return Selection.near(doc.resolve(toResult.pos));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return new MultipleNodeSelection(
|
|
79
|
+
doc.resolve(fromResult.pos),
|
|
80
|
+
doc.resolve(toResult.pos)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
toJSON(): any {
|
|
85
|
+
return { type: "node", anchor: this.anchor, head: this.head };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -5,9 +5,14 @@ import {
|
|
|
5
5
|
} from "@tiptap/core";
|
|
6
6
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
7
7
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
8
|
+
import BlockAttributes from "./BlockAttributes";
|
|
8
9
|
|
|
9
10
|
const PLUGIN_KEY = new PluginKey(`previous-blocks`);
|
|
10
11
|
|
|
12
|
+
// Inserts "prev-" string into an HTML attribute name with a "data-" prefix, e.g. "data-depth" -> "data-prev-depth".
|
|
13
|
+
// Assumes "data-" prefix is in the attribute name.
|
|
14
|
+
const insertPrev = (attr: string) => attr.slice(0, 5) + "prev-" + attr.slice(5);
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* This plugin tracks transformation of Block node attributes, so we can support CSS transitions.
|
|
13
18
|
*
|
|
@@ -129,8 +134,9 @@ export const PreviousBlockTypePlugin = () => {
|
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
const decorationAttributes: any = {};
|
|
132
|
-
for (let [
|
|
133
|
-
decorationAttributes[
|
|
137
|
+
for (let [nodeAttr, val] of Object.entries(prevAttrs)) {
|
|
138
|
+
decorationAttributes[insertPrev(BlockAttributes[nodeAttr])] =
|
|
139
|
+
val || "none";
|
|
134
140
|
}
|
|
135
141
|
const decoration = Decoration.node(pos, pos + node.nodeSize, {
|
|
136
142
|
...decorationAttributes,
|
|
@@ -40,7 +40,7 @@ NESTED BLOCKS
|
|
|
40
40
|
position: relative;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
.blockGroup .blockGroup > .blockOuter:not([data-prev-
|
|
43
|
+
.blockGroup .blockGroup > .blockOuter:not([data-prev-depth-changed])::before {
|
|
44
44
|
content: " ";
|
|
45
45
|
display: inline;
|
|
46
46
|
border-left: 1px solid #ccc;
|
|
@@ -50,71 +50,71 @@ NESTED BLOCKS
|
|
|
50
50
|
transition: all 0.2s 0.1s;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
.blockGroup .blockGroup > .blockOuter[data-prev-
|
|
53
|
+
.blockGroup .blockGroup > .blockOuter[data-prev-depth-change="-2"]::before {
|
|
54
54
|
height: 0;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/* NESTED BLOCK ANIMATIONS */
|
|
58
58
|
|
|
59
|
-
[data-prev-
|
|
59
|
+
[data-prev-depth-change="1"] {
|
|
60
60
|
--x: 1;
|
|
61
61
|
}
|
|
62
|
-
[data-prev-
|
|
62
|
+
[data-prev-depth-change="2"] {
|
|
63
63
|
--x: 2;
|
|
64
64
|
}
|
|
65
|
-
[data-prev-
|
|
65
|
+
[data-prev-depth-change="3"] {
|
|
66
66
|
--x: 3;
|
|
67
67
|
}
|
|
68
|
-
[data-prev-
|
|
68
|
+
[data-prev-depth-change="4"] {
|
|
69
69
|
--x: 4;
|
|
70
70
|
}
|
|
71
|
-
[data-prev-
|
|
71
|
+
[data-prev-depth-change="5"] {
|
|
72
72
|
--x: 5;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
[data-prev-
|
|
75
|
+
[data-prev-depth-change="-1"] {
|
|
76
76
|
--x: -1;
|
|
77
77
|
}
|
|
78
|
-
[data-prev-
|
|
78
|
+
[data-prev-depth-change="-2"] {
|
|
79
79
|
--x: -2;
|
|
80
80
|
}
|
|
81
|
-
[data-prev-
|
|
81
|
+
[data-prev-depth-change="-3"] {
|
|
82
82
|
--x: -3;
|
|
83
83
|
}
|
|
84
|
-
[data-prev-
|
|
84
|
+
[data-prev-depth-change="-4"] {
|
|
85
85
|
--x: -4;
|
|
86
86
|
}
|
|
87
|
-
[data-prev-
|
|
87
|
+
[data-prev-depth-change="-5"] {
|
|
88
88
|
--x: -5;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
.blockOuter[data-prev-
|
|
91
|
+
.blockOuter[data-prev-depth-change] {
|
|
92
92
|
margin-left: calc(10px * var(--x));
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
.blockOuter[data-prev-
|
|
95
|
+
.blockOuter[data-prev-depth-change] .blockOuter[data-prev-depth-change] {
|
|
96
96
|
margin-left: 0;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/* HEADINGS*/
|
|
100
|
-
.blockOuter[data-prev-
|
|
101
|
-
.blockOuter[data-
|
|
100
|
+
.blockOuter[data-prev-heading-type="1"] > .block > div:first-child,
|
|
101
|
+
.blockOuter[data-heading-type="1"]:not([data-prev-heading-type])
|
|
102
102
|
> .block
|
|
103
103
|
> div:first-child {
|
|
104
104
|
font-size: 3em;
|
|
105
105
|
font-weight: bold;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
.blockOuter[data-prev-
|
|
109
|
-
.blockOuter[data-
|
|
108
|
+
.blockOuter[data-prev-heading-type="2"] > .block > div:first-child,
|
|
109
|
+
.blockOuter[data-heading-type="2"]:not([data-prev-heading-type])
|
|
110
110
|
> .block
|
|
111
111
|
> div:first-child {
|
|
112
112
|
font-size: 2em;
|
|
113
113
|
font-weight: bold;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
.blockOuter[data-prev-
|
|
117
|
-
.blockOuter[data-
|
|
116
|
+
.blockOuter[data-prev-heading-type="3"] > .block > div:first-child,
|
|
117
|
+
.blockOuter[data-heading-type="3"]:not([data-prev-heading-type])
|
|
118
118
|
> .block
|
|
119
119
|
> div:first-child {
|
|
120
120
|
font-size: 1.3em;
|
|
@@ -129,8 +129,8 @@ NESTED BLOCKS
|
|
|
129
129
|
margin-left: 0px;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
.blockOuter[data-prev-
|
|
133
|
-
.blockOuter[data-
|
|
132
|
+
.blockOuter[data-prev-list-type="oli"] > .block > div:first-child::before,
|
|
133
|
+
.blockOuter[data-list-type="oli"]:not([data-prev-list-type])
|
|
134
134
|
> .block
|
|
135
135
|
> div:first-child::before {
|
|
136
136
|
content: attr(data-position);
|
|
@@ -138,8 +138,8 @@ NESTED BLOCKS
|
|
|
138
138
|
padding-left: 0px;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
.blockOuter[data-prev-
|
|
142
|
-
.blockOuter[data-
|
|
141
|
+
.blockOuter[data-prev-list-type="li"] > .block > div:first-child::before,
|
|
142
|
+
.blockOuter[data-list-type="li"]:not([data-prev-list-type])
|
|
143
143
|
> .block
|
|
144
144
|
> div:first-child::before {
|
|
145
145
|
content: "•";
|
|
@@ -151,16 +151,16 @@ NESTED BLOCKS
|
|
|
151
151
|
/* margin-left: 0.2em; */
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
.blockOuter[data-
|
|
154
|
+
.blockOuter[data-list-type="li"]
|
|
155
155
|
> .block
|
|
156
156
|
> .blockGroup
|
|
157
|
-
> .blockOuter[data-prev-
|
|
157
|
+
> .blockOuter[data-prev-list-type="li"]
|
|
158
158
|
> .block
|
|
159
159
|
> div:first-child::before,
|
|
160
|
-
.blockOuter[data-
|
|
160
|
+
.blockOuter[data-list-type="li"]
|
|
161
161
|
> .block
|
|
162
162
|
> .blockGroup
|
|
163
|
-
> .blockOuter[data-
|
|
163
|
+
> .blockOuter[data-list-type="li"]:not([data-prev-list-type])
|
|
164
164
|
> .block
|
|
165
165
|
> div:first-child::before {
|
|
166
166
|
content: "◦";
|
|
@@ -168,22 +168,22 @@ NESTED BLOCKS
|
|
|
168
168
|
font-family: arial;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
.blockOuter[data-
|
|
171
|
+
.blockOuter[data-list-type="li"]
|
|
172
172
|
> .block
|
|
173
173
|
> .blockGroup
|
|
174
|
-
> .blockOuter[data-
|
|
174
|
+
> .blockOuter[data-list-type="li"]
|
|
175
175
|
> .block
|
|
176
176
|
> .blockGroup
|
|
177
|
-
.blockOuter[data-prev-
|
|
177
|
+
.blockOuter[data-prev-list-type="li"]
|
|
178
178
|
> .block
|
|
179
179
|
> div:first-child::before,
|
|
180
|
-
.blockOuter[data-
|
|
180
|
+
.blockOuter[data-list-type="li"]
|
|
181
181
|
> .block
|
|
182
182
|
> .blockGroup
|
|
183
|
-
> .blockOuter[data-
|
|
183
|
+
> .blockOuter[data-list-type="li"]
|
|
184
184
|
> .block
|
|
185
185
|
> .blockGroup
|
|
186
|
-
.blockOuter[data-
|
|
186
|
+
.blockOuter[data-list-type="li"]:not([data-prev-list-type])
|
|
187
187
|
> .block
|
|
188
188
|
> div:first-child::before {
|
|
189
189
|
content: "▪";
|
|
@@ -217,10 +217,10 @@ NESTED BLOCKS
|
|
|
217
217
|
content: "Type to filter";
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
[data-
|
|
220
|
+
[data-heading-type] > .blockContent.isEmpty div::before {
|
|
221
221
|
content: "Heading";
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
[data-
|
|
224
|
+
[data-list-type] > .blockContent.isEmpty div::before {
|
|
225
225
|
content: "List";
|
|
226
|
-
}
|
|
226
|
+
}
|
|
@@ -7,12 +7,13 @@ import { OrderedListPlugin } from "../OrderedListPlugin";
|
|
|
7
7
|
import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
|
|
8
8
|
import { textblockTypeInputRuleSameNodeType } from "../rule";
|
|
9
9
|
import styles from "./Block.module.css";
|
|
10
|
+
import BlockAttributes from "../BlockAttributes";
|
|
10
11
|
|
|
11
12
|
export interface IBlock {
|
|
12
13
|
HTMLAttributes: Record<string, any>;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export type Level = 1 | 2 | 3;
|
|
16
|
+
export type Level = "1" | "2" | "3";
|
|
16
17
|
export type ListType = "li" | "oli";
|
|
17
18
|
|
|
18
19
|
declare module "@tiptap/core" {
|
|
@@ -60,63 +61,109 @@ export const Block = Node.create<IBlock>({
|
|
|
60
61
|
return {
|
|
61
62
|
listType: {
|
|
62
63
|
default: undefined,
|
|
63
|
-
renderHTML: (attributes) => {
|
|
64
|
-
return {
|
|
65
|
-
"data-listType": attributes.listType,
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
parseHTML: (element) => element.getAttribute("data-listType"),
|
|
69
64
|
},
|
|
70
65
|
blockColor: {
|
|
71
66
|
default: undefined,
|
|
72
|
-
renderHTML: (attributes) => {
|
|
73
|
-
return {
|
|
74
|
-
"data-blockColor": attributes.blockColor,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
parseHTML: (element) => element.getAttribute("data-blockColor"),
|
|
78
67
|
},
|
|
79
68
|
blockStyle: {
|
|
80
69
|
default: undefined,
|
|
81
|
-
renderHTML: (attributes) => {
|
|
82
|
-
return {
|
|
83
|
-
"data-blockStyle": attributes.blockStyle,
|
|
84
|
-
};
|
|
85
|
-
},
|
|
86
|
-
parseHTML: (element) => element.getAttribute("data-blockStyle"),
|
|
87
70
|
},
|
|
88
71
|
headingType: {
|
|
89
72
|
default: undefined,
|
|
90
73
|
keepOnSplit: false,
|
|
91
|
-
renderHTML: (attributes) => {
|
|
92
|
-
return {
|
|
93
|
-
"data-headingType": attributes.headingType,
|
|
94
|
-
};
|
|
95
|
-
},
|
|
96
|
-
parseHTML: (element) => element.getAttribute("data-headingType"),
|
|
97
74
|
},
|
|
98
75
|
};
|
|
99
76
|
},
|
|
100
77
|
|
|
101
|
-
// TODO: should we parse <li>, <ol>, <h1>, etc?
|
|
102
78
|
parseHTML() {
|
|
103
79
|
return [
|
|
80
|
+
// For parsing blocks within the editor.
|
|
104
81
|
{
|
|
105
82
|
tag: "div",
|
|
83
|
+
getAttrs: (element) => {
|
|
84
|
+
if (typeof element === "string") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const attrs: Record<string, string> = {};
|
|
89
|
+
for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {
|
|
90
|
+
if (element.getAttribute(HTMLAttr)) {
|
|
91
|
+
attrs[nodeAttr] = element.getAttribute(HTMLAttr)!;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (element.getAttribute("data-node-type") === "block") {
|
|
96
|
+
return attrs;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return false;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
// For parsing headings & paragraphs copied from outside the editor.
|
|
103
|
+
{
|
|
104
|
+
tag: "p",
|
|
105
|
+
priority: 100,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
tag: "h1",
|
|
109
|
+
attrs: { headingType: "1" },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
tag: "h2",
|
|
113
|
+
attrs: { headingType: "2" },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
tag: "h3",
|
|
117
|
+
attrs: { headingType: "3" },
|
|
118
|
+
},
|
|
119
|
+
// For parsing list items copied from outside the editor.
|
|
120
|
+
{
|
|
121
|
+
tag: "li",
|
|
122
|
+
getAttrs: (element) => {
|
|
123
|
+
if (typeof element === "string") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const parent = element.parentElement;
|
|
128
|
+
|
|
129
|
+
if (parent === null) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Gets type of list item (ordered/unordered) based on parent element's tag ("ol"/"ul").
|
|
134
|
+
if (parent.tagName === "UL") {
|
|
135
|
+
return { listType: "li" };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (parent.tagName === "OL") {
|
|
139
|
+
return { listType: "oli" };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return false;
|
|
143
|
+
},
|
|
106
144
|
},
|
|
107
145
|
];
|
|
108
146
|
},
|
|
109
147
|
|
|
110
148
|
renderHTML({ HTMLAttributes }) {
|
|
149
|
+
const attrs: Record<string, string> = {};
|
|
150
|
+
for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {
|
|
151
|
+
if (HTMLAttributes[nodeAttr]) {
|
|
152
|
+
attrs[HTMLAttr] = HTMLAttributes[nodeAttr];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
111
156
|
return [
|
|
112
157
|
"div",
|
|
113
|
-
mergeAttributes(
|
|
158
|
+
mergeAttributes(attrs, {
|
|
114
159
|
class: styles.blockOuter,
|
|
160
|
+
"data-node-type": "block-outer",
|
|
115
161
|
}),
|
|
116
162
|
[
|
|
117
163
|
"div",
|
|
118
|
-
mergeAttributes(
|
|
164
|
+
mergeAttributes(attrs, {
|
|
119
165
|
class: styles.block,
|
|
166
|
+
"data-node-type": "block",
|
|
120
167
|
}),
|
|
121
168
|
0,
|
|
122
169
|
],
|
|
@@ -125,7 +172,7 @@ export const Block = Node.create<IBlock>({
|
|
|
125
172
|
|
|
126
173
|
addInputRules() {
|
|
127
174
|
return [
|
|
128
|
-
...[1, 2, 3].map((level) => {
|
|
175
|
+
...["1", "2", "3"].map((level) => {
|
|
129
176
|
// Create a heading when starting with "#", "##", or "###""
|
|
130
177
|
return textblockTypeInputRuleSameNodeType({
|
|
131
178
|
find: new RegExp(`^(#{1,${level}})\\s$`),
|
|
@@ -378,9 +425,9 @@ export const Block = Node.create<IBlock>({
|
|
|
378
425
|
},
|
|
379
426
|
"Mod-Alt-0": () =>
|
|
380
427
|
this.editor.chain().unsetList().unsetBlockHeading().run(),
|
|
381
|
-
"Mod-Alt-1": () => this.editor.commands.setBlockHeading({ level: 1 }),
|
|
382
|
-
"Mod-Alt-2": () => this.editor.commands.setBlockHeading({ level: 2 }),
|
|
383
|
-
"Mod-Alt-3": () => this.editor.commands.setBlockHeading({ level: 3 }),
|
|
428
|
+
"Mod-Alt-1": () => this.editor.commands.setBlockHeading({ level: "1" }),
|
|
429
|
+
"Mod-Alt-2": () => this.editor.commands.setBlockHeading({ level: "2" }),
|
|
430
|
+
"Mod-Alt-3": () => this.editor.commands.setBlockHeading({ level: "3" }),
|
|
384
431
|
"Mod-Shift-7": () => this.editor.commands.setBlockList("li"),
|
|
385
432
|
"Mod-Shift-8": () => this.editor.commands.setBlockList("oli"),
|
|
386
433
|
// TODO: Add shortcuts for numbered and bullet list
|