@cyberyozh/text-editor 0.2.2 → 0.3.1
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/editor.js +51 -18
- package/dist/editor.js.map +1 -1
- package/dist/editor.mjs +54 -21
- package/dist/editor.mjs.map +1 -1
- package/package.json +1 -1
- package/src/editor/editor.tsx +1 -3
- package/src/editor/handlers/insertion-handlers.ts +21 -18
- package/src/editor/handlers/markup-insertion.ts +31 -0
package/package.json
CHANGED
package/src/editor/editor.tsx
CHANGED
|
@@ -107,7 +107,7 @@ function TiptapContent({
|
|
|
107
107
|
onPaste,
|
|
108
108
|
onDrop,
|
|
109
109
|
onMarkupInsertion,
|
|
110
|
-
} = useInsertionHandlers();
|
|
110
|
+
} = useInsertionHandlers(imageHandlers);
|
|
111
111
|
|
|
112
112
|
const editor = useEditor(
|
|
113
113
|
{
|
|
@@ -143,7 +143,6 @@ function TiptapContent({
|
|
|
143
143
|
event,
|
|
144
144
|
slice,
|
|
145
145
|
setIsDisabledInternal,
|
|
146
|
-
imageHandlers?.uploadMultipleByURL
|
|
147
146
|
);
|
|
148
147
|
|
|
149
148
|
if (typeof toReturn === 'boolean') return toReturn;
|
|
@@ -156,7 +155,6 @@ function TiptapContent({
|
|
|
156
155
|
event,
|
|
157
156
|
slice,
|
|
158
157
|
setIsDisabledInternal,
|
|
159
|
-
imageHandlers?.uploadMultipleByURL
|
|
160
158
|
);
|
|
161
159
|
|
|
162
160
|
if (typeof toReturn === 'boolean') return toReturn;
|
|
@@ -2,10 +2,10 @@ import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
|
|
2
2
|
import { useDialogCommand } from "../context";
|
|
3
3
|
import { EditorView } from "@tiptap/pm/view";
|
|
4
4
|
import { Slice } from "@tiptap/pm/model";
|
|
5
|
-
import {
|
|
6
|
-
import { applyUploadResultsToNodes, collectRehostableImages } from "./markup-insertion";
|
|
5
|
+
import { ImageHandlers } from "@/types";
|
|
6
|
+
import { applyUploadResultsToNodes, collectRehostableImages, removeImagesFromSlice } from "./markup-insertion";
|
|
7
7
|
|
|
8
|
-
export function useInsertionHandlers() {
|
|
8
|
+
export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
9
9
|
const [insertedImage, setInsertedImage] = useState<File | null>(null);
|
|
10
10
|
|
|
11
11
|
const { activeDialog, openDialogCommand } = useDialogCommand();
|
|
@@ -22,6 +22,8 @@ export function useInsertionHandlers() {
|
|
|
22
22
|
event.preventDefault();
|
|
23
23
|
event.stopPropagation();
|
|
24
24
|
|
|
25
|
+
if (!imageHandlers?.upload) return;
|
|
26
|
+
|
|
25
27
|
setInsertedImage(file);
|
|
26
28
|
openDialogCommand("image");
|
|
27
29
|
}
|
|
@@ -41,10 +43,7 @@ export function useInsertionHandlers() {
|
|
|
41
43
|
event: ClipboardEvent | DragEvent,
|
|
42
44
|
slice: Slice,
|
|
43
45
|
setDisabled: Dispatch<SetStateAction<boolean>>,
|
|
44
|
-
uploadMultipleByURL?: ImageUploadMultipleByURLHandler
|
|
45
46
|
): boolean | void {
|
|
46
|
-
if (!uploadMultipleByURL) return;
|
|
47
|
-
|
|
48
47
|
let hasMarkupInsertion = false;
|
|
49
48
|
|
|
50
49
|
if (
|
|
@@ -66,18 +65,22 @@ export function useInsertionHandlers() {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
if (hasMarkupInsertion) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
if (imageHandlers?.uploadMultipleByURL) {
|
|
69
|
+
setDisabled(true);
|
|
70
|
+
|
|
71
|
+
const imagesToNodes = collectRehostableImages(slice);
|
|
72
|
+
|
|
73
|
+
imageHandlers.uploadMultipleByURL(imagesToNodes.map(([url]) => url))
|
|
74
|
+
.then((results) => {
|
|
75
|
+
applyUploadResultsToNodes(imagesToNodes, results);
|
|
76
|
+
view.dispatch(view.state.tr.replaceSelection(slice));
|
|
77
|
+
})
|
|
78
|
+
.finally(() => {
|
|
79
|
+
setDisabled(false);
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
view.dispatch(view.state.tr.replaceSelection(removeImagesFromSlice(slice)));
|
|
83
|
+
}
|
|
81
84
|
|
|
82
85
|
return true;
|
|
83
86
|
}
|
|
@@ -19,6 +19,37 @@ export function collectRehostableImages(slice: Slice | Fragment | Node): ImageTo
|
|
|
19
19
|
return imagesToNodes;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function removeImagesFromFragment(fragment: Fragment): Fragment {
|
|
23
|
+
const newNodes: Node[] = [];
|
|
24
|
+
fragment.forEach((node) => {
|
|
25
|
+
if (node.type.name === "image" && !node.attrs.fileId) return;
|
|
26
|
+
newNodes.push(node.childCount > 0 ? node.copy(removeImagesFromFragment(node.content)) : node);
|
|
27
|
+
});
|
|
28
|
+
return Fragment.from(newNodes);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validOpenDepth(fragment: Fragment, requestedDepth: number, fromEnd = false): number {
|
|
32
|
+
let depth = 0;
|
|
33
|
+
let current = fragment;
|
|
34
|
+
while (depth < requestedDepth) {
|
|
35
|
+
if (current.childCount === 0) break;
|
|
36
|
+
const node = fromEnd ? current.lastChild! : current.firstChild!;
|
|
37
|
+
if (node.isLeaf) break;
|
|
38
|
+
current = node.content;
|
|
39
|
+
depth++;
|
|
40
|
+
}
|
|
41
|
+
return depth;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function removeImagesFromSlice(slice: Slice): Slice {
|
|
45
|
+
const newContent = removeImagesFromFragment(slice.content);
|
|
46
|
+
return new Slice(
|
|
47
|
+
newContent,
|
|
48
|
+
validOpenDepth(newContent, slice.openStart),
|
|
49
|
+
validOpenDepth(newContent, slice.openEnd, true),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
22
53
|
export function applyUploadResultsToNodes(
|
|
23
54
|
imagesToNodes: ImageToNode[],
|
|
24
55
|
results: (ImageUploadResult | null)[],
|