@blocknote/core 0.4.0 → 0.4.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/dist/blocknote.js +2043 -1918
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +20 -20
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/BlockNoteEditor.ts +5 -4
- package/src/BlockNoteExtensions.ts +11 -11
- package/src/api/Editor.ts +90 -6
- package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +616 -0
- package/src/api/blockManipulation/blockManipulation.test.ts +172 -0
- package/src/api/blockManipulation/blockManipulation.ts +25 -14
- package/src/api/formatConversions/__snapshots__/formatConversions.test.ts.snap +346 -0
- package/src/api/formatConversions/formatConversions.test.ts +766 -0
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +268 -0
- package/src/api/nodeConversions/nodeConversions.test.ts +244 -0
- package/src/api/nodeConversions/nodeConversions.ts +167 -58
- package/src/api/nodeConversions/testUtil.ts +61 -0
- package/src/api/util/nodeUtil.ts +38 -0
- package/src/editor.module.css +1 -0
- package/src/extensions/Blocks/api/blockTypes.ts +14 -9
- package/src/extensions/Blocks/api/inlineContentTypes.ts +27 -36
- package/src/extensions/Blocks/nodes/Block.module.css +39 -36
- package/src/extensions/Blocks/nodes/BlockContainer.ts +15 -14
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +149 -87
- package/src/shared/utils.ts +6 -0
- package/types/src/BlockNoteEditor.d.ts +2 -2
- package/types/src/api/Editor.d.ts +26 -6
- package/types/src/api/blockManipulation/blockManipulation.d.ts +5 -5
- package/types/src/api/blockManipulation/blockManipulation.test.d.ts +1 -0
- package/types/src/api/formatConversions/formatConversions.test.d.ts +1 -0
- package/types/src/api/nodeConversions/nodeConversions.d.ts +11 -4
- package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
- package/types/src/api/nodeConversions/testUtil.d.ts +2 -0
- package/types/src/api/util/nodeUtil.d.ts +8 -0
- package/types/src/extensions/Blocks/api/blockTypes.d.ts +10 -9
- package/types/src/extensions/Blocks/api/inlineContentTypes.d.ts +25 -19
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +15 -0
- package/types/src/shared/utils.d.ts +3 -0
- package/types/src/EditorElement.d.ts +0 -7
- package/types/src/api/Document.d.ts +0 -5
- package/types/src/api/removeUnderlinesRehypePlugin.d.ts +0 -6
- package/types/src/api/simplifyBlocksRehypePlugin.d.ts +0 -16
- package/types/src/extensions/Blocks/BlockAttributes.d.ts +0 -2
- package/types/src/extensions/Blocks/MultipleNodeSelection.d.ts +0 -24
- package/types/src/extensions/Blocks/api/apiTypes.d.ts +0 -18
- package/types/src/extensions/Blocks/api/styleTypes.d.ts +0 -22
- package/types/src/extensions/Blocks/apiTypes.d.ts +0 -16
- package/types/src/extensions/Blocks/nodes/Block.d.ts +0 -24
- package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +0 -4
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +0 -4
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.d.ts +0 -8
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.d.ts +0 -8
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.d.ts +0 -6
- package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +0 -8
- package/types/src/extensions/BubbleMenu/BubbleMenuFactoryTypes.d.ts +0 -27
- package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +0 -44
- package/types/src/extensions/DraggableBlocks/BlockMenuFactoryTypes.d.ts +0 -12
- package/types/src/extensions/DraggableBlocks/DragMenuFactoryTypes.d.ts +0 -18
- package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +0 -8
- package/types/src/extensions/Hyperlinks/HyperlinkMenuFactoryTypes.d.ts +0 -11
- package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +0 -11
- package/types/src/extensions/Paragraph/FixedParagraph.d.ts +0 -1
- package/types/src/extensions/SlashMenu/defaultCommands.d.ts +0 -8
- package/types/src/utils.d.ts +0 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Mark } from "@tiptap/pm/model";
|
|
1
2
|
import { Node, Schema } from "prosemirror-model";
|
|
2
3
|
import {
|
|
3
4
|
Block,
|
|
@@ -5,12 +6,104 @@ import {
|
|
|
5
6
|
PartialBlock,
|
|
6
7
|
} from "../../extensions/Blocks/api/blockTypes";
|
|
7
8
|
import {
|
|
9
|
+
ColorStyles,
|
|
8
10
|
InlineContent,
|
|
9
|
-
|
|
11
|
+
Link,
|
|
12
|
+
PartialInlineContent,
|
|
13
|
+
PartialLink,
|
|
14
|
+
StyledText,
|
|
15
|
+
Styles,
|
|
16
|
+
ToggledStyles,
|
|
10
17
|
} from "../../extensions/Blocks/api/inlineContentTypes";
|
|
11
18
|
import { getBlockInfoFromPos } from "../../extensions/Blocks/helpers/getBlockInfoFromPos";
|
|
12
19
|
import UniqueID from "../../extensions/UniqueID/UniqueID";
|
|
20
|
+
import { UnreachableCaseError } from "../../shared/utils";
|
|
21
|
+
|
|
22
|
+
const toggleStyles = new Set<ToggledStyles>([
|
|
23
|
+
"bold",
|
|
24
|
+
"italic",
|
|
25
|
+
"underline",
|
|
26
|
+
"strike",
|
|
27
|
+
]);
|
|
28
|
+
const colorStyles = new Set<ColorStyles>(["textColor", "backgroundColor"]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert a StyledText inline element to a
|
|
32
|
+
* prosemirror text node with the appropriate marks
|
|
33
|
+
*/
|
|
34
|
+
function styledTextToNode(styledText: StyledText, schema: Schema): Node {
|
|
35
|
+
const marks: Mark[] = [];
|
|
36
|
+
|
|
37
|
+
for (const [style, value] of Object.entries(styledText.styles)) {
|
|
38
|
+
if (toggleStyles.has(style as ToggledStyles)) {
|
|
39
|
+
marks.push(schema.mark(style));
|
|
40
|
+
} else if (colorStyles.has(style as ColorStyles)) {
|
|
41
|
+
marks.push(schema.mark(style, { color: value }));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return schema.text(styledText.text, marks);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Converts a Link inline content element to
|
|
50
|
+
* prosemirror text nodes with the appropriate marks
|
|
51
|
+
*/
|
|
52
|
+
function linkToNodes(link: PartialLink, schema: Schema): Node[] {
|
|
53
|
+
const linkMark = schema.marks.link.create({
|
|
54
|
+
href: link.href,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return styledTextArrayToNodes(link.content, schema).map((node) => {
|
|
58
|
+
return node.mark([...node.marks, linkMark]);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Converts an array of StyledText inline content elements to
|
|
64
|
+
* prosemirror text nodes with the appropriate marks
|
|
65
|
+
*/
|
|
66
|
+
function styledTextArrayToNodes(
|
|
67
|
+
content: string | StyledText[],
|
|
68
|
+
schema: Schema
|
|
69
|
+
): Node[] {
|
|
70
|
+
let nodes: Node[] = [];
|
|
71
|
+
|
|
72
|
+
if (typeof content === "string") {
|
|
73
|
+
nodes.push(schema.text(content));
|
|
74
|
+
return nodes;
|
|
75
|
+
}
|
|
13
76
|
|
|
77
|
+
for (const styledText of content) {
|
|
78
|
+
nodes.push(styledTextToNode(styledText, schema));
|
|
79
|
+
}
|
|
80
|
+
return nodes;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* converts an array of inline content elements to prosemirror nodes
|
|
85
|
+
*/
|
|
86
|
+
export function inlineContentToNodes(
|
|
87
|
+
blockContent: PartialInlineContent[],
|
|
88
|
+
schema: Schema
|
|
89
|
+
): Node[] {
|
|
90
|
+
let nodes: Node[] = [];
|
|
91
|
+
|
|
92
|
+
for (const content of blockContent) {
|
|
93
|
+
if (content.type === "link") {
|
|
94
|
+
nodes.push(...linkToNodes(content, schema));
|
|
95
|
+
} else if (content.type === "text") {
|
|
96
|
+
nodes.push(...styledTextArrayToNodes([content], schema));
|
|
97
|
+
} else {
|
|
98
|
+
throw new UnreachableCaseError(content);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return nodes;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Converts a BlockNote block to a TipTap node.
|
|
106
|
+
*/
|
|
14
107
|
export function blockToNode(block: PartialBlock, schema: Schema) {
|
|
15
108
|
let id = block.id;
|
|
16
109
|
|
|
@@ -18,24 +111,26 @@ export function blockToNode(block: PartialBlock, schema: Schema) {
|
|
|
18
111
|
id = UniqueID.options.generateID();
|
|
19
112
|
}
|
|
20
113
|
|
|
21
|
-
let
|
|
114
|
+
let type = block.type;
|
|
22
115
|
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
for (const styledText of block.content) {
|
|
27
|
-
const marks = [];
|
|
116
|
+
if (type === undefined) {
|
|
117
|
+
type = "paragraph";
|
|
118
|
+
}
|
|
28
119
|
|
|
29
|
-
|
|
30
|
-
marks.push(schema.mark(style.type, style.props));
|
|
31
|
-
}
|
|
120
|
+
let contentNode: Node;
|
|
32
121
|
|
|
33
|
-
|
|
34
|
-
|
|
122
|
+
if (!block.content) {
|
|
123
|
+
contentNode = schema.nodes[type].create(block.props);
|
|
124
|
+
} else if (typeof block.content === "string") {
|
|
125
|
+
contentNode = schema.nodes[type].create(
|
|
126
|
+
block.props,
|
|
127
|
+
schema.text(block.content)
|
|
128
|
+
);
|
|
129
|
+
} else {
|
|
130
|
+
const nodes = inlineContentToNodes(block.content, schema);
|
|
131
|
+
contentNode = schema.nodes[type].create(block.props, nodes);
|
|
35
132
|
}
|
|
36
133
|
|
|
37
|
-
const contentNode = schema.nodes[block.type].create(block.props, content);
|
|
38
|
-
|
|
39
134
|
const children: Node[] = [];
|
|
40
135
|
|
|
41
136
|
if (block.children) {
|
|
@@ -55,40 +150,69 @@ export function blockToNode(block: PartialBlock, schema: Schema) {
|
|
|
55
150
|
);
|
|
56
151
|
}
|
|
57
152
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let posBeforeNode: number | undefined = undefined;
|
|
64
|
-
|
|
65
|
-
doc.firstChild!.descendants((node, pos) => {
|
|
66
|
-
// Skips traversing nodes after node with target ID has been found.
|
|
67
|
-
if (targetNode) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Keeps traversing nodes if block with target ID has not been found.
|
|
72
|
-
if (node.type.name !== "blockContainer" || node.attrs.id !== id) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
153
|
+
/**
|
|
154
|
+
* Converts an internal (prosemirror) content node to a BlockNote InlineContent array.
|
|
155
|
+
*/
|
|
156
|
+
function contentNodeToInlineContent(contentNode: Node) {
|
|
157
|
+
const content: InlineContent[] = [];
|
|
75
158
|
|
|
76
|
-
|
|
77
|
-
posBeforeNode = pos + 1;
|
|
159
|
+
let currentLink: Link | undefined = undefined;
|
|
78
160
|
|
|
79
|
-
|
|
80
|
-
|
|
161
|
+
// Most of the logic below is for handling links because in ProseMirror links are marks
|
|
162
|
+
// while in BlockNote links are a type of inline content
|
|
163
|
+
contentNode.content.forEach((node) => {
|
|
164
|
+
const styles: Styles = {};
|
|
81
165
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
166
|
+
let linkMark: Mark | undefined;
|
|
167
|
+
for (const mark of node.marks) {
|
|
168
|
+
if (mark.type.name === "link") {
|
|
169
|
+
linkMark = mark;
|
|
170
|
+
} else if (toggleStyles.has(mark.type.name as ToggledStyles)) {
|
|
171
|
+
styles[mark.type.name as ToggledStyles] = true;
|
|
172
|
+
} else if (colorStyles.has(mark.type.name as ColorStyles)) {
|
|
173
|
+
styles[mark.type.name as ColorStyles] = mark.attrs.color;
|
|
174
|
+
} else {
|
|
175
|
+
throw Error("Mark is of an unrecognized type: " + mark.type.name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
85
178
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
179
|
+
if (linkMark && currentLink && linkMark.attrs.href === currentLink.href) {
|
|
180
|
+
// if the node is a link that matches the current link, add it to the current link
|
|
181
|
+
currentLink.content.push({
|
|
182
|
+
type: "text",
|
|
183
|
+
text: node.textContent,
|
|
184
|
+
styles,
|
|
185
|
+
});
|
|
186
|
+
} else if (linkMark) {
|
|
187
|
+
// if the node is a link that doesn't match the current link, create a new link
|
|
188
|
+
currentLink = {
|
|
189
|
+
type: "link",
|
|
190
|
+
href: linkMark.attrs.href,
|
|
191
|
+
content: [
|
|
192
|
+
{
|
|
193
|
+
type: "text",
|
|
194
|
+
text: node.textContent,
|
|
195
|
+
styles,
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
content.push(currentLink);
|
|
200
|
+
} else {
|
|
201
|
+
// if the node is not a link, add it to the content
|
|
202
|
+
content.push({
|
|
203
|
+
type: "text",
|
|
204
|
+
text: node.textContent,
|
|
205
|
+
styles,
|
|
206
|
+
});
|
|
207
|
+
currentLink = undefined;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
return content;
|
|
90
211
|
}
|
|
91
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Convert a TipTap node to a BlockNote block.
|
|
215
|
+
*/
|
|
92
216
|
export function nodeToBlock(
|
|
93
217
|
node: Node,
|
|
94
218
|
blockCache?: WeakMap<Node, Block>
|
|
@@ -134,22 +258,7 @@ export function nodeToBlock(
|
|
|
134
258
|
}
|
|
135
259
|
}
|
|
136
260
|
|
|
137
|
-
const content
|
|
138
|
-
blockInfo.contentNode.content.forEach((node) => {
|
|
139
|
-
const styles: Style[] = [];
|
|
140
|
-
|
|
141
|
-
for (const mark of node.marks) {
|
|
142
|
-
styles.push({
|
|
143
|
-
type: mark.type.name,
|
|
144
|
-
props: mark.attrs,
|
|
145
|
-
} as Style);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
content.push({
|
|
149
|
-
text: node.textContent,
|
|
150
|
-
styles,
|
|
151
|
-
});
|
|
152
|
-
});
|
|
261
|
+
const content = contentNodeToInlineContent(blockInfo.contentNode);
|
|
153
262
|
|
|
154
263
|
const children: Block[] = [];
|
|
155
264
|
for (let i = 0; i < blockInfo.numChildBlocks; i++) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Block, PartialBlock } from "../../extensions/Blocks/api/blockTypes";
|
|
2
|
+
import {
|
|
3
|
+
InlineContent,
|
|
4
|
+
PartialInlineContent,
|
|
5
|
+
StyledText,
|
|
6
|
+
} from "../../extensions/Blocks/api/inlineContentTypes";
|
|
7
|
+
|
|
8
|
+
function textShorthandToStyledText(
|
|
9
|
+
content: string | StyledText[] = ""
|
|
10
|
+
): StyledText[] {
|
|
11
|
+
if (typeof content === "string") {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
type: "text",
|
|
15
|
+
text: content,
|
|
16
|
+
styles: {},
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
return content;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function partialContentToInlineContent(
|
|
24
|
+
content: string | PartialInlineContent[] = ""
|
|
25
|
+
): InlineContent[] {
|
|
26
|
+
if (typeof content === "string") {
|
|
27
|
+
return textShorthandToStyledText(content);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return content.map((partialContent) => {
|
|
31
|
+
if (partialContent.type === "link") {
|
|
32
|
+
return {
|
|
33
|
+
...partialContent,
|
|
34
|
+
content: textShorthandToStyledText(partialContent.content),
|
|
35
|
+
};
|
|
36
|
+
} else {
|
|
37
|
+
return partialContent;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function partialBlockToBlockForTesting(
|
|
43
|
+
partialBlock: PartialBlock
|
|
44
|
+
): Block {
|
|
45
|
+
const withDefaults = {
|
|
46
|
+
id: "",
|
|
47
|
+
type: "paragraph" as any,
|
|
48
|
+
// because at this point we don't have an easy way to access default props at runtime,
|
|
49
|
+
// partialBlockToBlockForTesting will not set them.
|
|
50
|
+
props: {} as any,
|
|
51
|
+
content: [],
|
|
52
|
+
children: [],
|
|
53
|
+
...partialBlock,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...withDefaults,
|
|
58
|
+
content: partialContentToInlineContent(withDefaults.content),
|
|
59
|
+
children: withDefaults.children.map(partialBlockToBlockForTesting),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get a TipTap node by id
|
|
5
|
+
*/
|
|
6
|
+
export function getNodeById(
|
|
7
|
+
id: string,
|
|
8
|
+
doc: Node
|
|
9
|
+
): { node: Node; posBeforeNode: number } {
|
|
10
|
+
let targetNode: Node | undefined = undefined;
|
|
11
|
+
let posBeforeNode: number | undefined = undefined;
|
|
12
|
+
|
|
13
|
+
doc.firstChild!.descendants((node, pos) => {
|
|
14
|
+
// Skips traversing nodes after node with target ID has been found.
|
|
15
|
+
if (targetNode) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Keeps traversing nodes if block with target ID has not been found.
|
|
20
|
+
if (node.type.name !== "blockContainer" || node.attrs.id !== id) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
targetNode = node;
|
|
25
|
+
posBeforeNode = pos + 1;
|
|
26
|
+
|
|
27
|
+
return false;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (targetNode === undefined || posBeforeNode === undefined) {
|
|
31
|
+
throw Error("Could not find block in the editor with matching ID.");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
node: targetNode,
|
|
36
|
+
posBeforeNode: posBeforeNode,
|
|
37
|
+
};
|
|
38
|
+
}
|
package/src/editor.module.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Define the main block types **/
|
|
2
2
|
|
|
3
|
-
import { InlineContent } from "./inlineContentTypes";
|
|
3
|
+
import { InlineContent, PartialInlineContent } from "./inlineContentTypes";
|
|
4
4
|
|
|
5
5
|
export type BlockTemplate<
|
|
6
6
|
// Type of the block.
|
|
@@ -17,7 +17,7 @@ export type BlockTemplate<
|
|
|
17
17
|
children: Block[];
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
export type
|
|
20
|
+
export type DefaultBlockProps = {
|
|
21
21
|
backgroundColor: string;
|
|
22
22
|
textColor: string;
|
|
23
23
|
textAlignment: "left" | "center" | "right" | "justify";
|
|
@@ -25,19 +25,22 @@ export type GlobalProps = {
|
|
|
25
25
|
|
|
26
26
|
export type NumberedListItemBlock = BlockTemplate<
|
|
27
27
|
"numberedListItem",
|
|
28
|
-
|
|
28
|
+
DefaultBlockProps
|
|
29
29
|
>;
|
|
30
30
|
|
|
31
|
-
export type BulletListItemBlock = BlockTemplate<
|
|
31
|
+
export type BulletListItemBlock = BlockTemplate<
|
|
32
|
+
"bulletListItem",
|
|
33
|
+
DefaultBlockProps
|
|
34
|
+
>;
|
|
32
35
|
|
|
33
36
|
export type HeadingBlock = BlockTemplate<
|
|
34
37
|
"heading",
|
|
35
|
-
|
|
38
|
+
DefaultBlockProps & {
|
|
36
39
|
level: "1" | "2" | "3";
|
|
37
40
|
}
|
|
38
41
|
>;
|
|
39
42
|
|
|
40
|
-
export type ParagraphBlock = BlockTemplate<"paragraph",
|
|
43
|
+
export type ParagraphBlock = BlockTemplate<"paragraph", DefaultBlockProps>;
|
|
41
44
|
|
|
42
45
|
export type Block =
|
|
43
46
|
| ParagraphBlock
|
|
@@ -45,12 +48,14 @@ export type Block =
|
|
|
45
48
|
| BulletListItemBlock
|
|
46
49
|
| NumberedListItemBlock;
|
|
47
50
|
|
|
51
|
+
export type BlockIdentifier = string | Block;
|
|
52
|
+
|
|
48
53
|
/** Define "Partial Blocks", these are for updating or creating blocks */
|
|
49
54
|
export type PartialBlockTemplate<B extends Block> = B extends Block
|
|
50
55
|
? Partial<Omit<B, "props" | "children" | "content" | "type">> & {
|
|
51
|
-
type
|
|
56
|
+
type?: B["type"];
|
|
52
57
|
props?: Partial<B["props"]>;
|
|
53
|
-
content?: string |
|
|
58
|
+
content?: string | PartialInlineContent[];
|
|
54
59
|
children?: PartialBlock[];
|
|
55
60
|
}
|
|
56
61
|
: never;
|
|
@@ -66,7 +71,7 @@ export type BlockPropsTemplate<Props> = Props extends Block["props"]
|
|
|
66
71
|
* will change anyway once we allow for custom blocks
|
|
67
72
|
*/
|
|
68
73
|
|
|
69
|
-
export const globalProps: Array<keyof
|
|
74
|
+
export const globalProps: Array<keyof DefaultBlockProps> = [
|
|
70
75
|
"backgroundColor",
|
|
71
76
|
"textColor",
|
|
72
77
|
"textAlignment",
|
|
@@ -1,44 +1,35 @@
|
|
|
1
|
-
export type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
> = {
|
|
9
|
-
type: Type;
|
|
10
|
-
props: Props;
|
|
1
|
+
export type Styles = {
|
|
2
|
+
bold?: true;
|
|
3
|
+
italic?: true;
|
|
4
|
+
underline?: true;
|
|
5
|
+
strike?: true;
|
|
6
|
+
textColor?: string;
|
|
7
|
+
backgroundColor?: string;
|
|
11
8
|
};
|
|
12
9
|
|
|
13
|
-
export type
|
|
10
|
+
export type ToggledStyles = {
|
|
11
|
+
[K in keyof Styles]-?: Required<Styles>[K] extends true ? K : never;
|
|
12
|
+
}[keyof Styles];
|
|
14
13
|
|
|
15
|
-
export type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export type Strikethrough = StyleTemplate<"strike", {}>;
|
|
20
|
-
|
|
21
|
-
export type TextColor = StyleTemplate<"textColor", { color: string }>;
|
|
22
|
-
|
|
23
|
-
export type BackgroundColor = StyleTemplate<
|
|
24
|
-
"backgroundColor",
|
|
25
|
-
{ color: string }
|
|
26
|
-
>;
|
|
27
|
-
|
|
28
|
-
export type Link = StyleTemplate<"link", { href: string }>;
|
|
29
|
-
|
|
30
|
-
export type Style =
|
|
31
|
-
| Bold
|
|
32
|
-
| Italic
|
|
33
|
-
| Underline
|
|
34
|
-
| Strikethrough
|
|
35
|
-
| TextColor
|
|
36
|
-
| BackgroundColor
|
|
37
|
-
| Link;
|
|
14
|
+
export type ColorStyles = {
|
|
15
|
+
[K in keyof Styles]-?: Required<Styles>[K] extends string ? K : never;
|
|
16
|
+
}[keyof Styles];
|
|
38
17
|
|
|
39
18
|
export type StyledText = {
|
|
19
|
+
type: "text";
|
|
40
20
|
text: string;
|
|
41
|
-
styles:
|
|
21
|
+
styles: Styles;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type Link = {
|
|
25
|
+
type: "link";
|
|
26
|
+
href: string;
|
|
27
|
+
content: StyledText[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type PartialLink = Omit<Link, "content"> & {
|
|
31
|
+
content: string | Link["content"];
|
|
42
32
|
};
|
|
43
33
|
|
|
44
|
-
export type InlineContent = StyledText;
|
|
34
|
+
export type InlineContent = StyledText | Link;
|
|
35
|
+
export type PartialInlineContent = StyledText | PartialLink;
|
|
@@ -35,6 +35,7 @@ BASIC STYLES
|
|
|
35
35
|
.blockContent h2,
|
|
36
36
|
.blockContent h3,
|
|
37
37
|
.blockContent li {
|
|
38
|
+
all: unset;
|
|
38
39
|
margin: 0;
|
|
39
40
|
padding: 0;
|
|
40
41
|
font-size: inherit;
|
|
@@ -150,75 +151,79 @@ NESTED BLOCKS
|
|
|
150
151
|
|
|
151
152
|
/* Ordered */
|
|
152
153
|
[data-content-type="numberedListItem"] {
|
|
153
|
-
--index: attr(data-index)
|
|
154
|
+
--index: attr(data-index);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
[data-prev-type="numberedListItem"] {
|
|
157
|
-
--prev-index: attr(data-prev-index)
|
|
158
|
+
--prev-index: attr(data-prev-index);
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
.blockOuter[data-prev-type="numberedListItem"]:not([data-prev-index="none"])
|
|
161
|
-
> .block
|
|
162
|
-
> .blockContent::before {
|
|
162
|
+
> .block
|
|
163
|
+
> .blockContent::before {
|
|
163
164
|
margin-right: 1.2em;
|
|
164
|
-
content: var(--prev-index)".";
|
|
165
|
+
content: var(--prev-index) ".";
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
.blockOuter:not([data-prev-type])
|
|
168
|
-
> .block
|
|
169
|
-
> .blockContent[data-content-type="numberedListItem"]::before {
|
|
169
|
+
> .block
|
|
170
|
+
> .blockContent[data-content-type="numberedListItem"]::before {
|
|
170
171
|
margin-right: 1.2em;
|
|
171
|
-
content: var(--index)".";
|
|
172
|
+
content: var(--index) ".";
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
/* Unordered */
|
|
175
176
|
/* No list nesting */
|
|
176
|
-
.blockOuter[data-prev-type="bulletListItem"]
|
|
177
|
-
> .block
|
|
178
|
-
> .blockContent::before {
|
|
177
|
+
.blockOuter[data-prev-type="bulletListItem"] > .block > .blockContent::before {
|
|
179
178
|
margin-right: 1.2em;
|
|
180
179
|
content: "•";
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
.blockOuter:not([data-prev-type])
|
|
184
|
-
> .block
|
|
185
|
-
> .blockContent[data-content-type="bulletListItem"]::before {
|
|
183
|
+
> .block
|
|
184
|
+
> .blockContent[data-content-type="bulletListItem"]::before {
|
|
186
185
|
margin-right: 1.2em;
|
|
187
186
|
content: "•";
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
/* 1 level of list nesting */
|
|
191
|
-
[data-content-type="bulletListItem"]
|
|
192
|
-
|
|
193
|
-
> .
|
|
194
|
-
> .
|
|
190
|
+
[data-content-type="bulletListItem"]
|
|
191
|
+
~ .blockGroup
|
|
192
|
+
> .blockOuter[data-prev-type="bulletListItem"]
|
|
193
|
+
> .block
|
|
194
|
+
> .blockContent::before {
|
|
195
195
|
margin-right: 1.2em;
|
|
196
196
|
content: "◦";
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
[data-content-type="bulletListItem"]
|
|
200
|
-
|
|
201
|
-
> .
|
|
202
|
-
> .
|
|
199
|
+
[data-content-type="bulletListItem"]
|
|
200
|
+
~ .blockGroup
|
|
201
|
+
> .blockOuter:not([data-prev-type])
|
|
202
|
+
> .block
|
|
203
|
+
> .blockContent[data-content-type="bulletListItem"]::before {
|
|
203
204
|
margin-right: 1.2em;
|
|
204
205
|
content: "◦";
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
/* 2 levels of list nesting */
|
|
208
|
-
[data-content-type="bulletListItem"]
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
> .
|
|
209
|
+
[data-content-type="bulletListItem"]
|
|
210
|
+
~ .blockGroup
|
|
211
|
+
[data-content-type="bulletListItem"]
|
|
212
|
+
~ .blockGroup
|
|
213
|
+
> .blockOuter[data-prev-type="bulletListItem"]
|
|
214
|
+
> .block
|
|
215
|
+
> .blockContent::before {
|
|
213
216
|
margin-right: 1.2em;
|
|
214
217
|
content: "▪";
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
[data-content-type="bulletListItem"]
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
> .
|
|
220
|
+
[data-content-type="bulletListItem"]
|
|
221
|
+
~ .blockGroup
|
|
222
|
+
[data-content-type="bulletListItem"]
|
|
223
|
+
~ .blockGroup
|
|
224
|
+
> .blockOuter:not([data-prev-type])
|
|
225
|
+
> .block
|
|
226
|
+
> .blockContent[data-content-type="bulletListItem"]::before {
|
|
222
227
|
margin-right: 1.2em;
|
|
223
228
|
content: "▪";
|
|
224
229
|
}
|
|
@@ -251,15 +256,13 @@ NESTED BLOCKS
|
|
|
251
256
|
content: "Type to filter";
|
|
252
257
|
}
|
|
253
258
|
|
|
254
|
-
.blockContent[data-content-type="heading"].isEmpty
|
|
255
|
-
> :first-child::before {
|
|
259
|
+
.blockContent[data-content-type="heading"].isEmpty > :first-child::before {
|
|
256
260
|
content: "Heading";
|
|
257
261
|
}
|
|
258
262
|
|
|
259
|
-
.blockContent[data-content-type="bulletListItem"].isEmpty
|
|
260
|
-
> :first-child:before,
|
|
263
|
+
.blockContent[data-content-type="bulletListItem"].isEmpty > :first-child:before,
|
|
261
264
|
.blockContent[data-content-type="numberedListItem"].isEmpty
|
|
262
|
-
> :first-child:before {
|
|
265
|
+
> :first-child:before {
|
|
263
266
|
content: "List";
|
|
264
267
|
}
|
|
265
268
|
|