@blocknote/core 0.9.3 → 0.9.4
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 +1623 -1318
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +5 -5
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/BlockNoteEditor.ts +44 -12
- package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +21 -21
- package/src/api/blockManipulation/blockManipulation.test.ts +8 -11
- package/src/api/formatConversions/__snapshots__/formatConversions.test.ts.snap +3 -3
- package/src/api/formatConversions/formatConversions.test.ts +5 -5
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +3 -3
- package/src/api/nodeConversions/nodeConversions.test.ts +10 -4
- package/src/api/nodeConversions/nodeConversions.ts +9 -7
- package/src/api/nodeConversions/testUtil.ts +3 -3
- package/src/editor.module.css +1 -1
- package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +5 -3
- package/src/extensions/BackgroundColor/BackgroundColorMark.ts +2 -1
- package/src/extensions/Blocks/NonEditableBlockPlugin.ts +17 -0
- package/src/extensions/Blocks/api/block.ts +29 -16
- package/src/extensions/Blocks/api/blockTypes.ts +79 -27
- package/src/extensions/Blocks/api/defaultBlocks.ts +13 -41
- package/src/extensions/Blocks/api/defaultProps.ts +16 -0
- package/src/extensions/Blocks/nodes/Block.module.css +78 -24
- package/src/extensions/Blocks/nodes/BlockContainer.ts +17 -41
- package/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.ts +59 -13
- package/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/ImageBlockContent.ts +305 -0
- package/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts +13 -0
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +24 -2
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +146 -120
- package/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts +12 -2
- package/src/extensions/ImageToolbar/ImageToolbarPlugin.ts +239 -0
- package/src/extensions/SlashMenu/defaultSlashMenuItems.ts +47 -6
- package/src/extensions/TextColor/TextColorExtension.ts +4 -3
- package/src/extensions/TextColor/TextColorMark.ts +2 -1
- package/src/index.ts +4 -0
- package/types/src/BlockNoteEditor.d.ts +9 -0
- package/types/src/BlockNoteExtensions.d.ts +1 -1
- package/types/src/extensions/Blocks/api/block.d.ts +7 -8
- package/types/src/extensions/Blocks/api/blockTypes.d.ts +29 -20
- package/types/src/extensions/Blocks/api/defaultBlocks.d.ts +55 -51
- package/types/src/extensions/Blocks/api/defaultProps.d.ts +2 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +43 -9
- package/types/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/ImageBlockContent.d.ts +2 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +1 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/uploadToTmpFilesOrg_DEV_ONLY.d.ts +1 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +35 -9
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +35 -9
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +36 -1
- package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +1 -1
- package/types/src/index.d.ts +4 -0
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Attribute, Node } from "@tiptap/core";
|
|
1
|
+
import { Attribute, Attributes, Node } from "@tiptap/core";
|
|
2
2
|
import { BlockNoteDOMAttributes, BlockNoteEditor } from "../../..";
|
|
3
3
|
import styles from "../nodes/Block.module.css";
|
|
4
4
|
import {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
TipTapNodeConfig,
|
|
11
11
|
} from "./blockTypes";
|
|
12
12
|
import { mergeCSSClasses } from "../../../shared/utils";
|
|
13
|
+
import { ParseRule } from "prosemirror-model";
|
|
13
14
|
|
|
14
15
|
export function camelToDataKebab(str: string): string {
|
|
15
16
|
return "data-" + str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
@@ -27,7 +28,7 @@ export function propsToAttributes<
|
|
|
27
28
|
BlockConfig<BType, PSchema, ContainsInlineContent, BSchema>,
|
|
28
29
|
"render"
|
|
29
30
|
>
|
|
30
|
-
) {
|
|
31
|
+
): Attributes {
|
|
31
32
|
const tiptapAttributes: Record<string, Attribute> = {};
|
|
32
33
|
|
|
33
34
|
Object.entries(blockConfig.propSchema).forEach(([name, spec]) => {
|
|
@@ -63,7 +64,7 @@ export function parse<
|
|
|
63
64
|
BlockConfig<BType, PSchema, ContainsInlineContent, BSchema>,
|
|
64
65
|
"render"
|
|
65
66
|
>
|
|
66
|
-
) {
|
|
67
|
+
): ParseRule[] {
|
|
67
68
|
return [
|
|
68
69
|
{
|
|
69
70
|
tag: "div[data-content-type=" + blockConfig.type + "]",
|
|
@@ -120,21 +121,24 @@ export function render<
|
|
|
120
121
|
export function createBlockSpec<
|
|
121
122
|
BType extends string,
|
|
122
123
|
PSchema extends PropSchema,
|
|
123
|
-
ContainsInlineContent extends
|
|
124
|
+
ContainsInlineContent extends false,
|
|
124
125
|
BSchema extends BlockSchema
|
|
125
126
|
>(
|
|
126
127
|
blockConfig: BlockConfig<BType, PSchema, ContainsInlineContent, BSchema>
|
|
127
|
-
): BlockSpec<BType, PSchema> {
|
|
128
|
+
): BlockSpec<BType, PSchema, ContainsInlineContent> {
|
|
128
129
|
const node = createTipTapBlock<
|
|
129
130
|
BType,
|
|
131
|
+
ContainsInlineContent,
|
|
130
132
|
{
|
|
131
133
|
editor: BlockNoteEditor<BSchema>;
|
|
132
134
|
domAttributes?: BlockNoteDOMAttributes;
|
|
133
135
|
}
|
|
134
136
|
>({
|
|
135
137
|
name: blockConfig.type,
|
|
136
|
-
content: blockConfig.containsInlineContent
|
|
137
|
-
|
|
138
|
+
content: (blockConfig.containsInlineContent
|
|
139
|
+
? "inline*"
|
|
140
|
+
: "") as ContainsInlineContent extends true ? "inline*" : "",
|
|
141
|
+
selectable: true,
|
|
138
142
|
|
|
139
143
|
addAttributes() {
|
|
140
144
|
return propsToAttributes(blockConfig);
|
|
@@ -176,7 +180,9 @@ export function createBlockSpec<
|
|
|
176
180
|
|
|
177
181
|
// Gets BlockNote editor instance
|
|
178
182
|
const editor = this.options.editor! as BlockNoteEditor<
|
|
179
|
-
BSchema & {
|
|
183
|
+
BSchema & {
|
|
184
|
+
[k in BType]: BlockSpec<BType, PSchema, ContainsInlineContent>;
|
|
185
|
+
}
|
|
180
186
|
>;
|
|
181
187
|
// Gets position of the node
|
|
182
188
|
if (typeof getPos === "boolean") {
|
|
@@ -201,7 +207,10 @@ export function createBlockSpec<
|
|
|
201
207
|
// Render elements
|
|
202
208
|
const rendered = blockConfig.render(block as any, editor);
|
|
203
209
|
// Add HTML attributes to contentDOM
|
|
204
|
-
if (
|
|
210
|
+
if (blockConfig.containsInlineContent) {
|
|
211
|
+
const contentDOM = (rendered as { contentDOM: HTMLElement })
|
|
212
|
+
.contentDOM;
|
|
213
|
+
|
|
205
214
|
const inlineContentDOMAttributes =
|
|
206
215
|
this.options.domAttributes?.inlineContent || {};
|
|
207
216
|
// Add custom HTML attributes
|
|
@@ -209,12 +218,12 @@ export function createBlockSpec<
|
|
|
209
218
|
inlineContentDOMAttributes
|
|
210
219
|
)) {
|
|
211
220
|
if (attribute !== "class") {
|
|
212
|
-
|
|
221
|
+
contentDOM.setAttribute(attribute, value);
|
|
213
222
|
}
|
|
214
223
|
}
|
|
215
224
|
// Merge existing classes with inlineContent & custom classes
|
|
216
|
-
|
|
217
|
-
|
|
225
|
+
contentDOM.className = mergeCSSClasses(
|
|
226
|
+
contentDOM.className,
|
|
218
227
|
styles.inlineContent,
|
|
219
228
|
inlineContentDOMAttributes.class
|
|
220
229
|
);
|
|
@@ -226,22 +235,25 @@ export function createBlockSpec<
|
|
|
226
235
|
? {
|
|
227
236
|
dom: blockContent,
|
|
228
237
|
contentDOM: rendered.contentDOM,
|
|
238
|
+
destroy: rendered.destroy,
|
|
229
239
|
}
|
|
230
240
|
: {
|
|
231
241
|
dom: blockContent,
|
|
242
|
+
destroy: rendered.destroy,
|
|
232
243
|
};
|
|
233
244
|
};
|
|
234
245
|
},
|
|
235
246
|
});
|
|
236
247
|
|
|
237
248
|
return {
|
|
238
|
-
node: node as TipTapNode<BType>,
|
|
249
|
+
node: node as TipTapNode<BType, ContainsInlineContent>,
|
|
239
250
|
propSchema: blockConfig.propSchema,
|
|
240
251
|
};
|
|
241
252
|
}
|
|
242
253
|
|
|
243
254
|
export function createTipTapBlock<
|
|
244
255
|
Type extends string,
|
|
256
|
+
ContainsInlineContent extends boolean,
|
|
245
257
|
Options extends {
|
|
246
258
|
domAttributes?: BlockNoteDOMAttributes;
|
|
247
259
|
} = {
|
|
@@ -249,8 +261,8 @@ export function createTipTapBlock<
|
|
|
249
261
|
},
|
|
250
262
|
Storage = any
|
|
251
263
|
>(
|
|
252
|
-
config: TipTapNodeConfig<Type, Options, Storage>
|
|
253
|
-
): TipTapNode<Type, Options, Storage> {
|
|
264
|
+
config: TipTapNodeConfig<Type, ContainsInlineContent, Options, Storage>
|
|
265
|
+
): TipTapNode<Type, ContainsInlineContent, Options, Storage> {
|
|
254
266
|
// Type cast is needed as Node.name is mutable, though there is basically no
|
|
255
267
|
// reason to change it after creation. Alternative is to wrap Node in a new
|
|
256
268
|
// class, which I don't think is worth it since we'd only be changing 1
|
|
@@ -258,5 +270,6 @@ export function createTipTapBlock<
|
|
|
258
270
|
return Node.create<Options, Storage>({
|
|
259
271
|
...config,
|
|
260
272
|
group: "blockContent",
|
|
261
|
-
|
|
273
|
+
content: config.content,
|
|
274
|
+
}) as TipTapNode<Type, ContainsInlineContent, Options, Storage>;
|
|
262
275
|
}
|
|
@@ -16,11 +16,13 @@ export type BlockNoteDOMAttributes = Partial<{
|
|
|
16
16
|
}>;
|
|
17
17
|
|
|
18
18
|
// A configuration for a TipTap node, but with stricter type constraints on the
|
|
19
|
-
// "name" and "
|
|
20
|
-
// literal type, and the "
|
|
21
|
-
//
|
|
19
|
+
// "name" and "content" properties. The "name" property is now always a string
|
|
20
|
+
// literal type, and the "content" property can only be "inline*" or "". Used as
|
|
21
|
+
// the parameter in `createTipTapNode`. The "group" is also removed as
|
|
22
|
+
// `createTipTapNode` always sets it to "blockContent"
|
|
22
23
|
export type TipTapNodeConfig<
|
|
23
24
|
Name extends string,
|
|
25
|
+
ContainsInlineContent extends boolean,
|
|
24
26
|
Options extends {
|
|
25
27
|
domAttributes?: BlockNoteDOMAttributes;
|
|
26
28
|
} = {
|
|
@@ -30,32 +32,58 @@ export type TipTapNodeConfig<
|
|
|
30
32
|
> = {
|
|
31
33
|
[K in keyof NodeConfig<Options, Storage>]: K extends "name"
|
|
32
34
|
? Name
|
|
35
|
+
: K extends "content"
|
|
36
|
+
? ContainsInlineContent extends true
|
|
37
|
+
? "inline*"
|
|
38
|
+
: ""
|
|
33
39
|
: K extends "group"
|
|
34
40
|
? never
|
|
35
41
|
: NodeConfig<Options, Storage>[K];
|
|
42
|
+
} & {
|
|
43
|
+
name: Name;
|
|
44
|
+
content: ContainsInlineContent extends true ? "inline*" : "";
|
|
36
45
|
};
|
|
37
46
|
|
|
38
|
-
// A TipTap node with stricter type constraints on the "name"
|
|
39
|
-
// properties. The "name" property is now a string literal type, and
|
|
40
|
-
// "blockGroup" property is now "blockContent"
|
|
47
|
+
// A TipTap node with stricter type constraints on the "name", "group", and
|
|
48
|
+
// "content properties. The "name" property is now a string literal type, and
|
|
49
|
+
// the "blockGroup" property is now "blockContent", and the "content" property
|
|
50
|
+
// can only be "inline*" or "". Returned by `createTipTapNode`.
|
|
41
51
|
export type TipTapNode<
|
|
42
52
|
Name extends string,
|
|
53
|
+
ContainsInlineContent extends boolean,
|
|
43
54
|
Options extends {
|
|
44
55
|
domAttributes?: BlockNoteDOMAttributes;
|
|
45
56
|
} = {
|
|
46
57
|
domAttributes?: BlockNoteDOMAttributes;
|
|
47
58
|
},
|
|
48
59
|
Storage = any
|
|
49
|
-
> =
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
> = {
|
|
61
|
+
[Key in keyof Node<Options, Storage>]: Key extends "name"
|
|
62
|
+
? Name
|
|
63
|
+
: Key extends "config"
|
|
64
|
+
? {
|
|
65
|
+
[ConfigKey in keyof Node<
|
|
66
|
+
Options,
|
|
67
|
+
Storage
|
|
68
|
+
>["config"]]: ConfigKey extends "group"
|
|
69
|
+
? "blockContent"
|
|
70
|
+
: ConfigKey extends "content"
|
|
71
|
+
? ContainsInlineContent extends true
|
|
72
|
+
? "inline*"
|
|
73
|
+
: ""
|
|
74
|
+
: NodeConfig<Options, Storage>["config"][ConfigKey];
|
|
75
|
+
} & {
|
|
76
|
+
group: "blockContent";
|
|
77
|
+
content: ContainsInlineContent extends true ? "inline*" : "";
|
|
78
|
+
}
|
|
79
|
+
: Node<Options, Storage>["config"][Key];
|
|
52
80
|
};
|
|
53
81
|
|
|
54
82
|
// Defines a single prop spec, which includes the default value the prop should
|
|
55
83
|
// take and possible values it can take.
|
|
56
|
-
export type PropSpec = {
|
|
57
|
-
values?: readonly
|
|
58
|
-
default:
|
|
84
|
+
export type PropSpec<PType extends boolean | number | string> = {
|
|
85
|
+
values?: readonly PType[];
|
|
86
|
+
default: PType;
|
|
59
87
|
};
|
|
60
88
|
|
|
61
89
|
// Defines multiple block prop specs. The key of each prop is the name of the
|
|
@@ -63,15 +91,25 @@ export type PropSpec = {
|
|
|
63
91
|
// in a block config or schema. From a prop schema, we can derive both the props'
|
|
64
92
|
// internal implementation (as TipTap node attributes) and the type information
|
|
65
93
|
// for the external API.
|
|
66
|
-
export type PropSchema = Record<string, PropSpec
|
|
94
|
+
export type PropSchema = Record<string, PropSpec<boolean | number | string>>;
|
|
67
95
|
|
|
68
96
|
// Defines Props objects for use in Block objects in the external API. Converts
|
|
69
97
|
// each prop spec into a union type of its possible values, or a string if no
|
|
70
98
|
// values are specified.
|
|
71
99
|
export type Props<PSchema extends PropSchema> = {
|
|
72
|
-
[
|
|
73
|
-
? PSchema[
|
|
74
|
-
|
|
100
|
+
[PName in keyof PSchema]: PSchema[PName]["default"] extends boolean
|
|
101
|
+
? PSchema[PName]["values"] extends readonly boolean[]
|
|
102
|
+
? PSchema[PName]["values"][number]
|
|
103
|
+
: boolean
|
|
104
|
+
: PSchema[PName]["default"] extends number
|
|
105
|
+
? PSchema[PName]["values"] extends readonly number[]
|
|
106
|
+
? PSchema[PName]["values"][number]
|
|
107
|
+
: number
|
|
108
|
+
: PSchema[PName]["default"] extends string
|
|
109
|
+
? PSchema[PName]["values"] extends readonly string[]
|
|
110
|
+
? PSchema[PName]["values"][number]
|
|
111
|
+
: string
|
|
112
|
+
: never;
|
|
75
113
|
};
|
|
76
114
|
|
|
77
115
|
// Defines the config for a single block. Meant to be used as an argument to
|
|
@@ -96,7 +134,9 @@ export type BlockConfig<
|
|
|
96
134
|
* The custom block to render
|
|
97
135
|
*/
|
|
98
136
|
block: SpecificBlock<
|
|
99
|
-
BSchema & {
|
|
137
|
+
BSchema & {
|
|
138
|
+
[k in Type]: BlockSpec<Type, PSchema, ContainsInlineContent>;
|
|
139
|
+
},
|
|
100
140
|
Type
|
|
101
141
|
>,
|
|
102
142
|
/**
|
|
@@ -104,16 +144,20 @@ export type BlockConfig<
|
|
|
104
144
|
* This is typed generically. If you want an editor with your custom schema, you need to
|
|
105
145
|
* cast it manually, e.g.: `const e = editor as BlockNoteEditor<typeof mySchema>;`
|
|
106
146
|
*/
|
|
107
|
-
editor: BlockNoteEditor<
|
|
147
|
+
editor: BlockNoteEditor<
|
|
148
|
+
BSchema & { [k in Type]: BlockSpec<Type, PSchema, ContainsInlineContent> }
|
|
149
|
+
>
|
|
108
150
|
// (note) if we want to fix the manual cast, we need to prevent circular references and separate block definition and render implementations
|
|
109
151
|
// or allow manually passing <BSchema>, but that's not possible without passing the other generics because Typescript doesn't support partial inferred generics
|
|
110
152
|
) => ContainsInlineContent extends true
|
|
111
153
|
? {
|
|
112
154
|
dom: HTMLElement;
|
|
113
155
|
contentDOM: HTMLElement;
|
|
156
|
+
destroy?: () => void;
|
|
114
157
|
}
|
|
115
158
|
: {
|
|
116
159
|
dom: HTMLElement;
|
|
160
|
+
destroy?: () => void;
|
|
117
161
|
};
|
|
118
162
|
};
|
|
119
163
|
|
|
@@ -121,18 +165,22 @@ export type BlockConfig<
|
|
|
121
165
|
// the TipTap node used to implement it. Usually created using `createBlockSpec`
|
|
122
166
|
// though it can also be defined from scratch by providing your own TipTap node,
|
|
123
167
|
// allowing for more advanced custom blocks.
|
|
124
|
-
export type BlockSpec<
|
|
168
|
+
export type BlockSpec<
|
|
169
|
+
Type extends string,
|
|
170
|
+
PSchema extends PropSchema,
|
|
171
|
+
ContainsInlineContent extends boolean
|
|
172
|
+
> = {
|
|
173
|
+
node: TipTapNode<Type, ContainsInlineContent, any>;
|
|
125
174
|
readonly propSchema: PSchema;
|
|
126
|
-
node: TipTapNode<Type, any>;
|
|
127
175
|
};
|
|
128
176
|
|
|
129
177
|
// Utility type. For a given object block schema, ensures that the key of each
|
|
130
178
|
// block spec matches the name of the TipTap node in it.
|
|
131
|
-
|
|
132
|
-
Blocks extends Record<string, BlockSpec<string, PropSchema>>
|
|
179
|
+
type NamesMatch<
|
|
180
|
+
Blocks extends Record<string, BlockSpec<string, PropSchema, boolean>>
|
|
133
181
|
> = Blocks extends {
|
|
134
182
|
[Type in keyof Blocks]: Type extends string
|
|
135
|
-
? Blocks[Type] extends BlockSpec<Type, PropSchema>
|
|
183
|
+
? Blocks[Type] extends BlockSpec<Type, PropSchema, boolean>
|
|
136
184
|
? Blocks[Type]
|
|
137
185
|
: never
|
|
138
186
|
: never;
|
|
@@ -145,8 +193,8 @@ export type TypesMatch<
|
|
|
145
193
|
// `blocks` option of the BlockNoteEditor. From a block schema, we can derive
|
|
146
194
|
// both the blocks' internal implementation (as TipTap nodes) and the type
|
|
147
195
|
// information for the external API.
|
|
148
|
-
export type BlockSchema =
|
|
149
|
-
Record<string, BlockSpec<string, PropSchema>>
|
|
196
|
+
export type BlockSchema = NamesMatch<
|
|
197
|
+
Record<string, BlockSpec<string, PropSchema, boolean>>
|
|
150
198
|
>;
|
|
151
199
|
|
|
152
200
|
// Converts each block spec into a Block object without children. We later merge
|
|
@@ -157,7 +205,9 @@ type BlocksWithoutChildren<BSchema extends BlockSchema> = {
|
|
|
157
205
|
id: string;
|
|
158
206
|
type: BType;
|
|
159
207
|
props: Props<BSchema[BType]["propSchema"]>;
|
|
160
|
-
content:
|
|
208
|
+
content: BSchema[BType]["node"]["config"]["content"] extends "inline*"
|
|
209
|
+
? InlineContent[]
|
|
210
|
+
: undefined;
|
|
161
211
|
};
|
|
162
212
|
};
|
|
163
213
|
|
|
@@ -182,7 +232,9 @@ type PartialBlocksWithoutChildren<BSchema extends BlockSchema> = {
|
|
|
182
232
|
id: string;
|
|
183
233
|
type: BType;
|
|
184
234
|
props: Partial<Props<BSchema[BType]["propSchema"]>>;
|
|
185
|
-
content:
|
|
235
|
+
content: BSchema[BType]["node"]["config"]["content"] extends "inline*"
|
|
236
|
+
? PartialInlineContent[] | string
|
|
237
|
+
: undefined;
|
|
186
238
|
}>;
|
|
187
239
|
};
|
|
188
240
|
|
|
@@ -1,44 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export const defaultProps = {
|
|
8
|
-
backgroundColor: {
|
|
9
|
-
default: "transparent" as const,
|
|
10
|
-
},
|
|
11
|
-
textColor: {
|
|
12
|
-
default: "black" as const, // TODO
|
|
13
|
-
},
|
|
14
|
-
textAlignment: {
|
|
15
|
-
default: "left" as const,
|
|
16
|
-
values: ["left", "center", "right", "justify"] as const,
|
|
17
|
-
},
|
|
18
|
-
} satisfies PropSchema;
|
|
19
|
-
|
|
20
|
-
export type DefaultProps = typeof defaultProps;
|
|
1
|
+
import { Heading } from "../nodes/BlockContent/HeadingBlockContent/HeadingBlockContent";
|
|
2
|
+
import { BulletListItem } from "../nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent";
|
|
3
|
+
import { NumberedListItem } from "../nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent";
|
|
4
|
+
import { Paragraph } from "../nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent";
|
|
5
|
+
import { Image } from "../nodes/BlockContent/ImageBlockContent/ImageBlockContent";
|
|
6
|
+
import { BlockSchema } from "./blockTypes";
|
|
21
7
|
|
|
22
8
|
export const defaultBlockSchema = {
|
|
23
|
-
paragraph:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
...defaultProps,
|
|
30
|
-
level: { default: "1", values: ["1", "2", "3"] as const },
|
|
31
|
-
},
|
|
32
|
-
node: HeadingBlockContent,
|
|
33
|
-
},
|
|
34
|
-
bulletListItem: {
|
|
35
|
-
propSchema: defaultProps,
|
|
36
|
-
node: BulletListItemBlockContent,
|
|
37
|
-
},
|
|
38
|
-
numberedListItem: {
|
|
39
|
-
propSchema: defaultProps,
|
|
40
|
-
node: NumberedListItemBlockContent,
|
|
41
|
-
},
|
|
42
|
-
} as const;
|
|
9
|
+
paragraph: Paragraph,
|
|
10
|
+
heading: Heading,
|
|
11
|
+
bulletListItem: BulletListItem,
|
|
12
|
+
numberedListItem: NumberedListItem,
|
|
13
|
+
image: Image,
|
|
14
|
+
} as const satisfies BlockSchema;
|
|
43
15
|
|
|
44
|
-
export type DefaultBlockSchema =
|
|
16
|
+
export type DefaultBlockSchema = typeof defaultBlockSchema;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Props, PropSchema } from "./blockTypes";
|
|
2
|
+
|
|
3
|
+
export const defaultProps = {
|
|
4
|
+
backgroundColor: {
|
|
5
|
+
default: "default" as const,
|
|
6
|
+
},
|
|
7
|
+
textColor: {
|
|
8
|
+
default: "default" as const,
|
|
9
|
+
},
|
|
10
|
+
textAlignment: {
|
|
11
|
+
default: "left" as const,
|
|
12
|
+
values: ["left", "center", "right", "justify"] as const,
|
|
13
|
+
},
|
|
14
|
+
} satisfies PropSchema;
|
|
15
|
+
|
|
16
|
+
export type DefaultProps = Props<typeof defaultProps>;
|
|
@@ -56,20 +56,6 @@ NESTED BLOCKS
|
|
|
56
56
|
transition: all 0.2s 0.1s;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
[data-theme="light"]
|
|
60
|
-
.blockGroup
|
|
61
|
-
.blockGroup
|
|
62
|
-
> .blockOuter:not([data-prev-depth-changed])::before {
|
|
63
|
-
border-left: 1px solid #AFAFAF;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
[data-theme="dark"]
|
|
67
|
-
.blockGroup
|
|
68
|
-
.blockGroup
|
|
69
|
-
> .blockOuter:not([data-prev-depth-changed])::before {
|
|
70
|
-
border-left: 1px solid #7F7F7F;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
59
|
.blockGroup .blockGroup > .blockOuter[data-prev-depth-change="-2"]::before {
|
|
74
60
|
height: 0;
|
|
75
61
|
}
|
|
@@ -235,6 +221,80 @@ NESTED BLOCKS
|
|
|
235
221
|
content: "▪";
|
|
236
222
|
}
|
|
237
223
|
|
|
224
|
+
/* IMAGES */
|
|
225
|
+
|
|
226
|
+
[data-content-type="image"] .wrapper {
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
user-select: none;
|
|
231
|
+
width: 100%;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
[data-content-type="image"] .addImageButton {
|
|
235
|
+
display: flex;
|
|
236
|
+
flex-direction: row;
|
|
237
|
+
align-items: center;
|
|
238
|
+
gap: 8px;
|
|
239
|
+
background-color: whitesmoke;
|
|
240
|
+
border-radius: 4px;
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
padding: 12px;
|
|
243
|
+
width: 100%;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-content-type="image"] .addImageButton:hover {
|
|
247
|
+
background-color: gainsboro;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
[data-content-type="image"] .addImageButtonIcon {
|
|
251
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 5H4V19L13.2923 9.70649C13.6828 9.31595 14.3159 9.31591 14.7065 9.70641L20 15.0104V5ZM2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 11C6.89543 11 6 10.1046 6 9C6 7.89543 6.89543 7 8 7C9.10457 7 10 7.89543 10 9C10 10.1046 9.10457 11 8 11Z'%3E%3C/path%3E%3C/svg%3E");
|
|
252
|
+
width: 24px;
|
|
253
|
+
height: 24px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
[data-content-type="image"] .addImageButtonText {
|
|
257
|
+
color: black;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
[data-content-type="image"] .imageAndCaptionWrapper {
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: column;
|
|
263
|
+
border-radius: 4px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
[data-content-type="image"] .imageWrapper {
|
|
267
|
+
display: flex;
|
|
268
|
+
flex-direction: row;
|
|
269
|
+
align-items: center;
|
|
270
|
+
position: relative;
|
|
271
|
+
width: fit-content;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
[data-content-type="image"] .image {
|
|
275
|
+
border-radius: 4px;
|
|
276
|
+
max-width: 100%;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
[data-content-type="image"] .resizeHandle {
|
|
280
|
+
display: none;
|
|
281
|
+
position: absolute;
|
|
282
|
+
width: 8px;
|
|
283
|
+
height: 30px;
|
|
284
|
+
background-color: black;
|
|
285
|
+
border: 1px solid white;
|
|
286
|
+
border-radius: 4px;
|
|
287
|
+
cursor: ew-resize;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
[data-content-type="image"] .imageWrapper:hover .resizeHandle {
|
|
291
|
+
display: block;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
[data-content-type="image"] .caption {
|
|
295
|
+
font-size: 0.8em
|
|
296
|
+
}
|
|
297
|
+
|
|
238
298
|
/* PLACEHOLDERS*/
|
|
239
299
|
|
|
240
300
|
.isEmpty .inlineContent:before,
|
|
@@ -248,16 +308,6 @@ NESTED BLOCKS
|
|
|
248
308
|
font-style: italic;
|
|
249
309
|
}
|
|
250
310
|
|
|
251
|
-
[data-theme="light"] .isEmpty .inlineContent:before,
|
|
252
|
-
.isFilter .inlineContent:before {
|
|
253
|
-
color: #CFCFCF;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
[data-theme="dark"] .isEmpty .inlineContent:before,
|
|
257
|
-
.isFilter .inlineContent:before {
|
|
258
|
-
color: #7F7F7F;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
311
|
/* TODO: would be nicer if defined from code */
|
|
262
312
|
|
|
263
313
|
.blockContent.isEmpty.hasAnchor .inlineContent:before {
|
|
@@ -278,6 +328,10 @@ NESTED BLOCKS
|
|
|
278
328
|
content: "List";
|
|
279
329
|
}
|
|
280
330
|
|
|
331
|
+
.isEmpty .blockContent[data-content-type="captionedImage"] .inlineContent:before {
|
|
332
|
+
content: "Caption";
|
|
333
|
+
}
|
|
334
|
+
|
|
281
335
|
/* TEXT COLORS */
|
|
282
336
|
[data-text-color="gray"] {
|
|
283
337
|
color: #9b9a97;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mergeAttributes, Node } from "@tiptap/core";
|
|
2
2
|
import { Fragment, Node as PMNode, Slice } from "prosemirror-model";
|
|
3
|
-
import { TextSelection } from "prosemirror-state";
|
|
3
|
+
import { NodeSelection, TextSelection } from "prosemirror-state";
|
|
4
4
|
import {
|
|
5
5
|
blockToNode,
|
|
6
6
|
inlineContentToNodes,
|
|
@@ -16,6 +16,7 @@ import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
|
|
|
16
16
|
import styles from "./Block.module.css";
|
|
17
17
|
import BlockAttributes from "./BlockAttributes";
|
|
18
18
|
import { mergeCSSClasses } from "../../../shared/utils";
|
|
19
|
+
import { NonEditableBlockPlugin } from "../NonEditableBlockPlugin";
|
|
19
20
|
|
|
20
21
|
declare module "@tiptap/core" {
|
|
21
22
|
interface Commands<ReturnType> {
|
|
@@ -205,14 +206,20 @@ export const BlockContainer = Node.create<{
|
|
|
205
206
|
// Replaces the blockContent node with one of the new type and
|
|
206
207
|
// adds the provided props as attributes. Also preserves all
|
|
207
208
|
// existing attributes that are compatible with the new type.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
// Need to reset the selection since replacing the block content
|
|
210
|
+
// sets it to the next block.
|
|
211
|
+
state.tr
|
|
212
|
+
.replaceWith(
|
|
213
|
+
startPos,
|
|
214
|
+
endPos,
|
|
215
|
+
state.schema.nodes[newType].create({
|
|
216
|
+
...contentNode.attrs,
|
|
217
|
+
...block.props,
|
|
218
|
+
})
|
|
219
|
+
)
|
|
220
|
+
.setSelection(
|
|
221
|
+
new NodeSelection(state.tr.doc.resolve(startPos))
|
|
222
|
+
);
|
|
216
223
|
} else {
|
|
217
224
|
// Changes the blockContent node type and adds the provided props
|
|
218
225
|
// as attributes. Also preserves all existing attributes that are
|
|
@@ -404,7 +411,7 @@ export const BlockContainer = Node.create<{
|
|
|
404
411
|
},
|
|
405
412
|
|
|
406
413
|
addProseMirrorPlugins() {
|
|
407
|
-
return [PreviousBlockTypePlugin()];
|
|
414
|
+
return [PreviousBlockTypePlugin(), NonEditableBlockPlugin()];
|
|
408
415
|
},
|
|
409
416
|
|
|
410
417
|
addKeyboardShortcuts() {
|
|
@@ -577,37 +584,6 @@ export const BlockContainer = Node.create<{
|
|
|
577
584
|
this.editor.commands.BNCreateBlock(
|
|
578
585
|
this.editor.state.selection.anchor + 2
|
|
579
586
|
),
|
|
580
|
-
"Mod-Alt-1": () =>
|
|
581
|
-
this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
|
|
582
|
-
type: "heading",
|
|
583
|
-
props: {
|
|
584
|
-
level: "1",
|
|
585
|
-
},
|
|
586
|
-
}),
|
|
587
|
-
"Mod-Alt-2": () =>
|
|
588
|
-
this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
|
|
589
|
-
type: "heading",
|
|
590
|
-
props: {
|
|
591
|
-
level: "2",
|
|
592
|
-
},
|
|
593
|
-
}),
|
|
594
|
-
"Mod-Alt-3": () =>
|
|
595
|
-
this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
|
|
596
|
-
type: "heading",
|
|
597
|
-
props: {
|
|
598
|
-
level: "3",
|
|
599
|
-
},
|
|
600
|
-
}),
|
|
601
|
-
"Mod-Shift-7": () =>
|
|
602
|
-
this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
|
|
603
|
-
type: "bulletListItem",
|
|
604
|
-
props: {},
|
|
605
|
-
}),
|
|
606
|
-
"Mod-Shift-8": () =>
|
|
607
|
-
this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
|
|
608
|
-
type: "numberedListItem",
|
|
609
|
-
props: {},
|
|
610
|
-
}),
|
|
611
587
|
};
|
|
612
588
|
},
|
|
613
589
|
});
|