@blocknote/core 0.2.3 → 0.3.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.
- package/dist/blocknote.js +753 -633
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +1 -1
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +19 -28
- package/src/BlockNoteExtensions.ts +10 -0
- package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +61 -0
- package/src/extensions/BackgroundColor/BackgroundColorMark.ts +62 -0
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +107 -97
- package/src/extensions/Blocks/nodes/Block.module.css +91 -6
- package/src/extensions/Blocks/nodes/BlockContainer.ts +39 -26
- package/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts +4 -4
- package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +8 -0
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +80 -35
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +16 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +34 -3
- package/src/extensions/Placeholder/PlaceholderExtension.ts +1 -1
- package/src/extensions/SlashMenu/SlashMenuExtension.ts +1 -1
- package/src/extensions/SlashMenu/SlashMenuItem.ts +3 -28
- package/src/extensions/SlashMenu/defaultCommands.tsx +14 -32
- package/src/extensions/SlashMenu/index.ts +1 -6
- package/src/extensions/TextAlignment/TextAlignmentExtension.ts +75 -0
- package/src/extensions/TextColor/TextColorExtension.ts +54 -0
- package/src/extensions/TextColor/TextColorMark.ts +62 -0
- package/src/shared/plugins/suggestion/SuggestionItem.ts +0 -9
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +191 -228
- package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +1 -1
- package/types/src/api/Document.d.ts +5 -0
- package/types/src/extensions/BackgroundColor/BackgroundColorExtension.d.ts +9 -0
- package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +9 -0
- package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +3 -2
- package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +4 -0
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +4 -9
- package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +10 -0
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +2 -19
- package/types/src/extensions/SlashMenu/defaultSlashCommands.d.ts +5 -0
- package/types/src/extensions/SlashMenu/index.d.ts +1 -2
- package/types/src/extensions/TextAlignment/TextAlignmentExtension.d.ts +9 -0
- package/types/src/extensions/TextColor/TextColorExtension.d.ts +9 -0
- package/types/src/extensions/TextColor/TextColorMark.d.ts +9 -0
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +0 -6
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +9 -23
- package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mergeAttributes, Node } from "@tiptap/core";
|
|
2
|
-
import { Slice } from "prosemirror-model";
|
|
2
|
+
import { Fragment, Slice } from "prosemirror-model";
|
|
3
3
|
import { TextSelection } from "prosemirror-state";
|
|
4
4
|
import { BlockUpdate } from "../apiTypes";
|
|
5
5
|
import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
|
|
@@ -49,17 +49,6 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
49
49
|
};
|
|
50
50
|
},
|
|
51
51
|
|
|
52
|
-
addAttributes() {
|
|
53
|
-
return {
|
|
54
|
-
blockColor: {
|
|
55
|
-
default: undefined,
|
|
56
|
-
},
|
|
57
|
-
blockStyle: {
|
|
58
|
-
default: undefined,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
},
|
|
62
|
-
|
|
63
52
|
parseHTML() {
|
|
64
53
|
return [
|
|
65
54
|
{
|
|
@@ -233,20 +222,36 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
233
222
|
const { contentNode, contentType, startPos, endPos, depth } =
|
|
234
223
|
blockInfo;
|
|
235
224
|
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
// Creates new block first, otherwise positions get changed due to the original block's content changing.
|
|
239
|
-
// Only text content is transferred to the new block.
|
|
240
|
-
const secondBlockContent = state.doc.textBetween(posInBlock, endPos);
|
|
225
|
+
const originalBlockContent = state.doc.cut(startPos + 1, posInBlock);
|
|
226
|
+
const newBlockContent = state.doc.cut(posInBlock, endPos - 1);
|
|
241
227
|
|
|
242
228
|
const newBlock =
|
|
243
229
|
state.schema.nodes["blockContainer"].createAndFill()!;
|
|
230
|
+
|
|
231
|
+
const newBlockInsertionPos = endPos + 1;
|
|
244
232
|
const newBlockContentPos = newBlockInsertionPos + 2;
|
|
245
233
|
|
|
246
234
|
if (dispatch) {
|
|
235
|
+
// Creates a new block. Since the schema requires it to have a content node, a paragraph node is created
|
|
236
|
+
// automatically, spanning newBlockContentPos to newBlockContentPos + 1.
|
|
247
237
|
state.tr.insert(newBlockInsertionPos, newBlock);
|
|
248
|
-
state.tr.insertText(secondBlockContent, newBlockContentPos);
|
|
249
238
|
|
|
239
|
+
// Replaces the content of the newly created block's content node. Doesn't replace the whole content node so
|
|
240
|
+
// its type doesn't change.
|
|
241
|
+
state.tr.replace(
|
|
242
|
+
newBlockContentPos,
|
|
243
|
+
newBlockContentPos + 1,
|
|
244
|
+
newBlockContent.content.size > 0
|
|
245
|
+
? new Slice(
|
|
246
|
+
Fragment.from(newBlockContent),
|
|
247
|
+
depth + 2,
|
|
248
|
+
depth + 2
|
|
249
|
+
)
|
|
250
|
+
: undefined
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Changes the type of the content node. The range doesn't matter as long as both from and to positions are
|
|
254
|
+
// within the content node.
|
|
250
255
|
if (keepType) {
|
|
251
256
|
state.tr.setBlockType(
|
|
252
257
|
newBlockContentPos,
|
|
@@ -255,22 +260,30 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
255
260
|
contentNode.attrs
|
|
256
261
|
);
|
|
257
262
|
}
|
|
258
|
-
}
|
|
259
263
|
|
|
260
|
-
|
|
261
|
-
|
|
264
|
+
// Sets the selection to the start of the new block's content node.
|
|
265
|
+
state.tr.setSelection(
|
|
266
|
+
new TextSelection(state.doc.resolve(newBlockContentPos))
|
|
267
|
+
);
|
|
262
268
|
|
|
263
|
-
|
|
269
|
+
// Replaces the content of the original block's content node. Doesn't replace the whole content node so its
|
|
270
|
+
// type doesn't change.
|
|
264
271
|
state.tr.replace(
|
|
265
|
-
startPos,
|
|
266
|
-
endPos,
|
|
267
|
-
|
|
272
|
+
startPos + 1,
|
|
273
|
+
endPos - 1,
|
|
274
|
+
originalBlockContent.content.size > 0
|
|
275
|
+
? new Slice(
|
|
276
|
+
Fragment.from(originalBlockContent),
|
|
277
|
+
depth + 2,
|
|
278
|
+
depth + 2
|
|
279
|
+
)
|
|
280
|
+
: undefined
|
|
268
281
|
);
|
|
269
282
|
}
|
|
270
283
|
|
|
271
284
|
return true;
|
|
272
285
|
},
|
|
273
|
-
//
|
|
286
|
+
// Updates the type and attributes of a block at a given position.
|
|
274
287
|
BNUpdateBlock:
|
|
275
288
|
(posInBlock, blockUpdate) =>
|
|
276
289
|
({ state, dispatch }) => {
|
package/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Node } from "@tiptap/core";
|
|
1
|
+
import { mergeAttributes, Node } from "@tiptap/core";
|
|
2
2
|
import styles from "../../Block.module.css";
|
|
3
3
|
|
|
4
4
|
export const ParagraphBlockContent = Node.create({
|
|
@@ -16,13 +16,13 @@ export const ParagraphBlockContent = Node.create({
|
|
|
16
16
|
];
|
|
17
17
|
},
|
|
18
18
|
|
|
19
|
-
renderHTML() {
|
|
19
|
+
renderHTML({ HTMLAttributes }) {
|
|
20
20
|
return [
|
|
21
21
|
"div",
|
|
22
|
-
{
|
|
22
|
+
mergeAttributes(HTMLAttributes, {
|
|
23
23
|
class: styles.blockContent,
|
|
24
24
|
"data-content-type": this.name,
|
|
25
|
-
},
|
|
25
|
+
}),
|
|
26
26
|
["p", 0],
|
|
27
27
|
];
|
|
28
28
|
},
|
|
@@ -3,13 +3,21 @@ import { EditorElement, ElementFactory } from "../../shared/EditorElement";
|
|
|
3
3
|
export type BlockSideMenuStaticParams = {
|
|
4
4
|
addBlock: () => void;
|
|
5
5
|
deleteBlock: () => void;
|
|
6
|
+
|
|
6
7
|
blockDragStart: (event: DragEvent) => void;
|
|
7
8
|
blockDragEnd: () => void;
|
|
9
|
+
|
|
8
10
|
freezeMenu: () => void;
|
|
9
11
|
unfreezeMenu: () => void;
|
|
12
|
+
|
|
13
|
+
setBlockTextColor: (color: string) => void;
|
|
14
|
+
setBlockBackgroundColor: (color: string) => void;
|
|
10
15
|
};
|
|
11
16
|
|
|
12
17
|
export type BlockSideMenuDynamicParams = {
|
|
18
|
+
blockTextColor: string;
|
|
19
|
+
blockBackgroundColor: string;
|
|
20
|
+
|
|
13
21
|
referenceRect: DOMRect;
|
|
14
22
|
};
|
|
15
23
|
|
|
@@ -18,21 +18,8 @@ import { MultipleNodeSelection } from "./MultipleNodeSelection";
|
|
|
18
18
|
const serializeForClipboard = (pv as any).__serializeForClipboard;
|
|
19
19
|
// code based on https://github.com/ueberdosis/tiptap/issues/323#issuecomment-506637799
|
|
20
20
|
|
|
21
|
-
let horizontalAnchor: number;
|
|
22
21
|
let dragImageElement: Element | undefined;
|
|
23
22
|
|
|
24
|
-
function getHorizontalAnchor() {
|
|
25
|
-
if (!horizontalAnchor) {
|
|
26
|
-
const firstBlockGroup = document.querySelector(
|
|
27
|
-
".ProseMirror > [class*='blockGroup']"
|
|
28
|
-
) as HTMLElement | undefined; // first block group node
|
|
29
|
-
if (firstBlockGroup) {
|
|
30
|
-
horizontalAnchor = absoluteRect(firstBlockGroup).left;
|
|
31
|
-
} // Anchor to the left of the first block group
|
|
32
|
-
}
|
|
33
|
-
return horizontalAnchor;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
23
|
export function createRect(rect: DOMRect) {
|
|
37
24
|
let newRect = {
|
|
38
25
|
left: rect.left + document.body.scrollLeft,
|
|
@@ -47,10 +34,6 @@ export function createRect(rect: DOMRect) {
|
|
|
47
34
|
return newRect;
|
|
48
35
|
}
|
|
49
36
|
|
|
50
|
-
export function absoluteRect(element: HTMLElement) {
|
|
51
|
-
return createRect(element.getBoundingClientRect());
|
|
52
|
-
}
|
|
53
|
-
|
|
54
37
|
function getDraggableBlockFromCoords(
|
|
55
38
|
coords: { left: number; top: number },
|
|
56
39
|
view: EditorView
|
|
@@ -184,8 +167,10 @@ function dragStart(e: DragEvent, view: EditorView) {
|
|
|
184
167
|
return;
|
|
185
168
|
}
|
|
186
169
|
|
|
170
|
+
const editorBoundingBox = view.dom.getBoundingClientRect();
|
|
171
|
+
|
|
187
172
|
let coords = {
|
|
188
|
-
left:
|
|
173
|
+
left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
|
|
189
174
|
top: e.clientY,
|
|
190
175
|
};
|
|
191
176
|
|
|
@@ -238,9 +223,11 @@ export class BlockMenuView {
|
|
|
238
223
|
// When false, the drag handle with be just to the left of the element
|
|
239
224
|
horizontalPosAnchoredAtRoot: boolean;
|
|
240
225
|
|
|
226
|
+
horizontalPosAnchor: number;
|
|
227
|
+
|
|
241
228
|
blockMenu: BlockSideMenu;
|
|
242
229
|
|
|
243
|
-
|
|
230
|
+
hoveredBlockContent: HTMLElement | undefined;
|
|
244
231
|
|
|
245
232
|
menuOpen = false;
|
|
246
233
|
menuFrozen = false;
|
|
@@ -252,6 +239,9 @@ export class BlockMenuView {
|
|
|
252
239
|
}: BlockMenuViewProps) {
|
|
253
240
|
this.editor = editor;
|
|
254
241
|
this.horizontalPosAnchoredAtRoot = horizontalPosAnchoredAtRoot;
|
|
242
|
+
this.horizontalPosAnchor = (
|
|
243
|
+
editor.view.dom.firstChild! as HTMLElement
|
|
244
|
+
).getBoundingClientRect().x;
|
|
255
245
|
|
|
256
246
|
this.blockMenu = blockMenuFactory(this.getStaticParams());
|
|
257
247
|
|
|
@@ -263,9 +253,17 @@ export class BlockMenuView {
|
|
|
263
253
|
return;
|
|
264
254
|
}
|
|
265
255
|
|
|
256
|
+
// Editor itself may have padding or other styling which affects size/position, so we get the boundingRect of
|
|
257
|
+
// the first child (i.e. the blockGroup that wraps all blocks in the editor) for a more accurate bounding box.
|
|
258
|
+
const editorBoundingBox = (
|
|
259
|
+
this.editor.view.dom.firstChild! as HTMLElement
|
|
260
|
+
).getBoundingClientRect();
|
|
261
|
+
|
|
262
|
+
this.horizontalPosAnchor = editorBoundingBox.x;
|
|
263
|
+
|
|
266
264
|
// Gets block at mouse cursor's vertical position.
|
|
267
265
|
const coords = {
|
|
268
|
-
left:
|
|
266
|
+
left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
|
|
269
267
|
top: event.clientY,
|
|
270
268
|
};
|
|
271
269
|
const block = getDraggableBlockFromCoords(coords, this.editor.view);
|
|
@@ -283,15 +281,15 @@ export class BlockMenuView {
|
|
|
283
281
|
// Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
|
|
284
282
|
if (
|
|
285
283
|
this.menuOpen &&
|
|
286
|
-
this.
|
|
287
|
-
this.
|
|
284
|
+
this.hoveredBlockContent?.hasAttribute("data-id") &&
|
|
285
|
+
this.hoveredBlockContent?.getAttribute("data-id") === block.id
|
|
288
286
|
) {
|
|
289
287
|
return;
|
|
290
288
|
}
|
|
291
289
|
|
|
292
290
|
// Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
|
|
293
291
|
const blockContent = block.node.firstChild as HTMLElement;
|
|
294
|
-
this.
|
|
292
|
+
this.hoveredBlockContent = blockContent;
|
|
295
293
|
|
|
296
294
|
if (!blockContent) {
|
|
297
295
|
return;
|
|
@@ -352,11 +350,12 @@ export class BlockMenuView {
|
|
|
352
350
|
this.menuFrozen = true;
|
|
353
351
|
this.blockMenu.hide();
|
|
354
352
|
|
|
355
|
-
const
|
|
353
|
+
const blockContentBoundingBox =
|
|
354
|
+
this.hoveredBlockContent!.getBoundingClientRect();
|
|
356
355
|
|
|
357
356
|
const pos = this.editor.view.posAtCoords({
|
|
358
|
-
left:
|
|
359
|
-
top:
|
|
357
|
+
left: blockContentBoundingBox.left + blockContentBoundingBox.width / 2,
|
|
358
|
+
top: blockContentBoundingBox.top + blockContentBoundingBox.height / 2,
|
|
360
359
|
});
|
|
361
360
|
if (!pos) {
|
|
362
361
|
return;
|
|
@@ -380,6 +379,8 @@ export class BlockMenuView {
|
|
|
380
379
|
.BNUpdateBlock(newBlockContentPos, { type: "paragraph", props: {} })
|
|
381
380
|
.setTextSelection(newBlockContentPos)
|
|
382
381
|
.run();
|
|
382
|
+
} else {
|
|
383
|
+
this.editor.commands.setTextSelection(endPos);
|
|
383
384
|
}
|
|
384
385
|
|
|
385
386
|
// Focuses and activates the suggestion menu.
|
|
@@ -397,11 +398,12 @@ export class BlockMenuView {
|
|
|
397
398
|
this.menuOpen = false;
|
|
398
399
|
this.blockMenu.hide();
|
|
399
400
|
|
|
400
|
-
const
|
|
401
|
+
const blockContentBoundingBox =
|
|
402
|
+
this.hoveredBlockContent!.getBoundingClientRect();
|
|
401
403
|
|
|
402
404
|
const pos = this.editor.view.posAtCoords({
|
|
403
|
-
left:
|
|
404
|
-
top:
|
|
405
|
+
left: blockContentBoundingBox.left + blockContentBoundingBox.width / 2,
|
|
406
|
+
top: blockContentBoundingBox.top + blockContentBoundingBox.height / 2,
|
|
405
407
|
});
|
|
406
408
|
if (!pos) {
|
|
407
409
|
return;
|
|
@@ -410,6 +412,42 @@ export class BlockMenuView {
|
|
|
410
412
|
this.editor.commands.BNDeleteBlock(pos.pos);
|
|
411
413
|
}
|
|
412
414
|
|
|
415
|
+
setBlockBackgroundColor(color: string) {
|
|
416
|
+
this.menuOpen = false;
|
|
417
|
+
this.blockMenu.hide();
|
|
418
|
+
|
|
419
|
+
const blockContentBoundingBox =
|
|
420
|
+
this.hoveredBlockContent!.getBoundingClientRect();
|
|
421
|
+
|
|
422
|
+
const pos = this.editor.view.posAtCoords({
|
|
423
|
+
left: blockContentBoundingBox.left + blockContentBoundingBox.width / 2,
|
|
424
|
+
top: blockContentBoundingBox.top + blockContentBoundingBox.height / 2,
|
|
425
|
+
});
|
|
426
|
+
if (!pos) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
this.editor.commands.setBlockBackgroundColor(pos.pos, color);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
setBlockTextColor(color: string) {
|
|
434
|
+
this.menuOpen = false;
|
|
435
|
+
this.blockMenu.hide();
|
|
436
|
+
|
|
437
|
+
const blockContentBoundingBox =
|
|
438
|
+
this.hoveredBlockContent!.getBoundingClientRect();
|
|
439
|
+
|
|
440
|
+
const pos = this.editor.view.posAtCoords({
|
|
441
|
+
left: blockContentBoundingBox.left + blockContentBoundingBox.width / 2,
|
|
442
|
+
top: blockContentBoundingBox.top + blockContentBoundingBox.height / 2,
|
|
443
|
+
});
|
|
444
|
+
if (!pos) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
this.editor.commands.setBlockTextColor(pos.pos, color);
|
|
449
|
+
}
|
|
450
|
+
|
|
413
451
|
getStaticParams(): BlockSideMenuStaticParams {
|
|
414
452
|
return {
|
|
415
453
|
addBlock: () => this.addBlock(),
|
|
@@ -422,20 +460,27 @@ export class BlockMenuView {
|
|
|
422
460
|
unfreezeMenu: () => {
|
|
423
461
|
this.menuFrozen = false;
|
|
424
462
|
},
|
|
463
|
+
setBlockBackgroundColor: (color: string) =>
|
|
464
|
+
this.setBlockBackgroundColor(color),
|
|
465
|
+
setBlockTextColor: (color: string) => this.setBlockTextColor(color),
|
|
425
466
|
};
|
|
426
467
|
}
|
|
427
468
|
|
|
428
469
|
getDynamicParams(): BlockSideMenuDynamicParams {
|
|
429
|
-
const
|
|
470
|
+
const blockContentBoundingBox =
|
|
471
|
+
this.hoveredBlockContent!.getBoundingClientRect();
|
|
430
472
|
|
|
431
473
|
return {
|
|
474
|
+
blockBackgroundColor:
|
|
475
|
+
this.editor.getAttributes("blockContainer").backgroundColor,
|
|
476
|
+
blockTextColor: this.editor.getAttributes("blockContainer").textColor,
|
|
432
477
|
referenceRect: new DOMRect(
|
|
433
478
|
this.horizontalPosAnchoredAtRoot
|
|
434
|
-
?
|
|
435
|
-
:
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
479
|
+
? this.horizontalPosAnchor
|
|
480
|
+
: blockContentBoundingBox.x,
|
|
481
|
+
blockContentBoundingBox.y,
|
|
482
|
+
blockContentBoundingBox.width,
|
|
483
|
+
blockContentBoundingBox.height
|
|
439
484
|
),
|
|
440
485
|
};
|
|
441
486
|
}
|
|
@@ -8,6 +8,15 @@ export type FormattingToolbarStaticParams = {
|
|
|
8
8
|
toggleStrike: () => void;
|
|
9
9
|
setHyperlink: (url: string, text?: string) => void;
|
|
10
10
|
|
|
11
|
+
setTextColor: (color: string) => void;
|
|
12
|
+
setBackgroundColor: (color: string) => void;
|
|
13
|
+
setTextAlignment: (
|
|
14
|
+
textAlignment: "left" | "center" | "right" | "justify"
|
|
15
|
+
) => void;
|
|
16
|
+
|
|
17
|
+
increaseBlockIndent: () => void;
|
|
18
|
+
decreaseBlockIndent: () => void;
|
|
19
|
+
|
|
11
20
|
updateBlock: (blockUpdate: BlockUpdate) => void;
|
|
12
21
|
};
|
|
13
22
|
|
|
@@ -20,6 +29,13 @@ export type FormattingToolbarDynamicParams = {
|
|
|
20
29
|
activeHyperlinkUrl: string;
|
|
21
30
|
activeHyperlinkText: string;
|
|
22
31
|
|
|
32
|
+
textColor: string;
|
|
33
|
+
backgroundColor: string;
|
|
34
|
+
textAlignment: "left" | "center" | "right" | "justify";
|
|
35
|
+
|
|
36
|
+
canIncreaseBlockIndent: boolean;
|
|
37
|
+
canDecreaseBlockIndent: boolean;
|
|
38
|
+
|
|
23
39
|
block: Block;
|
|
24
40
|
|
|
25
41
|
referenceRect: DOMRect;
|
|
@@ -266,6 +266,28 @@ export class FormattingToolbarView {
|
|
|
266
266
|
);
|
|
267
267
|
this.editor.view.focus();
|
|
268
268
|
},
|
|
269
|
+
setTextColor: (color: string) => {
|
|
270
|
+
this.editor.view.focus();
|
|
271
|
+
this.editor.commands.setTextColor(color);
|
|
272
|
+
},
|
|
273
|
+
setBackgroundColor: (color: string) => {
|
|
274
|
+
this.editor.view.focus();
|
|
275
|
+
this.editor.commands.setBackgroundColor(color);
|
|
276
|
+
},
|
|
277
|
+
setTextAlignment: (
|
|
278
|
+
textAlignment: "left" | "center" | "right" | "justify"
|
|
279
|
+
) => {
|
|
280
|
+
this.editor.view.focus();
|
|
281
|
+
this.editor.commands.setTextAlignment(textAlignment);
|
|
282
|
+
},
|
|
283
|
+
increaseBlockIndent: () => {
|
|
284
|
+
this.editor.view.focus();
|
|
285
|
+
this.editor.commands.sinkListItem("blockContainer");
|
|
286
|
+
},
|
|
287
|
+
decreaseBlockIndent: () => {
|
|
288
|
+
this.editor.view.focus();
|
|
289
|
+
this.editor.commands.liftListItem("blockContainer");
|
|
290
|
+
},
|
|
269
291
|
updateBlock: (blockUpdate: BlockUpdate) => {
|
|
270
292
|
this.editor.view.focus();
|
|
271
293
|
this.editor.commands.BNUpdateBlock(
|
|
@@ -288,13 +310,22 @@ export class FormattingToolbarView {
|
|
|
288
310
|
underlineIsActive: this.editor.isActive("underline"),
|
|
289
311
|
strikeIsActive: this.editor.isActive("strike"),
|
|
290
312
|
hyperlinkIsActive: this.editor.isActive("link"),
|
|
291
|
-
activeHyperlinkUrl: this.editor.getAttributes("link").href
|
|
292
|
-
? this.editor.getAttributes("link").href
|
|
293
|
-
: "",
|
|
313
|
+
activeHyperlinkUrl: this.editor.getAttributes("link").href || "",
|
|
294
314
|
activeHyperlinkText: this.editor.state.doc.textBetween(
|
|
295
315
|
this.editor.state.selection.from,
|
|
296
316
|
this.editor.state.selection.to
|
|
297
317
|
),
|
|
318
|
+
textColor: this.editor.getAttributes("textColor").color || "default",
|
|
319
|
+
backgroundColor:
|
|
320
|
+
this.editor.getAttributes("backgroundColor").color || "default",
|
|
321
|
+
textAlignment:
|
|
322
|
+
this.editor.getAttributes(blockInfo.contentType).textAlignment ||
|
|
323
|
+
"left",
|
|
324
|
+
canIncreaseBlockIndent:
|
|
325
|
+
this.editor.state.doc
|
|
326
|
+
.resolve(blockInfo.startPos)
|
|
327
|
+
.index(blockInfo.depth - 1) > 0,
|
|
328
|
+
canDecreaseBlockIndent: blockInfo.depth > 2,
|
|
298
329
|
// Needs type cast as there is no way to create a type that dynamically updates based on which extensions are
|
|
299
330
|
// loaded by the editor.
|
|
300
331
|
block: {
|
|
@@ -81,7 +81,7 @@ export const Placeholder = Extension.create<PlaceholderOptions>({
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// If slash menu is of drag type and active, show the filter placeholder
|
|
84
|
-
if (menuState?.
|
|
84
|
+
if (menuState?.triggerCharacter === "" && menuState?.active) {
|
|
85
85
|
classes.push(this.options.isFilterClass);
|
|
86
86
|
}
|
|
87
87
|
// using widget, didn't work (caret position bug)
|
|
@@ -31,7 +31,7 @@ export const SlashMenuExtension = Extension.create<SlashMenuOptions>({
|
|
|
31
31
|
createSuggestionPlugin<SlashMenuItem>({
|
|
32
32
|
pluginKey: SlashMenuPluginKey,
|
|
33
33
|
editor: this.editor,
|
|
34
|
-
|
|
34
|
+
defaultTriggerCharacter: "/",
|
|
35
35
|
suggestionsMenuFactory: this.options.slashMenuFactory!,
|
|
36
36
|
items: (query) => {
|
|
37
37
|
const commands = [];
|
|
@@ -1,49 +1,24 @@
|
|
|
1
1
|
import { Editor, Range } from "@tiptap/core";
|
|
2
2
|
import { SuggestionItem } from "../../shared/plugins/suggestion/SuggestionItem";
|
|
3
3
|
|
|
4
|
-
export type SlashMenuCallback = (editor: Editor, range: Range) => boolean;
|
|
5
|
-
|
|
6
|
-
export enum SlashMenuGroups {
|
|
7
|
-
HEADINGS = "Headings",
|
|
8
|
-
BASIC_BLOCKS = "Basic Blocks",
|
|
9
|
-
CODE = "Code Blocks",
|
|
10
|
-
|
|
11
|
-
// Just some examples, that are not currently in use
|
|
12
|
-
INLINE = "Inline",
|
|
13
|
-
EMBED = "Embed",
|
|
14
|
-
PLUGIN = "Plugin",
|
|
15
|
-
}
|
|
16
|
-
|
|
17
4
|
/**
|
|
18
5
|
* A class that defines a slash command (/<command>).
|
|
19
6
|
*
|
|
20
7
|
* Not to be confused with ProseMirror commands nor TipTap commands.
|
|
21
8
|
*/
|
|
22
9
|
export class SlashMenuItem implements SuggestionItem {
|
|
23
|
-
groupName: string;
|
|
24
|
-
// other parameters initialized in the constructor
|
|
25
|
-
|
|
26
10
|
/**
|
|
27
11
|
* Constructs a new slash-command.
|
|
28
12
|
*
|
|
29
13
|
* @param name The name of the command
|
|
30
|
-
* @param group Used to organize the menu
|
|
31
14
|
* @param execute The callback for creating a new node
|
|
32
15
|
* @param aliases Aliases for this command
|
|
33
|
-
* @param icon To be shown next to the name in the menu
|
|
34
|
-
* @param hint Short description of command
|
|
35
|
-
* @param shortcut Info about keyboard shortcut that would activate this command
|
|
36
16
|
*/
|
|
37
17
|
constructor(
|
|
38
18
|
public readonly name: string,
|
|
39
|
-
public readonly
|
|
40
|
-
public readonly
|
|
41
|
-
|
|
42
|
-
public readonly hint?: string,
|
|
43
|
-
public readonly shortcut?: string
|
|
44
|
-
) {
|
|
45
|
-
this.groupName = group;
|
|
46
|
-
}
|
|
19
|
+
public readonly execute: (editor: Editor, range: Range) => void,
|
|
20
|
+
public readonly aliases: string[] = []
|
|
21
|
+
) {}
|
|
47
22
|
|
|
48
23
|
match(query: string): boolean {
|
|
49
24
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { SlashMenuItem } from "./SlashMenuItem";
|
|
2
|
+
import { Editor, Range } from "@tiptap/core";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* An array containing commands for creating all default blocks.
|
|
@@ -8,8 +8,7 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
8
8
|
// Command for creating a level 1 heading
|
|
9
9
|
heading: new SlashMenuItem(
|
|
10
10
|
"Heading",
|
|
11
|
-
|
|
12
|
-
(editor, range) => {
|
|
11
|
+
(editor: Editor, range: Range) => {
|
|
13
12
|
return editor
|
|
14
13
|
.chain()
|
|
15
14
|
.focus()
|
|
@@ -22,16 +21,13 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
22
21
|
})
|
|
23
22
|
.run();
|
|
24
23
|
},
|
|
25
|
-
["h", "heading1", "h1"]
|
|
26
|
-
"Used for a top-level heading",
|
|
27
|
-
formatKeyboardShortcut("Mod-Alt-1")
|
|
24
|
+
["h", "heading1", "h1"]
|
|
28
25
|
),
|
|
29
26
|
|
|
30
27
|
// Command for creating a level 2 heading
|
|
31
28
|
heading2: new SlashMenuItem(
|
|
32
29
|
"Heading 2",
|
|
33
|
-
|
|
34
|
-
(editor, range) => {
|
|
30
|
+
(editor: Editor, range: Range) => {
|
|
35
31
|
return editor
|
|
36
32
|
.chain()
|
|
37
33
|
.focus()
|
|
@@ -44,16 +40,13 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
44
40
|
})
|
|
45
41
|
.run();
|
|
46
42
|
},
|
|
47
|
-
["h2", "heading2", "subheading"]
|
|
48
|
-
"Used for key sections",
|
|
49
|
-
formatKeyboardShortcut("Mod-Alt-2")
|
|
43
|
+
["h2", "heading2", "subheading"]
|
|
50
44
|
),
|
|
51
45
|
|
|
52
46
|
// Command for creating a level 3 heading
|
|
53
47
|
heading3: new SlashMenuItem(
|
|
54
48
|
"Heading 3",
|
|
55
|
-
|
|
56
|
-
(editor, range) => {
|
|
49
|
+
(editor: Editor, range: Range) => {
|
|
57
50
|
return editor
|
|
58
51
|
.chain()
|
|
59
52
|
.focus()
|
|
@@ -66,16 +59,13 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
66
59
|
})
|
|
67
60
|
.run();
|
|
68
61
|
},
|
|
69
|
-
["h3", "heading3", "subheading"]
|
|
70
|
-
"Used for subsections and group headings",
|
|
71
|
-
formatKeyboardShortcut("Mod-Alt-3")
|
|
62
|
+
["h3", "heading3", "subheading"]
|
|
72
63
|
),
|
|
73
64
|
|
|
74
65
|
// Command for creating an ordered list
|
|
75
66
|
numberedList: new SlashMenuItem(
|
|
76
67
|
"Numbered List",
|
|
77
|
-
|
|
78
|
-
(editor, range) => {
|
|
68
|
+
(editor: Editor, range: Range) => {
|
|
79
69
|
return editor
|
|
80
70
|
.chain()
|
|
81
71
|
.focus()
|
|
@@ -86,16 +76,13 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
86
76
|
})
|
|
87
77
|
.run();
|
|
88
78
|
},
|
|
89
|
-
["li", "list", "numberedlist", "numbered list"]
|
|
90
|
-
"Used to display a numbered list",
|
|
91
|
-
formatKeyboardShortcut("Mod-Shift-7")
|
|
79
|
+
["li", "list", "numberedlist", "numbered list"]
|
|
92
80
|
),
|
|
93
81
|
|
|
94
82
|
// Command for creating a bullet list
|
|
95
83
|
bulletList: new SlashMenuItem(
|
|
96
84
|
"Bullet List",
|
|
97
|
-
|
|
98
|
-
(editor, range) => {
|
|
85
|
+
(editor: Editor, range: Range) => {
|
|
99
86
|
return editor
|
|
100
87
|
.chain()
|
|
101
88
|
.focus()
|
|
@@ -106,16 +93,13 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
106
93
|
})
|
|
107
94
|
.run();
|
|
108
95
|
},
|
|
109
|
-
["ul", "list", "bulletlist", "bullet list"]
|
|
110
|
-
"Used to display an unordered list",
|
|
111
|
-
formatKeyboardShortcut("Mod-Shift-8")
|
|
96
|
+
["ul", "list", "bulletlist", "bullet list"]
|
|
112
97
|
),
|
|
113
98
|
|
|
114
99
|
// Command for creating a paragraph (pretty useless)
|
|
115
100
|
paragraph: new SlashMenuItem(
|
|
116
101
|
"Paragraph",
|
|
117
|
-
|
|
118
|
-
(editor, range) => {
|
|
102
|
+
(editor: Editor, range: Range) => {
|
|
119
103
|
return editor
|
|
120
104
|
.chain()
|
|
121
105
|
.focus()
|
|
@@ -126,9 +110,7 @@ const defaultCommands: { [key: string]: SlashMenuItem } = {
|
|
|
126
110
|
})
|
|
127
111
|
.run();
|
|
128
112
|
},
|
|
129
|
-
["p"]
|
|
130
|
-
"Used for the body of your document",
|
|
131
|
-
formatKeyboardShortcut("Mod-Alt-0")
|
|
113
|
+
["p"]
|
|
132
114
|
),
|
|
133
115
|
|
|
134
116
|
// replaceRangeWithNode(editor, range, node);
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { SlashMenuExtension } from "./SlashMenuExtension";
|
|
2
2
|
import defaultCommands from "./defaultCommands";
|
|
3
|
-
import { SlashMenuGroups, SlashMenuItem } from "./SlashMenuItem";
|
|
4
3
|
|
|
5
|
-
export {
|
|
6
|
-
defaultCommands,
|
|
7
|
-
SlashMenuItem as SlashCommand,
|
|
8
|
-
SlashMenuGroups as CommandGroup,
|
|
9
|
-
};
|
|
4
|
+
export { defaultCommands };
|
|
10
5
|
|
|
11
6
|
export default SlashMenuExtension;
|