@blocknote/core 0.44.2 → 0.45.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/BlockNoteExtension-BWw0r8Gy.cjs.map +1 -1
- package/dist/BlockNoteExtension-C2X7LW-V.js.map +1 -1
- package/dist/{BlockNoteSchema-BsTi0fNS.js → BlockNoteSchema-BOW16JHv.js} +2 -2
- package/dist/{BlockNoteSchema-BsTi0fNS.js.map → BlockNoteSchema-BOW16JHv.js.map} +1 -1
- package/dist/{BlockNoteSchema-CBNkNhkw.cjs → BlockNoteSchema-CzZbr4Ed.cjs} +2 -2
- package/dist/{BlockNoteSchema-CBNkNhkw.cjs.map → BlockNoteSchema-CzZbr4Ed.cjs.map} +1 -1
- package/dist/{TrailingNode-CG2a-HDA.js → TrailingNode-8cXFaQUm.js} +484 -487
- package/dist/TrailingNode-8cXFaQUm.js.map +1 -0
- package/dist/TrailingNode-DPu6X9ym.cjs +2 -0
- package/dist/TrailingNode-DPu6X9ym.cjs.map +1 -0
- package/dist/{blockToNode-DBNbhwwC.js → blockToNode-BNoNIXU7.js} +2 -2
- package/dist/{blockToNode-DBNbhwwC.js.map → blockToNode-BNoNIXU7.js.map} +1 -1
- package/dist/{blockToNode-w7H99R6p.cjs → blockToNode-CumVjgem.cjs} +2 -2
- package/dist/{blockToNode-w7H99R6p.cjs.map → blockToNode-CumVjgem.cjs.map} +1 -1
- package/dist/blocknote.cjs +4 -4
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +1118 -1077
- package/dist/blocknote.js.map +1 -1
- package/dist/blocks.cjs +1 -1
- package/dist/blocks.js +2 -2
- package/dist/defaultBlocks-D1cc0lV9.cjs +6 -0
- package/dist/defaultBlocks-D1cc0lV9.cjs.map +1 -0
- package/dist/{defaultBlocks-B63ufZ5N.js → defaultBlocks-DvCGYzqu.js} +168 -206
- package/dist/defaultBlocks-DvCGYzqu.js.map +1 -0
- package/dist/extensions.cjs +1 -1
- package/dist/extensions.js +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/dist/yjs.cjs +1 -1
- package/dist/yjs.js +1 -1
- package/package.json +15 -15
- package/src/api/blockManipulation/selections/selection.ts +9 -4
- package/src/api/blockManipulation/tables/tables.test.ts +140 -0
- package/src/api/blockManipulation/tables/tables.ts +1 -1
- package/src/api/parsers/markdown/parseMarkdown.ts +11 -0
- package/src/blocks/ListItem/CheckListItem/block.ts +2 -2
- package/src/blocks/ListItem/NumberedListItem/block.ts +5 -1
- package/src/editor/BlockNoteEditor.test.ts +0 -1
- package/src/editor/BlockNoteEditor.ts +9 -39
- package/src/editor/BlockNoteExtension.ts +5 -0
- package/src/editor/managers/EventManager.ts +1 -1
- package/src/editor/managers/ExtensionManager/extensions.ts +2 -12
- package/src/editor/managers/ExtensionManager/index.ts +7 -2
- package/src/editor/managers/SelectionManager.ts +10 -10
- package/src/extensions/BlockChange/BlockChange.ts +2 -2
- package/src/extensions/Collaboration/Collaboration.ts +55 -0
- package/src/extensions/Collaboration/ForkYDoc.ts +4 -9
- package/src/extensions/Collaboration/YCursorPlugin.ts +56 -60
- package/src/extensions/Collaboration/YSync.ts +2 -2
- package/src/extensions/Collaboration/YUndo.ts +2 -2
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +40 -68
- package/src/extensions/TableHandles/TableHandles.ts +9 -5
- package/src/index.ts +2 -1
- package/src/schema/blocks/createSpec.ts +3 -0
- package/src/util/expandToWords.ts +38 -0
- package/types/src/api/blockManipulation/selections/selection.d.ts +1 -1
- package/types/src/editor/BlockNoteEditor.d.ts +5 -34
- package/types/src/editor/BlockNoteExtension.d.ts +4 -0
- package/types/src/editor/managers/SelectionManager.d.ts +4 -4
- package/types/src/extensions/Collaboration/Collaboration.d.ts +76 -0
- package/types/src/extensions/Collaboration/ForkYDoc.d.ts +2 -11
- package/types/src/extensions/Collaboration/YCursorPlugin.d.ts +3 -11
- package/types/src/extensions/Collaboration/YSync.d.ts +2 -4
- package/types/src/extensions/Collaboration/YUndo.d.ts +1 -1
- package/types/src/index.d.ts +2 -1
- package/types/src/util/expandToWords.d.ts +13 -0
- package/dist/TrailingNode-CG2a-HDA.js.map +0 -1
- package/dist/TrailingNode-Du4SNHun.cjs +0 -2
- package/dist/TrailingNode-Du4SNHun.cjs.map +0 -1
- package/dist/defaultBlocks-B63ufZ5N.js.map +0 -1
- package/dist/defaultBlocks-BX6UxQa8.cjs +0 -6
- package/dist/defaultBlocks-BX6UxQa8.cjs.map +0 -1
|
@@ -203,6 +203,12 @@ export class ExtensionManager {
|
|
|
203
203
|
|
|
204
204
|
this.extensions.push(instance);
|
|
205
205
|
|
|
206
|
+
if (instance.blockNoteExtensions) {
|
|
207
|
+
for (const extension of instance.blockNoteExtensions) {
|
|
208
|
+
this.addExtension(extension);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
206
212
|
return instance as any;
|
|
207
213
|
}
|
|
208
214
|
|
|
@@ -317,8 +323,7 @@ export class ExtensionManager {
|
|
|
317
323
|
const tiptapExtensions = getDefaultTiptapExtensions(
|
|
318
324
|
this.editor,
|
|
319
325
|
this.options,
|
|
320
|
-
);
|
|
321
|
-
// TODO filter out the default extensions via the disabledExtensions set?
|
|
326
|
+
).filter((extension) => !this.disabledExtensions.has(extension.name));
|
|
322
327
|
|
|
323
328
|
const getPriority = sortByDependencies(this.extensions);
|
|
324
329
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNodeSelection, posToDOMRect } from "@tiptap/core";
|
|
1
2
|
import {
|
|
2
3
|
getSelection,
|
|
3
4
|
getSelectionCutBlocks,
|
|
@@ -7,21 +8,20 @@ import {
|
|
|
7
8
|
getTextCursorPosition,
|
|
8
9
|
setTextCursorPosition,
|
|
9
10
|
} from "../../api/blockManipulation/selections/textCursorPosition.js";
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
DefaultBlockSchema,
|
|
13
|
+
DefaultInlineContentSchema,
|
|
14
|
+
DefaultStyleSchema,
|
|
15
|
+
} from "../../blocks/defaultBlocks.js";
|
|
11
16
|
import {
|
|
12
17
|
BlockIdentifier,
|
|
13
18
|
BlockSchema,
|
|
14
19
|
InlineContentSchema,
|
|
15
20
|
StyleSchema,
|
|
16
21
|
} from "../../schema/index.js";
|
|
17
|
-
import {
|
|
18
|
-
DefaultBlockSchema,
|
|
19
|
-
DefaultInlineContentSchema,
|
|
20
|
-
DefaultStyleSchema,
|
|
21
|
-
} from "../../blocks/defaultBlocks.js";
|
|
22
|
-
import { Selection } from "../selectionTypes.js";
|
|
23
|
-
import { TextCursorPosition } from "../cursorPositionTypes.js";
|
|
24
22
|
import { BlockNoteEditor } from "../BlockNoteEditor.js";
|
|
23
|
+
import { TextCursorPosition } from "../cursorPositionTypes.js";
|
|
24
|
+
import { Selection } from "../selectionTypes.js";
|
|
25
25
|
|
|
26
26
|
export class SelectionManager<
|
|
27
27
|
BSchema extends BlockSchema = DefaultBlockSchema,
|
|
@@ -47,8 +47,8 @@ export class SelectionManager<
|
|
|
47
47
|
* If the selection starts / ends halfway through a block, the returned block will be
|
|
48
48
|
* only the part of the block that is included in the selection.
|
|
49
49
|
*/
|
|
50
|
-
public getSelectionCutBlocks() {
|
|
51
|
-
return this.editor.transact((tr) => getSelectionCutBlocks(tr));
|
|
50
|
+
public getSelectionCutBlocks(expandToWords = false) {
|
|
51
|
+
return this.editor.transact((tr) => getSelectionCutBlocks(tr, expandToWords));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -20,7 +20,7 @@ export const BlockChangeExtension = createExtension(() => {
|
|
|
20
20
|
key: new PluginKey("blockChange"),
|
|
21
21
|
filterTransaction: (tr) => {
|
|
22
22
|
let changes:
|
|
23
|
-
| ReturnType<typeof getBlocksChangedByTransaction
|
|
23
|
+
| ReturnType<typeof getBlocksChangedByTransaction<any, any, any>>
|
|
24
24
|
| undefined = undefined;
|
|
25
25
|
|
|
26
26
|
return beforeChangeCallbacks.reduce((acc, cb) => {
|
|
@@ -34,7 +34,7 @@ export const BlockChangeExtension = createExtension(() => {
|
|
|
34
34
|
if (changes) {
|
|
35
35
|
return changes;
|
|
36
36
|
}
|
|
37
|
-
changes = getBlocksChangedByTransaction(tr);
|
|
37
|
+
changes = getBlocksChangedByTransaction<any, any, any>(tr);
|
|
38
38
|
return changes;
|
|
39
39
|
},
|
|
40
40
|
tr,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as Y from "yjs";
|
|
2
|
+
import type { Awareness } from "y-protocols/awareness";
|
|
3
|
+
import {
|
|
4
|
+
createExtension,
|
|
5
|
+
ExtensionOptions,
|
|
6
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
7
|
+
import { ForkYDocExtension } from "./ForkYDoc.js";
|
|
8
|
+
import { SchemaMigration } from "./schemaMigration/SchemaMigration.js";
|
|
9
|
+
import { YCursorExtension } from "./YCursorPlugin.js";
|
|
10
|
+
import { YSyncExtension } from "./YSync.js";
|
|
11
|
+
import { YUndoExtension } from "./YUndo.js";
|
|
12
|
+
|
|
13
|
+
export type CollaborationOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* The Yjs XML fragment that's used for collaboration.
|
|
16
|
+
*/
|
|
17
|
+
fragment: Y.XmlFragment;
|
|
18
|
+
/**
|
|
19
|
+
* The user info for the current user that's shown to other collaborators.
|
|
20
|
+
*/
|
|
21
|
+
user: {
|
|
22
|
+
name: string;
|
|
23
|
+
color: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A Yjs provider (used for awareness / cursor information)
|
|
27
|
+
*/
|
|
28
|
+
provider?: { awareness?: Awareness };
|
|
29
|
+
/**
|
|
30
|
+
* Optional function to customize how cursors of users are rendered
|
|
31
|
+
*/
|
|
32
|
+
renderCursor?: (user: any) => HTMLElement;
|
|
33
|
+
/**
|
|
34
|
+
* Optional flag to set when the user label should be shown with the default
|
|
35
|
+
* collaboration cursor. Setting to "always" will always show the label,
|
|
36
|
+
* while "activity" will only show the label when the user moves the cursor
|
|
37
|
+
* or types. Defaults to "activity".
|
|
38
|
+
*/
|
|
39
|
+
showCursorLabels?: "always" | "activity";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const CollaborationExtension = createExtension(
|
|
43
|
+
({ options }: ExtensionOptions<CollaborationOptions>) => {
|
|
44
|
+
return {
|
|
45
|
+
key: "collaboration",
|
|
46
|
+
blockNoteExtensions: [
|
|
47
|
+
ForkYDocExtension(options),
|
|
48
|
+
YCursorExtension(options),
|
|
49
|
+
YSyncExtension(options),
|
|
50
|
+
YUndoExtension(),
|
|
51
|
+
SchemaMigration(options),
|
|
52
|
+
],
|
|
53
|
+
} as const;
|
|
54
|
+
},
|
|
55
|
+
);
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
createStore,
|
|
6
6
|
ExtensionOptions,
|
|
7
7
|
} from "../../editor/BlockNoteExtension.js";
|
|
8
|
+
import { CollaborationOptions } from "./Collaboration.js";
|
|
8
9
|
import { YCursorExtension } from "./YCursorPlugin.js";
|
|
9
10
|
import { YSyncExtension } from "./YSync.js";
|
|
10
11
|
import { YUndoExtension } from "./YUndo.js";
|
|
11
|
-
import { BlockNoteEditorOptions } from "../../editor/BlockNoteEditor.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* To find a fragment in another ydoc, we need to search for it.
|
|
@@ -44,12 +44,7 @@ function findTypeInOtherYdoc<T extends Y.AbstractType<any>>(
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export const ForkYDocExtension = createExtension(
|
|
47
|
-
({
|
|
48
|
-
editor,
|
|
49
|
-
options,
|
|
50
|
-
}: ExtensionOptions<
|
|
51
|
-
NonNullable<BlockNoteEditorOptions<any, any, any>["collaboration"]>
|
|
52
|
-
>) => {
|
|
47
|
+
({ editor, options }: ExtensionOptions<CollaborationOptions>) => {
|
|
53
48
|
let forkedState:
|
|
54
49
|
| {
|
|
55
50
|
originalFragment: Y.XmlFragment;
|
|
@@ -107,7 +102,7 @@ export const ForkYDocExtension = createExtension(
|
|
|
107
102
|
editor.registerExtension([
|
|
108
103
|
YSyncExtension(newOptions),
|
|
109
104
|
// No need to register the cursor plugin again, it's a local fork
|
|
110
|
-
YUndoExtension(
|
|
105
|
+
YUndoExtension(),
|
|
111
106
|
]);
|
|
112
107
|
|
|
113
108
|
// Tell the store that the editor is now forked
|
|
@@ -131,7 +126,7 @@ export const ForkYDocExtension = createExtension(
|
|
|
131
126
|
editor.registerExtension([
|
|
132
127
|
YSyncExtension(options),
|
|
133
128
|
YCursorExtension(options),
|
|
134
|
-
YUndoExtension(
|
|
129
|
+
YUndoExtension(),
|
|
135
130
|
]);
|
|
136
131
|
|
|
137
132
|
// Reset the undo stack to the original undo stack
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createExtension,
|
|
4
4
|
ExtensionOptions,
|
|
5
5
|
} from "../../editor/BlockNoteExtension.js";
|
|
6
|
-
import {
|
|
6
|
+
import { CollaborationOptions } from "./Collaboration.js";
|
|
7
7
|
|
|
8
8
|
export type CollaborationUser = {
|
|
9
9
|
name: string;
|
|
@@ -67,30 +67,24 @@ function defaultCursorRender(user: CollaborationUser) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export const YCursorExtension = createExtension(
|
|
70
|
-
({
|
|
71
|
-
options,
|
|
72
|
-
}: ExtensionOptions<
|
|
73
|
-
NonNullable<BlockNoteEditorOptions<any, any, any>["collaboration"]>
|
|
74
|
-
>) => {
|
|
70
|
+
({ options }: ExtensionOptions<CollaborationOptions>) => {
|
|
75
71
|
const recentlyUpdatedCursors = new Map();
|
|
76
|
-
|
|
77
|
-
if (
|
|
72
|
+
const awareness =
|
|
78
73
|
options.provider &&
|
|
79
74
|
"awareness" in options.provider &&
|
|
80
75
|
typeof options.provider.awareness === "object"
|
|
81
|
-
|
|
76
|
+
? options.provider.awareness
|
|
77
|
+
: undefined;
|
|
78
|
+
if (awareness) {
|
|
82
79
|
if (
|
|
83
|
-
"setLocalStateField" in
|
|
84
|
-
typeof
|
|
80
|
+
"setLocalStateField" in awareness &&
|
|
81
|
+
typeof awareness.setLocalStateField === "function"
|
|
85
82
|
) {
|
|
86
|
-
|
|
83
|
+
awareness.setLocalStateField("user", options.user);
|
|
87
84
|
}
|
|
88
|
-
if (
|
|
89
|
-
"on" in options.provider.awareness &&
|
|
90
|
-
typeof options.provider.awareness.on === "function"
|
|
91
|
-
) {
|
|
85
|
+
if ("on" in awareness && typeof awareness.on === "function") {
|
|
92
86
|
if (options.showCursorLabels !== "always") {
|
|
93
|
-
|
|
87
|
+
awareness.on(
|
|
94
88
|
"change",
|
|
95
89
|
({
|
|
96
90
|
updated,
|
|
@@ -126,57 +120,59 @@ export const YCursorExtension = createExtension(
|
|
|
126
120
|
return {
|
|
127
121
|
key: "yCursor",
|
|
128
122
|
prosemirrorPlugins: [
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
123
|
+
awareness
|
|
124
|
+
? yCursorPlugin(awareness, {
|
|
125
|
+
selectionBuilder: defaultSelectionBuilder,
|
|
126
|
+
cursorBuilder(user: CollaborationUser, clientID: number) {
|
|
127
|
+
let cursorData = recentlyUpdatedCursors.get(clientID);
|
|
128
|
+
|
|
129
|
+
if (!cursorData) {
|
|
130
|
+
const cursorElement = (
|
|
131
|
+
options.renderCursor ?? defaultCursorRender
|
|
132
|
+
)(user);
|
|
133
|
+
|
|
134
|
+
if (options.showCursorLabels !== "always") {
|
|
135
|
+
cursorElement.addEventListener("mouseenter", () => {
|
|
136
|
+
const cursor = recentlyUpdatedCursors.get(clientID)!;
|
|
137
|
+
cursor.element.setAttribute("data-active", "");
|
|
138
|
+
|
|
139
|
+
if (cursor.hideTimeout) {
|
|
140
|
+
clearTimeout(cursor.hideTimeout);
|
|
141
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
142
|
+
element: cursor.element,
|
|
143
|
+
hideTimeout: undefined,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
149
146
|
});
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
147
|
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
cursorElement.addEventListener("mouseleave", () => {
|
|
149
|
+
const cursor = recentlyUpdatedCursors.get(clientID)!;
|
|
155
150
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
152
|
+
element: cursor.element,
|
|
153
|
+
hideTimeout: setTimeout(() => {
|
|
154
|
+
cursor.element.removeAttribute("data-active");
|
|
155
|
+
}, 2000),
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
164
159
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
cursorData = {
|
|
161
|
+
element: cursorElement,
|
|
162
|
+
hideTimeout: undefined,
|
|
163
|
+
};
|
|
169
164
|
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
recentlyUpdatedCursors.set(clientID, cursorData);
|
|
166
|
+
}
|
|
172
167
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
168
|
+
return cursorData.element;
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
: undefined,
|
|
172
|
+
].filter(Boolean),
|
|
177
173
|
dependsOn: ["ySync"],
|
|
178
174
|
updateUser(user: { name: string; color: string; [key: string]: string }) {
|
|
179
|
-
|
|
175
|
+
awareness?.setLocalStateField("user", user);
|
|
180
176
|
},
|
|
181
177
|
} as const;
|
|
182
178
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ySyncPlugin } from "y-prosemirror";
|
|
2
|
-
import { XmlFragment } from "yjs";
|
|
3
2
|
import {
|
|
4
3
|
ExtensionOptions,
|
|
5
4
|
createExtension,
|
|
6
5
|
} from "../../editor/BlockNoteExtension.js";
|
|
6
|
+
import { CollaborationOptions } from "./Collaboration.js";
|
|
7
7
|
|
|
8
8
|
export const YSyncExtension = createExtension(
|
|
9
|
-
({ options }: ExtensionOptions<
|
|
9
|
+
({ options }: ExtensionOptions<Pick<CollaborationOptions, "fragment">>) => {
|
|
10
10
|
return {
|
|
11
11
|
key: "ySync",
|
|
12
12
|
prosemirrorPlugins: [ySyncPlugin(options.fragment)],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { redoCommand, undoCommand, yUndoPlugin } from "y-prosemirror";
|
|
2
2
|
import { createExtension } from "../../editor/BlockNoteExtension.js";
|
|
3
3
|
|
|
4
|
-
export const YUndoExtension = createExtension((
|
|
4
|
+
export const YUndoExtension = createExtension(() => {
|
|
5
5
|
return {
|
|
6
6
|
key: "yUndo",
|
|
7
|
-
prosemirrorPlugins: [yUndoPlugin(
|
|
7
|
+
prosemirrorPlugins: [yUndoPlugin()],
|
|
8
8
|
dependsOn: ["yCursor", "ySync"],
|
|
9
9
|
undoCommand: undoCommand,
|
|
10
10
|
redoCommand: redoCommand,
|
|
@@ -89,41 +89,24 @@ export function getDefaultSlashMenuItems<
|
|
|
89
89
|
const items: DefaultSuggestionItem[] = [];
|
|
90
90
|
|
|
91
91
|
if (editorHasBlockWithType(editor, "heading", { level: "number" })) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
badge: formatKeyboardShortcut("Mod-Alt-2"),
|
|
112
|
-
key: "heading_2",
|
|
113
|
-
...editor.dictionary.slash_menu.heading_2,
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
onItemClick: () => {
|
|
117
|
-
insertOrUpdateBlockForSlashMenu(editor, {
|
|
118
|
-
type: "heading",
|
|
119
|
-
props: { level: 3 },
|
|
120
|
-
});
|
|
121
|
-
},
|
|
122
|
-
badge: formatKeyboardShortcut("Mod-Alt-3"),
|
|
123
|
-
key: "heading_3",
|
|
124
|
-
...editor.dictionary.slash_menu.heading_3,
|
|
125
|
-
},
|
|
126
|
-
);
|
|
92
|
+
(editor.schema.blockSchema.heading.propSchema.level.values || [])
|
|
93
|
+
.filter((level): level is 1 | 2 | 3 => level <= 3)
|
|
94
|
+
.forEach((level) => {
|
|
95
|
+
items.push({
|
|
96
|
+
onItemClick: () => {
|
|
97
|
+
insertOrUpdateBlockForSlashMenu(editor, {
|
|
98
|
+
type: "heading",
|
|
99
|
+
props: { level: level },
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
badge: formatKeyboardShortcut(`Mod-Alt-${level}`),
|
|
103
|
+
key:
|
|
104
|
+
level === 1 ? ("heading" as const) : (`heading_${level}` as const),
|
|
105
|
+
...editor.dictionary.slash_menu[
|
|
106
|
+
level === 1 ? ("heading" as const) : (`heading_${level}` as const)
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
});
|
|
127
110
|
}
|
|
128
111
|
|
|
129
112
|
if (editorHasBlockWithType(editor, "quote")) {
|
|
@@ -316,39 +299,27 @@ export function getDefaultSlashMenuItems<
|
|
|
316
299
|
isToggleable: "boolean",
|
|
317
300
|
})
|
|
318
301
|
) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
onItemClick: () => {
|
|
343
|
-
insertOrUpdateBlockForSlashMenu(editor, {
|
|
344
|
-
type: "heading",
|
|
345
|
-
props: { level: 3, isToggleable: true },
|
|
346
|
-
});
|
|
347
|
-
},
|
|
348
|
-
key: "toggle_heading_3",
|
|
349
|
-
...editor.dictionary.slash_menu.toggle_heading_3,
|
|
350
|
-
},
|
|
351
|
-
);
|
|
302
|
+
(editor.schema.blockSchema.heading.propSchema.level.values || [])
|
|
303
|
+
.filter((level): level is 1 | 2 | 3 => level <= 3)
|
|
304
|
+
.forEach((level) => {
|
|
305
|
+
items.push({
|
|
306
|
+
onItemClick: () => {
|
|
307
|
+
insertOrUpdateBlockForSlashMenu(editor, {
|
|
308
|
+
type: "heading",
|
|
309
|
+
props: { level: level, isToggleable: true },
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
key:
|
|
313
|
+
level === 1
|
|
314
|
+
? ("toggle_heading" as const)
|
|
315
|
+
: (`toggle_heading_${level}` as const),
|
|
316
|
+
...editor.dictionary.slash_menu[
|
|
317
|
+
level === 1
|
|
318
|
+
? ("toggle_heading" as const)
|
|
319
|
+
: (`toggle_heading_${level}` as const)
|
|
320
|
+
],
|
|
321
|
+
});
|
|
322
|
+
});
|
|
352
323
|
}
|
|
353
324
|
|
|
354
325
|
if (editorHasBlockWithType(editor, "heading", { level: "number" })) {
|
|
@@ -362,6 +333,7 @@ export function getDefaultSlashMenuItems<
|
|
|
362
333
|
props: { level: level },
|
|
363
334
|
});
|
|
364
335
|
},
|
|
336
|
+
badge: formatKeyboardShortcut(`Mod-Alt-${level}`),
|
|
365
337
|
key: `heading_${level}`,
|
|
366
338
|
...editor.dictionary.slash_menu[`heading_${level}`],
|
|
367
339
|
});
|
|
@@ -621,12 +621,16 @@ export const TableHandlesExtension = createExtension(({ editor }) => {
|
|
|
621
621
|
key: tableHandlesPluginKey,
|
|
622
622
|
view: (editorView) => {
|
|
623
623
|
view = new TableHandlesView(editor as any, editorView, (state) => {
|
|
624
|
-
store.setState(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
624
|
+
store.setState(
|
|
625
|
+
state.block
|
|
626
|
+
? {
|
|
627
|
+
...state,
|
|
628
|
+
draggingState: state.draggingState
|
|
629
|
+
? { ...state.draggingState }
|
|
630
|
+
: undefined,
|
|
631
|
+
}
|
|
628
632
|
: undefined,
|
|
629
|
-
|
|
633
|
+
);
|
|
630
634
|
});
|
|
631
635
|
return view;
|
|
632
636
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./api/blockManipulation/commands/insertBlocks/insertBlocks.js";
|
|
2
2
|
export * from "./api/blockManipulation/commands/replaceBlocks/replaceBlocks.js";
|
|
3
|
-
export * from "./api/blockManipulation/commands/updateBlock/updateBlock.js";
|
|
4
3
|
export * from "./api/blockManipulation/commands/replaceBlocks/util/fixColumnList.js";
|
|
4
|
+
export * from "./api/blockManipulation/commands/updateBlock/updateBlock.js";
|
|
5
5
|
export * from "./api/exporters/html/externalHTMLExporter.js";
|
|
6
6
|
export * from "./api/exporters/html/internalHTMLSerializer.js";
|
|
7
7
|
export * from "./api/getBlockInfoFromPos.js";
|
|
@@ -19,6 +19,7 @@ export * from "./i18n/dictionary.js";
|
|
|
19
19
|
export * from "./schema/index.js";
|
|
20
20
|
export * from "./util/browser.js";
|
|
21
21
|
export * from "./util/combineByGroup.js";
|
|
22
|
+
export * from "./util/expandToWords.js";
|
|
22
23
|
export * from "./util/string.js";
|
|
23
24
|
export * from "./util/table.js";
|
|
24
25
|
export * from "./util/typescript.js";
|
|
@@ -70,6 +70,8 @@ export function getParseRules<
|
|
|
70
70
|
|
|
71
71
|
return props;
|
|
72
72
|
},
|
|
73
|
+
// Because we do the parsing ourselves, we want to preserve whitespace for content we've parsed
|
|
74
|
+
preserveWhitespace: true,
|
|
73
75
|
getContent:
|
|
74
76
|
config.content === "inline" || config.content === "none"
|
|
75
77
|
? (node, schema) => {
|
|
@@ -97,6 +99,7 @@ export function getParseRules<
|
|
|
97
99
|
const parser = DOMParser.fromSchema(schema);
|
|
98
100
|
const parsed = parser.parse(clone, {
|
|
99
101
|
topNode: schema.nodes.paragraph.create(),
|
|
102
|
+
preserveWhitespace: true,
|
|
100
103
|
});
|
|
101
104
|
|
|
102
105
|
return parsed.content;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Node, ResolvedPos } from "prosemirror-model";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Expands a range (start to end) to include the rest of the word if it starts or ends within a word
|
|
5
|
+
*/
|
|
6
|
+
export function expandPMRangeToWords(
|
|
7
|
+
doc: Node,
|
|
8
|
+
range: { $from: ResolvedPos; $to: ResolvedPos },
|
|
9
|
+
) {
|
|
10
|
+
let { $from, $to } = range;
|
|
11
|
+
|
|
12
|
+
// Expand Start
|
|
13
|
+
// If the selection starts with a word character or punctuation, check if we need to expand left to include the rest of the word
|
|
14
|
+
if ($from.pos > $from.start() && $from.pos < doc.content.size) {
|
|
15
|
+
const charAfterStart = doc.textBetween($from.pos, $from.pos + 1);
|
|
16
|
+
if (/^[\w\p{P}]$/u.test(charAfterStart)) {
|
|
17
|
+
const textBefore = doc.textBetween($from.start(), $from.pos);
|
|
18
|
+
const wordMatch = textBefore.match(/[\w\p{P}]+$/u);
|
|
19
|
+
if (wordMatch) {
|
|
20
|
+
$from = doc.resolve($from.pos - wordMatch[0].length);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Expand End
|
|
26
|
+
// If the selection ends with a word characte or punctuation, check if we need to expand right to include the rest of the word
|
|
27
|
+
if ($to.pos < $to.end() && $to.pos > 0) {
|
|
28
|
+
const charBeforeEnd = doc.textBetween($to.pos - 1, $to.pos);
|
|
29
|
+
if (/^[\w\p{P}]$/u.test(charBeforeEnd)) {
|
|
30
|
+
const textAfter = doc.textBetween($to.pos, $to.end());
|
|
31
|
+
const wordMatch = textAfter.match(/^[\w\p{P}]+/u);
|
|
32
|
+
if (wordMatch) {
|
|
33
|
+
$to = doc.resolve($to.pos + wordMatch[0].length);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { $from, $to, from: $from.pos, to: $to.pos };
|
|
38
|
+
}
|
|
@@ -4,7 +4,7 @@ import { Selection } from "../../../editor/selectionTypes.js";
|
|
|
4
4
|
import { BlockIdentifier, BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema/index.js";
|
|
5
5
|
export declare function getSelection<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(tr: Transaction): Selection<BSchema, I, S> | undefined;
|
|
6
6
|
export declare function setSelection(tr: Transaction, startBlock: BlockIdentifier, endBlock: BlockIdentifier): void;
|
|
7
|
-
export declare function getSelectionCutBlocks(tr: Transaction): {
|
|
7
|
+
export declare function getSelectionCutBlocks(tr: Transaction, expandToWords?: boolean): {
|
|
8
8
|
blocks: Block<Record<string, import("../../../index.js").BlockConfig<string, import("../../../index.js").PropSchema, "inline" | "none" | "table">>, InlineContentSchema, StyleSchema>[];
|
|
9
9
|
blockCutAtStart: string | undefined;
|
|
10
10
|
blockCutAtEnd: string | undefined;
|