dn-react-text-editor 0.1.2 → 0.2.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/README.md +19 -0
- package/dist/attach_file.d.mts +20 -22
- package/dist/attach_file.d.ts +20 -22
- package/dist/attach_file.js +5 -5
- package/dist/attach_file.mjs +5 -4
- package/dist/base64_file_uploader.d.mts +6 -0
- package/dist/base64_file_uploader.d.ts +6 -0
- package/dist/{plugins/trailing_paragraph.js → base64_file_uploader.js} +19 -22
- package/dist/base64_file_uploader.mjs +18 -0
- package/dist/commands.d.mts +23 -0
- package/dist/commands.d.ts +23 -0
- package/dist/commands.js +110 -0
- package/dist/commands.mjs +75 -0
- package/dist/create_text_editor.d.mts +28 -0
- package/dist/create_text_editor.d.ts +28 -0
- package/dist/create_text_editor.js +1082 -0
- package/dist/create_text_editor.mjs +1053 -0
- package/dist/html.d.mts +8 -0
- package/dist/html.d.ts +8 -0
- package/dist/html.js +136 -0
- package/dist/html.mjs +98 -0
- package/dist/index.d.mts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +770 -364
- package/dist/index.mjs +765 -359
- package/dist/input.d.mts +21 -0
- package/dist/input.d.ts +21 -0
- package/dist/input.js +70 -0
- package/dist/input.mjs +37 -0
- package/dist/plugins/drag_and_drop.d.mts +1 -1
- package/dist/plugins/drag_and_drop.d.ts +1 -1
- package/dist/plugins/drag_and_drop.js +3 -0
- package/dist/plugins/drag_and_drop.mjs +3 -0
- package/dist/plugins/highlighter.d.mts +6 -0
- package/dist/plugins/highlighter.d.ts +6 -0
- package/dist/plugins/highlighter.js +105 -0
- package/dist/plugins/highlighter.mjs +69 -0
- package/dist/plugins/keymap.js +17 -0
- package/dist/plugins/keymap.mjs +17 -0
- package/dist/schema.d.mts +2 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +255 -14
- package/dist/schema.mjs +245 -14
- package/dist/text_editor_controller.d.mts +46 -0
- package/dist/text_editor_controller.d.ts +46 -0
- package/dist/text_editor_controller.js +503 -0
- package/dist/text_editor_controller.mjs +470 -0
- package/package.json +3 -1
- package/dist/plugins/trailing_paragraph.d.mts +0 -5
- package/dist/plugins/trailing_paragraph.d.ts +0 -5
- package/dist/plugins/trailing_paragraph.mjs +0 -21
- package/dist/text_editor.d.mts +0 -37
- package/dist/text_editor.d.ts +0 -37
- package/dist/text_editor.js +0 -722
- package/dist/text_editor.mjs +0 -692
package/dist/index.mjs
CHANGED
|
@@ -1,179 +1,83 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
3
|
-
useImperativeHandle
|
|
4
|
-
useMemo
|
|
1
|
+
// src/create_text_editor.tsx
|
|
2
|
+
import React2, {
|
|
3
|
+
useImperativeHandle
|
|
5
4
|
} from "react";
|
|
6
|
-
import
|
|
7
|
-
EditorState as EditorState2
|
|
8
|
-
} from "prosemirror-state";
|
|
9
|
-
import { EditorView as EditorView3 } from "prosemirror-view";
|
|
10
|
-
import { useEffect, useRef } from "react";
|
|
11
|
-
import { baseKeymap } from "prosemirror-commands";
|
|
12
|
-
import { keymap } from "prosemirror-keymap";
|
|
13
|
-
|
|
14
|
-
// src/plugins/drag_and_drop.tsx
|
|
15
|
-
import { Plugin } from "prosemirror-state";
|
|
16
|
-
function dragAndDropPlugin({ attachFile }) {
|
|
17
|
-
return new Plugin({
|
|
18
|
-
props: {
|
|
19
|
-
handleDOMEvents: {
|
|
20
|
-
drop(view, event) {
|
|
21
|
-
const files = event.dataTransfer?.files;
|
|
22
|
-
if (!files || files.length === 0) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
event.preventDefault();
|
|
26
|
-
const pos = view.state.selection.$from.pos || view.posAtCoords({
|
|
27
|
-
left: event.clientX,
|
|
28
|
-
top: event.clientY
|
|
29
|
-
})?.pos || null;
|
|
30
|
-
if (pos === null) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const medias = Array.from(files).filter(
|
|
34
|
-
(file) => file.type.startsWith("image/") || file.type.startsWith("video/")
|
|
35
|
-
);
|
|
36
|
-
attachFile(view, medias);
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// src/plugins/upload_placeholder.tsx
|
|
45
|
-
import { Plugin as Plugin2 } from "prosemirror-state";
|
|
46
|
-
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
47
|
-
var uploadPlaceholderPlugin = new Plugin2({
|
|
48
|
-
state: {
|
|
49
|
-
init() {
|
|
50
|
-
return DecorationSet.empty;
|
|
51
|
-
},
|
|
52
|
-
apply(tr, set) {
|
|
53
|
-
set = set.map(tr.mapping, tr.doc);
|
|
54
|
-
const action = tr.getMeta(this);
|
|
55
|
-
if (action && action.add) {
|
|
56
|
-
const { type, width, height } = action.add;
|
|
57
|
-
const widget = document.createElement("div");
|
|
58
|
-
widget.className = "upload-placeholder";
|
|
59
|
-
widget.style.width = `100%`;
|
|
60
|
-
if (type.startsWith("image/") || type.startsWith("video/")) {
|
|
61
|
-
widget.style.aspectRatio = `${width} / ${height}`;
|
|
62
|
-
widget.style.maxWidth = `${width}px`;
|
|
63
|
-
} else {
|
|
64
|
-
widget.style.height = "80px";
|
|
65
|
-
}
|
|
66
|
-
const progress = document.createElement("div");
|
|
67
|
-
progress.className = "upload-progress";
|
|
68
|
-
widget.appendChild(progress);
|
|
69
|
-
const deco = Decoration.widget(action.add.pos, widget, {
|
|
70
|
-
id: action.add.id
|
|
71
|
-
});
|
|
72
|
-
set = set.add(tr.doc, [deco]);
|
|
73
|
-
} else if (action && action.progress) {
|
|
74
|
-
const found = set.find(
|
|
75
|
-
void 0,
|
|
76
|
-
void 0,
|
|
77
|
-
(spec) => spec.id === action.progress.id
|
|
78
|
-
);
|
|
79
|
-
if (found.length) {
|
|
80
|
-
const widget = found[0].type.toDOM;
|
|
81
|
-
const progress = widget.querySelector(".upload-progress");
|
|
82
|
-
if (progress) {
|
|
83
|
-
progress.innerHTML = `${Math.round(action.progress.progress)}%`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
} else if (action && action.remove) {
|
|
87
|
-
set = set.remove(
|
|
88
|
-
set.find(void 0, void 0, (spec) => spec.id === action.remove.id)
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
return set;
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
props: {
|
|
95
|
-
decorations(state) {
|
|
96
|
-
return this.getState(state);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
var findPlaceholder = (state, id) => {
|
|
101
|
-
const decos = uploadPlaceholderPlugin.getState(state);
|
|
102
|
-
if (!decos) {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
const found = decos.find(void 0, void 0, (spec) => spec.id === id);
|
|
106
|
-
return found.length ? found[0].from : null;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// src/plugins/placehoder.tsx
|
|
110
|
-
import "prosemirror-model";
|
|
111
|
-
import { Plugin as Plugin3 } from "prosemirror-state";
|
|
5
|
+
import "prosemirror-state";
|
|
112
6
|
import "prosemirror-view";
|
|
113
|
-
|
|
114
|
-
const nodes = [];
|
|
115
|
-
view.state.doc?.descendants((n) => {
|
|
116
|
-
nodes.push(n);
|
|
117
|
-
});
|
|
118
|
-
return nodes;
|
|
119
|
-
};
|
|
120
|
-
function placeholderPlugin(text) {
|
|
121
|
-
const update = (view) => {
|
|
122
|
-
const decos = uploadPlaceholderPlugin.getState(view.state);
|
|
123
|
-
if (decos && decos.find().length > 0 || view.state.doc.content.content.some((e) => e.type.name !== "paragraph") || view.state.doc.childCount > 1 || getFirstChildDescendants(view).length > 1 || view.state.doc.textContent) {
|
|
124
|
-
view.dom.removeAttribute("data-placeholder");
|
|
125
|
-
} else {
|
|
126
|
-
view.dom.setAttribute("data-placeholder", text);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
return new Plugin3({
|
|
130
|
-
view(view) {
|
|
131
|
-
update(view);
|
|
132
|
-
return { update };
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// src/text_editor.tsx
|
|
138
|
-
import { DOMSerializer, DOMParser } from "prosemirror-model";
|
|
139
|
-
import { history } from "prosemirror-history";
|
|
140
|
-
|
|
141
|
-
// src/plugins/keymap.tsx
|
|
142
|
-
import { undo, redo } from "prosemirror-history";
|
|
143
|
-
import { chainCommands, splitBlockAs } from "prosemirror-commands";
|
|
144
|
-
import { splitListItem } from "prosemirror-schema-list";
|
|
145
|
-
function buildKeymap(schema) {
|
|
146
|
-
const keys = {};
|
|
147
|
-
function bind(key, cmd) {
|
|
148
|
-
keys[key] = cmd;
|
|
149
|
-
}
|
|
150
|
-
bind("Mod-z", undo);
|
|
151
|
-
bind("Shift-Mod-z", redo);
|
|
152
|
-
bind("Mod-y", redo);
|
|
153
|
-
const li = schema.nodes.list_item;
|
|
154
|
-
bind(
|
|
155
|
-
"Enter",
|
|
156
|
-
chainCommands(splitListItem(li), (state, dispatch) => {
|
|
157
|
-
const { $head } = state.selection;
|
|
158
|
-
if ($head.parent.type === state.schema.nodes.paragraph) {
|
|
159
|
-
splitBlockAs((n) => {
|
|
160
|
-
return {
|
|
161
|
-
type: n.type,
|
|
162
|
-
attrs: n.attrs
|
|
163
|
-
};
|
|
164
|
-
})(state, dispatch);
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
return false;
|
|
168
|
-
})
|
|
169
|
-
);
|
|
170
|
-
return keys;
|
|
171
|
-
}
|
|
7
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
172
8
|
|
|
173
9
|
// src/schema.tsx
|
|
174
10
|
import { Schema } from "prosemirror-model";
|
|
175
11
|
import { addListNodes } from "prosemirror-schema-list";
|
|
176
|
-
|
|
12
|
+
|
|
13
|
+
// src/plugins/highlighter.ts
|
|
14
|
+
import hljs from "highlight.js";
|
|
15
|
+
import bash from "highlight.js/lib/languages/bash";
|
|
16
|
+
import c from "highlight.js/lib/languages/c";
|
|
17
|
+
import cpp from "highlight.js/lib/languages/cpp";
|
|
18
|
+
import css from "highlight.js/lib/languages/css";
|
|
19
|
+
import java from "highlight.js/lib/languages/java";
|
|
20
|
+
import ts from "highlight.js/lib/languages/typescript";
|
|
21
|
+
import js from "highlight.js/lib/languages/javascript";
|
|
22
|
+
import json from "highlight.js/lib/languages/json";
|
|
23
|
+
import xml from "highlight.js/lib/languages/xml";
|
|
24
|
+
import python from "highlight.js/lib/languages/python";
|
|
25
|
+
import dart from "highlight.js/lib/languages/dart";
|
|
26
|
+
import csharp from "highlight.js/lib/languages/csharp";
|
|
27
|
+
import markdown from "highlight.js/lib/languages/markdown";
|
|
28
|
+
import nginx from "highlight.js/lib/languages/nginx";
|
|
29
|
+
import php from "highlight.js/lib/languages/php";
|
|
30
|
+
import ruby from "highlight.js/lib/languages/ruby";
|
|
31
|
+
import sql from "highlight.js/lib/languages/sql";
|
|
32
|
+
import swift from "highlight.js/lib/languages/swift";
|
|
33
|
+
import yaml from "highlight.js/lib/languages/yaml";
|
|
34
|
+
import rust from "highlight.js/lib/languages/rust";
|
|
35
|
+
hljs.registerLanguage("bash", bash);
|
|
36
|
+
hljs.registerLanguage("c", c);
|
|
37
|
+
hljs.registerLanguage("cpp", cpp);
|
|
38
|
+
hljs.registerLanguage("css", css);
|
|
39
|
+
hljs.registerLanguage("java", java);
|
|
40
|
+
hljs.registerLanguage("markdown", markdown);
|
|
41
|
+
hljs.registerLanguage("nginx", nginx);
|
|
42
|
+
hljs.registerLanguage("php", php);
|
|
43
|
+
hljs.registerLanguage("ruby", ruby);
|
|
44
|
+
hljs.registerLanguage("sql", sql);
|
|
45
|
+
hljs.registerLanguage("swift", swift);
|
|
46
|
+
hljs.registerLanguage("yaml", yaml);
|
|
47
|
+
hljs.registerLanguage("rust", rust);
|
|
48
|
+
hljs.registerLanguage("json", json);
|
|
49
|
+
hljs.registerLanguage("javascript", js);
|
|
50
|
+
hljs.registerLanguage("typescript", ts);
|
|
51
|
+
hljs.registerLanguage("xml", xml);
|
|
52
|
+
hljs.registerLanguage("python", python);
|
|
53
|
+
hljs.registerLanguage("dart", dart);
|
|
54
|
+
hljs.registerLanguage("csharp", csharp);
|
|
55
|
+
var supportedLanguages = [
|
|
56
|
+
"bash",
|
|
57
|
+
"c",
|
|
58
|
+
"cpp",
|
|
59
|
+
"css",
|
|
60
|
+
"java",
|
|
61
|
+
"markdown",
|
|
62
|
+
"nginx",
|
|
63
|
+
"php",
|
|
64
|
+
"ruby",
|
|
65
|
+
"sql",
|
|
66
|
+
"swift",
|
|
67
|
+
"yaml",
|
|
68
|
+
"rust",
|
|
69
|
+
"json",
|
|
70
|
+
"javascript",
|
|
71
|
+
"typescript",
|
|
72
|
+
"xml",
|
|
73
|
+
"python",
|
|
74
|
+
"dart",
|
|
75
|
+
"csharp"
|
|
76
|
+
];
|
|
77
|
+
var highlighter = hljs;
|
|
78
|
+
|
|
79
|
+
// src/schema.tsx
|
|
80
|
+
function createSchema(spec = { nodes: {}, marks: {} }) {
|
|
177
81
|
const customSchema = new Schema({
|
|
178
82
|
nodes: {
|
|
179
83
|
doc: { content: "block+" },
|
|
@@ -339,67 +243,232 @@ function createSchema() {
|
|
|
339
243
|
}
|
|
340
244
|
];
|
|
341
245
|
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
246
|
+
},
|
|
247
|
+
video: {
|
|
248
|
+
inline: false,
|
|
249
|
+
group: "block",
|
|
250
|
+
draggable: true,
|
|
346
251
|
attrs: {
|
|
347
|
-
|
|
348
|
-
title: {
|
|
252
|
+
src: { validate: "string" },
|
|
253
|
+
title: {
|
|
254
|
+
default: null,
|
|
255
|
+
validate: "string|null"
|
|
256
|
+
},
|
|
257
|
+
width: {
|
|
258
|
+
default: null,
|
|
259
|
+
validate: "number|null"
|
|
260
|
+
},
|
|
261
|
+
height: {
|
|
262
|
+
default: null,
|
|
263
|
+
validate: "number|null"
|
|
264
|
+
},
|
|
265
|
+
poster: {
|
|
266
|
+
default: null,
|
|
267
|
+
validate: "string|null"
|
|
268
|
+
}
|
|
349
269
|
},
|
|
350
|
-
inclusive: false,
|
|
351
270
|
parseDOM: [
|
|
352
271
|
{
|
|
353
|
-
tag: "
|
|
272
|
+
tag: "video",
|
|
354
273
|
getAttrs(dom) {
|
|
355
274
|
return {
|
|
356
|
-
|
|
357
|
-
title: dom.getAttribute("title")
|
|
275
|
+
src: dom.getAttribute("src"),
|
|
276
|
+
title: dom.getAttribute("title"),
|
|
277
|
+
width: dom.getAttribute("width"),
|
|
278
|
+
height: dom.getAttribute("height"),
|
|
279
|
+
poster: dom.getAttribute("poster")
|
|
358
280
|
};
|
|
359
281
|
}
|
|
360
282
|
}
|
|
361
283
|
],
|
|
362
284
|
toDOM(node) {
|
|
363
|
-
const {
|
|
364
|
-
const target = "_blank";
|
|
365
|
-
const rel = "noopener noreferrer";
|
|
285
|
+
const { src, title, width, height, poster } = node.attrs;
|
|
366
286
|
return [
|
|
367
|
-
"
|
|
368
|
-
{
|
|
369
|
-
|
|
287
|
+
"video",
|
|
288
|
+
{
|
|
289
|
+
src,
|
|
290
|
+
title,
|
|
291
|
+
poster,
|
|
292
|
+
width,
|
|
293
|
+
height,
|
|
294
|
+
playsinline: "true",
|
|
295
|
+
controls: "controls",
|
|
296
|
+
style: `aspect-ratio: ${width} / ${height}`
|
|
297
|
+
}
|
|
370
298
|
];
|
|
371
299
|
}
|
|
372
300
|
},
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
301
|
+
iframe: {
|
|
302
|
+
group: "block",
|
|
303
|
+
draggable: true,
|
|
304
|
+
attrs: {
|
|
305
|
+
src: { validate: "string" },
|
|
306
|
+
title: {
|
|
307
|
+
default: null,
|
|
308
|
+
validate: "string|null"
|
|
379
309
|
},
|
|
380
|
-
{
|
|
381
|
-
|
|
382
|
-
|
|
310
|
+
width: {
|
|
311
|
+
default: null,
|
|
312
|
+
validate: "number|null"
|
|
383
313
|
},
|
|
384
|
-
{
|
|
385
|
-
|
|
386
|
-
|
|
314
|
+
height: {
|
|
315
|
+
default: null,
|
|
316
|
+
validate: "number|null"
|
|
317
|
+
},
|
|
318
|
+
allow: {
|
|
319
|
+
default: null,
|
|
320
|
+
validate: "string|null"
|
|
321
|
+
},
|
|
322
|
+
allowfullscreen: {
|
|
323
|
+
default: null,
|
|
324
|
+
validate: "string|null"
|
|
325
|
+
},
|
|
326
|
+
referrerPolicy: {
|
|
327
|
+
default: null,
|
|
328
|
+
validate: "string|null"
|
|
329
|
+
},
|
|
330
|
+
style: {
|
|
331
|
+
default: null,
|
|
332
|
+
validate: "string|null"
|
|
387
333
|
}
|
|
388
|
-
|
|
389
|
-
toDOM() {
|
|
390
|
-
return ["strong", 0];
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
italic: {
|
|
334
|
+
},
|
|
394
335
|
parseDOM: [
|
|
395
|
-
{ tag: "em" },
|
|
396
|
-
{ tag: "i" },
|
|
397
|
-
{ style: "font-style=italic" },
|
|
398
336
|
{
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
337
|
+
tag: "iframe[src]",
|
|
338
|
+
getAttrs(dom) {
|
|
339
|
+
return {
|
|
340
|
+
src: dom.getAttribute("src"),
|
|
341
|
+
title: dom.getAttribute("title"),
|
|
342
|
+
width: dom.getAttribute("width"),
|
|
343
|
+
height: dom.getAttribute("height"),
|
|
344
|
+
style: dom.getAttribute("style"),
|
|
345
|
+
allow: dom.getAttribute("allow"),
|
|
346
|
+
allowfullscreen: dom.getAttribute("allowfullscreen"),
|
|
347
|
+
referrerpolicy: dom.getAttribute("referrerpolicy")
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
],
|
|
352
|
+
toDOM(node) {
|
|
353
|
+
const {
|
|
354
|
+
src,
|
|
355
|
+
title,
|
|
356
|
+
width,
|
|
357
|
+
height,
|
|
358
|
+
allow,
|
|
359
|
+
allowfullscreen,
|
|
360
|
+
referrerpolicy,
|
|
361
|
+
style
|
|
362
|
+
} = node.attrs;
|
|
363
|
+
return [
|
|
364
|
+
"iframe",
|
|
365
|
+
{
|
|
366
|
+
src,
|
|
367
|
+
title,
|
|
368
|
+
width,
|
|
369
|
+
height,
|
|
370
|
+
style,
|
|
371
|
+
allow,
|
|
372
|
+
allowfullscreen,
|
|
373
|
+
referrerpolicy,
|
|
374
|
+
frameborder: "0"
|
|
375
|
+
}
|
|
376
|
+
];
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
blockquote: {
|
|
380
|
+
content: "block+",
|
|
381
|
+
group: "block",
|
|
382
|
+
defining: true,
|
|
383
|
+
parseDOM: [{ tag: "blockquote" }],
|
|
384
|
+
toDOM() {
|
|
385
|
+
return ["blockquote", 0];
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
code_block: {
|
|
389
|
+
content: "text*",
|
|
390
|
+
marks: "",
|
|
391
|
+
group: "block",
|
|
392
|
+
code: true,
|
|
393
|
+
defining: true,
|
|
394
|
+
parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
|
|
395
|
+
toDOM(node) {
|
|
396
|
+
const auto = highlighter.highlightAuto(
|
|
397
|
+
node.textContent,
|
|
398
|
+
supportedLanguages
|
|
399
|
+
);
|
|
400
|
+
return [
|
|
401
|
+
"pre",
|
|
402
|
+
{
|
|
403
|
+
class: "hljs"
|
|
404
|
+
},
|
|
405
|
+
[
|
|
406
|
+
"code",
|
|
407
|
+
{
|
|
408
|
+
class: `language-${auto.language}`
|
|
409
|
+
},
|
|
410
|
+
0
|
|
411
|
+
]
|
|
412
|
+
];
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
...spec.nodes
|
|
416
|
+
},
|
|
417
|
+
marks: {
|
|
418
|
+
link: {
|
|
419
|
+
attrs: {
|
|
420
|
+
href: { default: "" },
|
|
421
|
+
title: { default: null }
|
|
422
|
+
},
|
|
423
|
+
inclusive: false,
|
|
424
|
+
parseDOM: [
|
|
425
|
+
{
|
|
426
|
+
tag: "a[href]",
|
|
427
|
+
getAttrs(dom) {
|
|
428
|
+
return {
|
|
429
|
+
href: dom.getAttribute("href"),
|
|
430
|
+
title: dom.getAttribute("title")
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
],
|
|
435
|
+
toDOM(node) {
|
|
436
|
+
const { href, title } = node.attrs;
|
|
437
|
+
const target = "_blank";
|
|
438
|
+
const rel = "noopener noreferrer";
|
|
439
|
+
return ["a", { href, title: title || href, target, rel }, 0];
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
bold: {
|
|
443
|
+
parseDOM: [
|
|
444
|
+
{ tag: "strong" },
|
|
445
|
+
{
|
|
446
|
+
tag: "b",
|
|
447
|
+
getAttrs: (node) => node.style.fontWeight != "normal" && null
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
style: "font-weight=400",
|
|
451
|
+
clearMark: (m) => m.type.name == "strong"
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
style: "font-weight",
|
|
455
|
+
getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null
|
|
456
|
+
}
|
|
457
|
+
],
|
|
458
|
+
toDOM() {
|
|
459
|
+
return ["strong", 0];
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
italic: {
|
|
463
|
+
parseDOM: [
|
|
464
|
+
{ tag: "em" },
|
|
465
|
+
{ tag: "i" },
|
|
466
|
+
{ style: "font-style=italic" },
|
|
467
|
+
{
|
|
468
|
+
style: "font-style=normal",
|
|
469
|
+
clearMark: (m) => m.type.name == "em"
|
|
470
|
+
}
|
|
471
|
+
],
|
|
403
472
|
toDOM() {
|
|
404
473
|
return ["em", 0];
|
|
405
474
|
}
|
|
@@ -415,22 +484,314 @@ function createSchema() {
|
|
|
415
484
|
toDOM() {
|
|
416
485
|
return ["u", 0];
|
|
417
486
|
}
|
|
418
|
-
}
|
|
419
|
-
|
|
487
|
+
},
|
|
488
|
+
...spec.marks
|
|
489
|
+
},
|
|
490
|
+
topNode: spec.topNode
|
|
420
491
|
});
|
|
421
492
|
const prosemirrorSchema = new Schema({
|
|
422
|
-
nodes: addListNodes(
|
|
423
|
-
customSchema.spec.nodes,
|
|
424
|
-
"paragraph block*",
|
|
425
|
-
"block"
|
|
426
|
-
),
|
|
493
|
+
nodes: addListNodes(customSchema.spec.nodes, "paragraph block*", "block"),
|
|
427
494
|
marks: customSchema.spec.marks
|
|
428
495
|
});
|
|
429
496
|
return prosemirrorSchema;
|
|
430
497
|
}
|
|
431
498
|
|
|
432
|
-
// src/
|
|
433
|
-
import
|
|
499
|
+
// src/create_text_editor.tsx
|
|
500
|
+
import "rxjs";
|
|
501
|
+
|
|
502
|
+
// src/commands.tsx
|
|
503
|
+
import * as commands from "prosemirror-commands";
|
|
504
|
+
import * as schemaList from "prosemirror-schema-list";
|
|
505
|
+
var createCommands = (schema, view, options = {}) => {
|
|
506
|
+
{
|
|
507
|
+
const isBlockTypeActive = (node, attrs, excludes = []) => {
|
|
508
|
+
const state = view.state;
|
|
509
|
+
const ranges = state.selection.ranges;
|
|
510
|
+
let active = false;
|
|
511
|
+
for (const range of ranges) {
|
|
512
|
+
const { $from, $to } = range;
|
|
513
|
+
state.doc.nodesBetween($from.pos, $to.pos, (n) => {
|
|
514
|
+
if (active) {
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
if (n.type !== node || excludes.includes(n.type)) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
if (!attrs || Object.keys(attrs).every((key) => n.attrs[key] === attrs[key])) {
|
|
521
|
+
active = true;
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
return active;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
const setBlockType2 = (node, attrs) => {
|
|
528
|
+
view.focus();
|
|
529
|
+
const nodeType = schema.nodes[node];
|
|
530
|
+
const command = commands.setBlockType(nodeType, attrs);
|
|
531
|
+
command(view.state, view.dispatch);
|
|
532
|
+
};
|
|
533
|
+
const toggleBlockType = (node, attrs) => {
|
|
534
|
+
view.focus();
|
|
535
|
+
const nodeType = schema.nodes[node];
|
|
536
|
+
const command = commands.setBlockType(nodeType, attrs);
|
|
537
|
+
if (isBlockTypeActive(nodeType, attrs)) {
|
|
538
|
+
command(view.state, view.dispatch);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const toggleMark2 = (mark, attrs, options2) => {
|
|
542
|
+
view.focus();
|
|
543
|
+
const markType = schema.marks[mark];
|
|
544
|
+
const command = commands.toggleMark(markType, attrs, options2);
|
|
545
|
+
command(view.state, view.dispatch);
|
|
546
|
+
};
|
|
547
|
+
const wrapInList2 = (listType, attrs) => {
|
|
548
|
+
view.focus();
|
|
549
|
+
const nodeType = schema.nodes[listType];
|
|
550
|
+
const command = schemaList.wrapInList(nodeType, attrs);
|
|
551
|
+
command(view.state, view.dispatch);
|
|
552
|
+
};
|
|
553
|
+
const clear = () => {
|
|
554
|
+
const tr = view.state.tr.replaceWith(
|
|
555
|
+
0,
|
|
556
|
+
view.state.doc.content.size,
|
|
557
|
+
schema.nodes.doc.createAndFill()
|
|
558
|
+
);
|
|
559
|
+
view.dispatch(tr);
|
|
560
|
+
};
|
|
561
|
+
return {
|
|
562
|
+
isBlockTypeActive,
|
|
563
|
+
setBlockType: setBlockType2,
|
|
564
|
+
toggleBlockType,
|
|
565
|
+
toggleMark: toggleMark2,
|
|
566
|
+
wrapInList: wrapInList2,
|
|
567
|
+
clear,
|
|
568
|
+
attachFile: (files) => {
|
|
569
|
+
options.attachFile?.(view, files);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
// src/input.tsx
|
|
576
|
+
import React, {
|
|
577
|
+
useEffect
|
|
578
|
+
} from "react";
|
|
579
|
+
import { debounceTime, filter } from "rxjs";
|
|
580
|
+
function TextEditorInput({
|
|
581
|
+
ref,
|
|
582
|
+
onChange,
|
|
583
|
+
updateDelay = 0,
|
|
584
|
+
...props
|
|
585
|
+
}) {
|
|
586
|
+
const inputRef = React.useRef(null);
|
|
587
|
+
useEffect(() => {
|
|
588
|
+
const controller = ref.current;
|
|
589
|
+
if (!controller) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const sub = controller.subject.pipe(
|
|
593
|
+
filter((tr) => tr.docChanged),
|
|
594
|
+
debounceTime(updateDelay)
|
|
595
|
+
).subscribe(() => {
|
|
596
|
+
if (inputRef.current) {
|
|
597
|
+
inputRef.current.value = controller.value;
|
|
598
|
+
const event = new Event("input", { bubbles: true });
|
|
599
|
+
inputRef.current.dispatchEvent(event);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
return () => {
|
|
603
|
+
sub.unsubscribe();
|
|
604
|
+
controller.view.destroy();
|
|
605
|
+
};
|
|
606
|
+
}, []);
|
|
607
|
+
return /* @__PURE__ */ React.createElement("input", { ...props, ref: inputRef, type: "hidden", onInput: onChange });
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/text_editor_controller.tsx
|
|
611
|
+
import {
|
|
612
|
+
EditorState as EditorState2
|
|
613
|
+
} from "prosemirror-state";
|
|
614
|
+
import { EditorView as EditorView3 } from "prosemirror-view";
|
|
615
|
+
import * as commands2 from "prosemirror-commands";
|
|
616
|
+
import { keymap } from "prosemirror-keymap";
|
|
617
|
+
|
|
618
|
+
// src/plugins/drag_and_drop.tsx
|
|
619
|
+
import { Plugin } from "prosemirror-state";
|
|
620
|
+
function dragAndDropPlugin({ attachFile }) {
|
|
621
|
+
return new Plugin({
|
|
622
|
+
props: {
|
|
623
|
+
handleDOMEvents: {
|
|
624
|
+
drop(view, event) {
|
|
625
|
+
if (!attachFile) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const files = event.dataTransfer?.files;
|
|
629
|
+
if (!files || files.length === 0) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
event.preventDefault();
|
|
633
|
+
const pos = view.state.selection.$from.pos || view.posAtCoords({
|
|
634
|
+
left: event.clientX,
|
|
635
|
+
top: event.clientY
|
|
636
|
+
})?.pos || null;
|
|
637
|
+
if (pos === null) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const medias = Array.from(files).filter(
|
|
641
|
+
(file) => file.type.startsWith("image/") || file.type.startsWith("video/")
|
|
642
|
+
);
|
|
643
|
+
attachFile(view, medias);
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/plugins/upload_placeholder.tsx
|
|
652
|
+
import { Plugin as Plugin2 } from "prosemirror-state";
|
|
653
|
+
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
654
|
+
var uploadPlaceholderPlugin = new Plugin2({
|
|
655
|
+
state: {
|
|
656
|
+
init() {
|
|
657
|
+
return DecorationSet.empty;
|
|
658
|
+
},
|
|
659
|
+
apply(tr, set) {
|
|
660
|
+
set = set.map(tr.mapping, tr.doc);
|
|
661
|
+
const action = tr.getMeta(this);
|
|
662
|
+
if (action && action.add) {
|
|
663
|
+
const { type, width, height } = action.add;
|
|
664
|
+
const widget = document.createElement("div");
|
|
665
|
+
widget.className = "upload-placeholder";
|
|
666
|
+
widget.style.width = `100%`;
|
|
667
|
+
if (type.startsWith("image/") || type.startsWith("video/")) {
|
|
668
|
+
widget.style.aspectRatio = `${width} / ${height}`;
|
|
669
|
+
widget.style.maxWidth = `${width}px`;
|
|
670
|
+
} else {
|
|
671
|
+
widget.style.height = "80px";
|
|
672
|
+
}
|
|
673
|
+
const progress = document.createElement("div");
|
|
674
|
+
progress.className = "upload-progress";
|
|
675
|
+
widget.appendChild(progress);
|
|
676
|
+
const deco = Decoration.widget(action.add.pos, widget, {
|
|
677
|
+
id: action.add.id
|
|
678
|
+
});
|
|
679
|
+
set = set.add(tr.doc, [deco]);
|
|
680
|
+
} else if (action && action.progress) {
|
|
681
|
+
const found = set.find(
|
|
682
|
+
void 0,
|
|
683
|
+
void 0,
|
|
684
|
+
(spec) => spec.id === action.progress.id
|
|
685
|
+
);
|
|
686
|
+
if (found.length) {
|
|
687
|
+
const widget = found[0].type.toDOM;
|
|
688
|
+
const progress = widget.querySelector(".upload-progress");
|
|
689
|
+
if (progress) {
|
|
690
|
+
progress.innerHTML = `${Math.round(action.progress.progress)}%`;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
} else if (action && action.remove) {
|
|
694
|
+
set = set.remove(
|
|
695
|
+
set.find(void 0, void 0, (spec) => spec.id === action.remove.id)
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
return set;
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
props: {
|
|
702
|
+
decorations(state) {
|
|
703
|
+
return this.getState(state);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
var findPlaceholder = (state, id) => {
|
|
708
|
+
const decos = uploadPlaceholderPlugin.getState(state);
|
|
709
|
+
if (!decos) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const found = decos.find(void 0, void 0, (spec) => spec.id === id);
|
|
713
|
+
return found.length ? found[0].from : null;
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
// src/plugins/placehoder.tsx
|
|
717
|
+
import "prosemirror-model";
|
|
718
|
+
import { Plugin as Plugin3 } from "prosemirror-state";
|
|
719
|
+
import "prosemirror-view";
|
|
720
|
+
var getFirstChildDescendants = (view) => {
|
|
721
|
+
const nodes = [];
|
|
722
|
+
view.state.doc?.descendants((n) => {
|
|
723
|
+
nodes.push(n);
|
|
724
|
+
});
|
|
725
|
+
return nodes;
|
|
726
|
+
};
|
|
727
|
+
function placeholderPlugin(text) {
|
|
728
|
+
const update = (view) => {
|
|
729
|
+
const decos = uploadPlaceholderPlugin.getState(view.state);
|
|
730
|
+
if (decos && decos.find().length > 0 || view.state.doc.content.content.some((e) => e.type.name !== "paragraph") || view.state.doc.childCount > 1 || getFirstChildDescendants(view).length > 1 || view.state.doc.textContent) {
|
|
731
|
+
view.dom.removeAttribute("data-placeholder");
|
|
732
|
+
} else {
|
|
733
|
+
view.dom.setAttribute("data-placeholder", text);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
return new Plugin3({
|
|
737
|
+
view(view) {
|
|
738
|
+
update(view);
|
|
739
|
+
return { update };
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/text_editor_controller.tsx
|
|
745
|
+
import { history } from "prosemirror-history";
|
|
746
|
+
|
|
747
|
+
// src/plugins/keymap.tsx
|
|
748
|
+
import { TextSelection } from "prosemirror-state";
|
|
749
|
+
import { undo, redo } from "prosemirror-history";
|
|
750
|
+
import { chainCommands, splitBlockAs } from "prosemirror-commands";
|
|
751
|
+
import { splitListItem } from "prosemirror-schema-list";
|
|
752
|
+
function buildKeymap(schema) {
|
|
753
|
+
const keys = {};
|
|
754
|
+
function bind(key, cmd) {
|
|
755
|
+
keys[key] = cmd;
|
|
756
|
+
}
|
|
757
|
+
bind("Mod-z", undo);
|
|
758
|
+
bind("Shift-Mod-z", redo);
|
|
759
|
+
bind("Mod-y", redo);
|
|
760
|
+
const li = schema.nodes.list_item;
|
|
761
|
+
bind(
|
|
762
|
+
"Enter",
|
|
763
|
+
chainCommands(splitListItem(li), (state, dispatch) => {
|
|
764
|
+
const { $head } = state.selection;
|
|
765
|
+
if ($head.parent.type === state.schema.nodes.paragraph) {
|
|
766
|
+
splitBlockAs((n) => {
|
|
767
|
+
return {
|
|
768
|
+
type: n.type,
|
|
769
|
+
attrs: n.attrs
|
|
770
|
+
};
|
|
771
|
+
})(state, dispatch);
|
|
772
|
+
return true;
|
|
773
|
+
}
|
|
774
|
+
return false;
|
|
775
|
+
})
|
|
776
|
+
);
|
|
777
|
+
bind("ArrowDown", (state, dispatch) => {
|
|
778
|
+
const doc = state.doc;
|
|
779
|
+
const lastNode = doc.lastChild;
|
|
780
|
+
if (lastNode && lastNode.type.name !== "paragraph") {
|
|
781
|
+
const paragraphType = state.schema.nodes.paragraph;
|
|
782
|
+
let tr = state.tr;
|
|
783
|
+
const endPos = doc.content.size;
|
|
784
|
+
tr = tr.insert(endPos, paragraphType.create());
|
|
785
|
+
tr = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
|
|
786
|
+
if (dispatch) {
|
|
787
|
+
dispatch(tr);
|
|
788
|
+
}
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
return false;
|
|
792
|
+
});
|
|
793
|
+
return keys;
|
|
794
|
+
}
|
|
434
795
|
|
|
435
796
|
// src/cn.ts
|
|
436
797
|
function cn(...classes) {
|
|
@@ -439,7 +800,9 @@ function cn(...classes) {
|
|
|
439
800
|
|
|
440
801
|
// src/attach_file.tsx
|
|
441
802
|
import "prosemirror-view";
|
|
442
|
-
|
|
803
|
+
|
|
804
|
+
// src/base64_file_uploader.ts
|
|
805
|
+
var base64FileUploader = async (file) => {
|
|
443
806
|
const base64 = await new Promise((resolve, reject) => {
|
|
444
807
|
const reader = new FileReader();
|
|
445
808
|
reader.onload = () => {
|
|
@@ -453,10 +816,12 @@ var base64ImageUploader = async (file) => {
|
|
|
453
816
|
alt: file.name
|
|
454
817
|
};
|
|
455
818
|
};
|
|
819
|
+
|
|
820
|
+
// src/attach_file.tsx
|
|
456
821
|
function createAttachFile({
|
|
457
822
|
schema,
|
|
458
823
|
generateMetadata,
|
|
459
|
-
uploadFile =
|
|
824
|
+
uploadFile = base64FileUploader
|
|
460
825
|
}) {
|
|
461
826
|
const attachEachFile = async (view, file, pos) => {
|
|
462
827
|
const metadata = generateMetadata ? await generateMetadata(file) : {};
|
|
@@ -519,177 +884,218 @@ function createAttachFile({
|
|
|
519
884
|
};
|
|
520
885
|
}
|
|
521
886
|
|
|
522
|
-
// src/
|
|
523
|
-
|
|
524
|
-
|
|
887
|
+
// src/text_editor_controller.tsx
|
|
888
|
+
import { DOMParser, DOMSerializer } from "prosemirror-model";
|
|
889
|
+
import { Subject } from "rxjs";
|
|
890
|
+
function createTextEditorController(container, schema, options, {
|
|
891
|
+
mode,
|
|
892
|
+
state,
|
|
893
|
+
editor,
|
|
894
|
+
defaultValue,
|
|
895
|
+
updateDelay = 500,
|
|
896
|
+
placeholder
|
|
897
|
+
}) {
|
|
898
|
+
const subject = new Subject();
|
|
525
899
|
const prosemirrorParser = DOMParser.fromSchema(schema);
|
|
526
900
|
const prosemirrorSerializer = DOMSerializer.fromSchema(schema);
|
|
901
|
+
const wrapper = document.createElement("div");
|
|
902
|
+
const toInnerHTML = (value) => {
|
|
903
|
+
if (mode === "html") {
|
|
904
|
+
return value;
|
|
905
|
+
}
|
|
906
|
+
return value.split("\n").map((line) => `<p>${line}</p>`).join("");
|
|
907
|
+
};
|
|
908
|
+
wrapper.innerHTML = toInnerHTML(defaultValue ? String(defaultValue) : "");
|
|
527
909
|
const attachFile = createAttachFile({
|
|
528
910
|
schema,
|
|
529
911
|
generateMetadata: options.attachFile?.generateMetadata,
|
|
530
912
|
uploadFile: options.attachFile?.uploadFile
|
|
531
913
|
});
|
|
914
|
+
const view = new EditorView3(container, {
|
|
915
|
+
...editor,
|
|
916
|
+
attributes: (state2) => {
|
|
917
|
+
const propsAttributes = (() => {
|
|
918
|
+
if (typeof editor?.attributes === "function") {
|
|
919
|
+
return editor.attributes(state2);
|
|
920
|
+
}
|
|
921
|
+
return editor?.attributes;
|
|
922
|
+
})();
|
|
923
|
+
return {
|
|
924
|
+
...propsAttributes,
|
|
925
|
+
class: cn(options?.className, propsAttributes?.class),
|
|
926
|
+
spellcheck: propsAttributes?.spellcheck || "false",
|
|
927
|
+
style: options.style || "width: 100%; height: inherit; outline: none;"
|
|
928
|
+
};
|
|
929
|
+
},
|
|
930
|
+
state: EditorState2.create({
|
|
931
|
+
...state,
|
|
932
|
+
schema: state?.schema || schema,
|
|
933
|
+
doc: state?.doc || prosemirrorParser.parse(wrapper),
|
|
934
|
+
plugins: [
|
|
935
|
+
...state?.plugins || [],
|
|
936
|
+
history({
|
|
937
|
+
newGroupDelay: updateDelay
|
|
938
|
+
}),
|
|
939
|
+
keymap(buildKeymap(schema)),
|
|
940
|
+
keymap(commands2.baseKeymap),
|
|
941
|
+
uploadPlaceholderPlugin,
|
|
942
|
+
dragAndDropPlugin({
|
|
943
|
+
attachFile
|
|
944
|
+
}),
|
|
945
|
+
placeholder && placeholderPlugin(placeholder)
|
|
946
|
+
].filter((e) => !!e)
|
|
947
|
+
}),
|
|
948
|
+
dispatchTransaction(tr) {
|
|
949
|
+
let result;
|
|
950
|
+
if (editor?.dispatchTransaction) {
|
|
951
|
+
result = editor.dispatchTransaction(tr);
|
|
952
|
+
} else {
|
|
953
|
+
view.updateState(view.state.apply(tr));
|
|
954
|
+
}
|
|
955
|
+
subject.next(tr);
|
|
956
|
+
return result;
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
function setValue(value) {
|
|
960
|
+
const wrap = document.createElement("div");
|
|
961
|
+
wrap.innerHTML = toInnerHTML(value);
|
|
962
|
+
const doc = prosemirrorParser.parse(wrap);
|
|
963
|
+
const tr = view.state.tr.replaceWith(
|
|
964
|
+
0,
|
|
965
|
+
view.state.doc.content.size,
|
|
966
|
+
doc.content
|
|
967
|
+
);
|
|
968
|
+
view.dispatch(tr);
|
|
969
|
+
}
|
|
970
|
+
function toHTML() {
|
|
971
|
+
const fragment = prosemirrorSerializer.serializeFragment(
|
|
972
|
+
view.state.doc.content
|
|
973
|
+
);
|
|
974
|
+
const container2 = document.createElement("div");
|
|
975
|
+
container2.appendChild(fragment);
|
|
976
|
+
return container2.innerHTML;
|
|
977
|
+
}
|
|
978
|
+
function toTextContent() {
|
|
979
|
+
const state2 = view.state;
|
|
980
|
+
return state2.doc.textBetween(0, state2.doc.content.size, "\n");
|
|
981
|
+
}
|
|
982
|
+
const textEditorCommands = createCommands(schema, view, {
|
|
983
|
+
attachFile
|
|
984
|
+
});
|
|
985
|
+
const textEditorController = {
|
|
986
|
+
schema,
|
|
987
|
+
view,
|
|
988
|
+
subject,
|
|
989
|
+
set value(value) {
|
|
990
|
+
setValue(value);
|
|
991
|
+
},
|
|
992
|
+
get value() {
|
|
993
|
+
switch (mode) {
|
|
994
|
+
case "text":
|
|
995
|
+
return toTextContent();
|
|
996
|
+
default:
|
|
997
|
+
return toHTML();
|
|
998
|
+
}
|
|
999
|
+
},
|
|
1000
|
+
commands: textEditorCommands
|
|
1001
|
+
};
|
|
1002
|
+
return textEditorController;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/create_text_editor.tsx
|
|
1006
|
+
function createTextEditor(options = {}) {
|
|
1007
|
+
const schema = createSchema();
|
|
532
1008
|
function Component({
|
|
533
1009
|
ref,
|
|
534
|
-
|
|
535
|
-
editor,
|
|
536
|
-
mode = "html",
|
|
537
|
-
container,
|
|
1010
|
+
className,
|
|
538
1011
|
autoFocus,
|
|
539
|
-
name,
|
|
540
1012
|
placeholder,
|
|
541
|
-
className,
|
|
542
1013
|
defaultValue,
|
|
543
|
-
onClick,
|
|
544
1014
|
onChange,
|
|
545
|
-
updateDelay
|
|
1015
|
+
updateDelay,
|
|
546
1016
|
...props
|
|
547
1017
|
} = {}) {
|
|
548
1018
|
const containerRef = useRef(null);
|
|
549
|
-
const inputRef = useRef(null);
|
|
550
1019
|
const controllerRef = useRef(null);
|
|
551
|
-
const subject = useMemo(() => new Subject(), []);
|
|
552
1020
|
useImperativeHandle(ref, () => {
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
return value;
|
|
557
|
-
}
|
|
558
|
-
return value.split("\n").map((line) => `<p>${line}</p>`).join("");
|
|
559
|
-
};
|
|
560
|
-
wrapper.innerHTML = toInnerHTML(defaultValue ? String(defaultValue) : "");
|
|
561
|
-
const view = new EditorView3(containerRef.current, {
|
|
562
|
-
...editor,
|
|
563
|
-
attributes: (state2) => {
|
|
564
|
-
const propsAttributes = (() => {
|
|
565
|
-
if (typeof editor?.attributes === "function") {
|
|
566
|
-
return editor.attributes(state2);
|
|
567
|
-
}
|
|
568
|
-
return editor?.attributes;
|
|
569
|
-
})();
|
|
570
|
-
return {
|
|
571
|
-
...propsAttributes,
|
|
572
|
-
class: cn(propsAttributes?.class, className),
|
|
573
|
-
spellcheck: propsAttributes?.spellcheck || "false"
|
|
574
|
-
};
|
|
575
|
-
},
|
|
576
|
-
state: EditorState2.create({
|
|
577
|
-
...state,
|
|
578
|
-
schema: state?.schema || schema,
|
|
579
|
-
doc: state?.doc || prosemirrorParser.parse(wrapper),
|
|
580
|
-
plugins: [
|
|
581
|
-
...state?.plugins || [],
|
|
582
|
-
history({
|
|
583
|
-
newGroupDelay: updateDelay
|
|
584
|
-
}),
|
|
585
|
-
keymap(buildKeymap(schema)),
|
|
586
|
-
keymap(baseKeymap),
|
|
587
|
-
uploadPlaceholderPlugin,
|
|
588
|
-
attachFile ? dragAndDropPlugin({
|
|
589
|
-
attachFile
|
|
590
|
-
}) : null,
|
|
591
|
-
placeholder && placeholderPlugin(placeholder)
|
|
592
|
-
].filter((e) => !!e)
|
|
593
|
-
}),
|
|
594
|
-
dispatchTransaction(tr) {
|
|
595
|
-
let result;
|
|
596
|
-
if (editor?.dispatchTransaction) {
|
|
597
|
-
result = editor.dispatchTransaction(tr);
|
|
598
|
-
} else {
|
|
599
|
-
view.updateState(view.state.apply(tr));
|
|
600
|
-
}
|
|
601
|
-
subject.next(tr);
|
|
602
|
-
return result;
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
function setValue(value) {
|
|
606
|
-
const wrap = document.createElement("div");
|
|
607
|
-
wrap.innerHTML = toInnerHTML(value);
|
|
608
|
-
const doc = prosemirrorParser.parse(wrap);
|
|
609
|
-
const tr = view.state.tr.replaceWith(
|
|
610
|
-
0,
|
|
611
|
-
view.state.doc.content.size,
|
|
612
|
-
doc.content
|
|
613
|
-
);
|
|
614
|
-
view.dispatch(tr);
|
|
615
|
-
}
|
|
616
|
-
function clear() {
|
|
617
|
-
const tr = view.state.tr.replaceWith(
|
|
618
|
-
0,
|
|
619
|
-
view.state.doc.content.size,
|
|
620
|
-
schema.nodes.doc.createAndFill()
|
|
621
|
-
);
|
|
622
|
-
view.dispatch(tr);
|
|
623
|
-
}
|
|
624
|
-
function toHTML() {
|
|
625
|
-
const fragment = prosemirrorSerializer.serializeFragment(
|
|
626
|
-
view.state.doc.content
|
|
627
|
-
);
|
|
628
|
-
const container2 = document.createElement("div");
|
|
629
|
-
container2.appendChild(fragment);
|
|
630
|
-
return container2.innerHTML;
|
|
631
|
-
}
|
|
632
|
-
function toTextContent() {
|
|
633
|
-
const state2 = view.state;
|
|
634
|
-
return state2.doc.textBetween(0, state2.doc.content.size, "\n");
|
|
635
|
-
}
|
|
636
|
-
if (autoFocus) {
|
|
637
|
-
view.focus();
|
|
638
|
-
}
|
|
639
|
-
const textEditorController = {
|
|
1021
|
+
const container = containerRef.current;
|
|
1022
|
+
const textEditorController = createTextEditorController(
|
|
1023
|
+
container,
|
|
640
1024
|
schema,
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
setValue(value);
|
|
645
|
-
},
|
|
646
|
-
get value() {
|
|
647
|
-
switch (mode) {
|
|
648
|
-
case "text":
|
|
649
|
-
return toTextContent();
|
|
650
|
-
default:
|
|
651
|
-
return toHTML();
|
|
652
|
-
}
|
|
653
|
-
},
|
|
654
|
-
clear
|
|
655
|
-
};
|
|
1025
|
+
options,
|
|
1026
|
+
props
|
|
1027
|
+
);
|
|
656
1028
|
controllerRef.current = textEditorController;
|
|
657
1029
|
return textEditorController;
|
|
658
1030
|
});
|
|
659
|
-
|
|
1031
|
+
useEffect2(() => {
|
|
660
1032
|
const controller = controllerRef.current;
|
|
661
1033
|
if (!controller) {
|
|
662
1034
|
return;
|
|
663
1035
|
}
|
|
664
|
-
const sub = controller.subject.pipe(
|
|
665
|
-
filter((tr) => tr.docChanged),
|
|
666
|
-
debounceTime(updateDelay)
|
|
667
|
-
).subscribe(() => {
|
|
668
|
-
if (inputRef.current) {
|
|
669
|
-
inputRef.current.value = controller.value;
|
|
670
|
-
const event = new Event("input", { bubbles: true });
|
|
671
|
-
inputRef.current.dispatchEvent(event);
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
1036
|
if (autoFocus) {
|
|
675
1037
|
controller.view.focus();
|
|
676
1038
|
}
|
|
677
|
-
return () => {
|
|
678
|
-
sub.unsubscribe();
|
|
679
|
-
controller.view.destroy();
|
|
680
|
-
};
|
|
681
1039
|
}, []);
|
|
682
|
-
return /* @__PURE__ */
|
|
1040
|
+
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { ...props, ref: containerRef, className }), /* @__PURE__ */ React2.createElement(
|
|
1041
|
+
TextEditorInput,
|
|
1042
|
+
{
|
|
1043
|
+
ref: controllerRef,
|
|
1044
|
+
updateDelay,
|
|
1045
|
+
onChange
|
|
1046
|
+
}
|
|
1047
|
+
));
|
|
683
1048
|
}
|
|
684
|
-
return
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
1049
|
+
return Component;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// src/html.tsx
|
|
1053
|
+
import { decode } from "html-entities";
|
|
1054
|
+
import React3 from "react";
|
|
1055
|
+
function createInnerHTML(raw) {
|
|
1056
|
+
return raw.replace(/<\/p>/g, "<br></p>").replace(/(<p><br><\/p>)+$/g, "").replace(
|
|
1057
|
+
/<code class="language-(\w+)">([\s\S]*?)<\/code>/g,
|
|
1058
|
+
(_, lang, code) => {
|
|
1059
|
+
try {
|
|
1060
|
+
const highlighted = highlighter.highlight(code, {
|
|
1061
|
+
language: lang
|
|
1062
|
+
}).value;
|
|
1063
|
+
return `<code class="language-${lang}">${decode(highlighted)}</code>`;
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
return `<code class="language-${lang}">${decode(code)}</code>`;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
).replace(
|
|
1069
|
+
/<a([^>]*target="_blank"[^>]*)>(.*?)<\/a>/g,
|
|
1070
|
+
(_, attrs, content) => {
|
|
1071
|
+
return `<a${attrs} rel="noopener noreferrer" target="_blank">${content}<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.25 15.5a.75.75 0 0 1-.75-.75V7.56L7.28 17.78a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L16.44 6.5H9.25a.75.75 0 0 1 0-1.5h9a.75.75 0 0 1 .75.75v9a.75.75 0 0 1-.75.75Z"></path></svg></a>`;
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
function createTextEditorView(options = {}) {
|
|
1076
|
+
return function Component({
|
|
1077
|
+
className,
|
|
1078
|
+
dangerouslySetInnerHTML,
|
|
1079
|
+
...props
|
|
1080
|
+
}) {
|
|
1081
|
+
return /* @__PURE__ */ React3.createElement(
|
|
1082
|
+
"div",
|
|
1083
|
+
{
|
|
1084
|
+
...props,
|
|
1085
|
+
className: cn(options?.className, className),
|
|
1086
|
+
dangerouslySetInnerHTML: {
|
|
1087
|
+
__html: createInnerHTML(
|
|
1088
|
+
String(dangerouslySetInnerHTML?.__html || "")
|
|
1089
|
+
)
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
);
|
|
688
1093
|
};
|
|
689
1094
|
}
|
|
690
1095
|
export {
|
|
691
|
-
base64ImageUploader,
|
|
692
1096
|
createAttachFile,
|
|
1097
|
+
createInnerHTML,
|
|
693
1098
|
createSchema,
|
|
694
|
-
createTextEditor
|
|
1099
|
+
createTextEditor,
|
|
1100
|
+
createTextEditorView
|
|
695
1101
|
};
|