dn-react-text-editor 0.1.1 → 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 +14 -214
- package/dist/attach_file.d.mts +20 -18
- package/dist/attach_file.d.ts +20 -18
- package/dist/attach_file.js +18 -9
- package/dist/attach_file.mjs +18 -9
- 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 +790 -380
- package/dist/index.mjs +789 -377
- 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 -720
- package/dist/text_editor.mjs +0 -687
package/dist/index.mjs
CHANGED
|
@@ -1,176 +1,83 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { EditorView as EditorView3 } from "prosemirror-view";
|
|
7
|
-
import { useEffect, useRef } from "react";
|
|
8
|
-
import { baseKeymap } from "prosemirror-commands";
|
|
9
|
-
import { keymap } from "prosemirror-keymap";
|
|
10
|
-
|
|
11
|
-
// src/plugins/drag_and_drop.tsx
|
|
12
|
-
import { Plugin } from "prosemirror-state";
|
|
13
|
-
function dragAndDropPlugin({ attachFile }) {
|
|
14
|
-
return new Plugin({
|
|
15
|
-
props: {
|
|
16
|
-
handleDOMEvents: {
|
|
17
|
-
drop(view, event) {
|
|
18
|
-
const files = event.dataTransfer?.files;
|
|
19
|
-
if (!files || files.length === 0) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
event.preventDefault();
|
|
23
|
-
const pos = view.state.selection.$from.pos || view.posAtCoords({
|
|
24
|
-
left: event.clientX,
|
|
25
|
-
top: event.clientY
|
|
26
|
-
})?.pos || null;
|
|
27
|
-
if (pos === null) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const medias = Array.from(files).filter(
|
|
31
|
-
(file) => file.type.startsWith("image/") || file.type.startsWith("video/")
|
|
32
|
-
);
|
|
33
|
-
attachFile(view, medias);
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/plugins/upload_placeholder.tsx
|
|
42
|
-
import { Plugin as Plugin2 } from "prosemirror-state";
|
|
43
|
-
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
44
|
-
var uploadPlaceholderPlugin = new Plugin2({
|
|
45
|
-
state: {
|
|
46
|
-
init() {
|
|
47
|
-
return DecorationSet.empty;
|
|
48
|
-
},
|
|
49
|
-
apply(tr, set) {
|
|
50
|
-
set = set.map(tr.mapping, tr.doc);
|
|
51
|
-
const action = tr.getMeta(this);
|
|
52
|
-
if (action && action.add) {
|
|
53
|
-
const { type, width, height } = action.add;
|
|
54
|
-
const widget = document.createElement("div");
|
|
55
|
-
widget.className = "upload-placeholder";
|
|
56
|
-
widget.style.width = `100%`;
|
|
57
|
-
if (type.startsWith("image/") || type.startsWith("video/")) {
|
|
58
|
-
widget.style.aspectRatio = `${width} / ${height}`;
|
|
59
|
-
widget.style.maxWidth = `${width}px`;
|
|
60
|
-
} else {
|
|
61
|
-
widget.style.height = "80px";
|
|
62
|
-
}
|
|
63
|
-
const progress = document.createElement("div");
|
|
64
|
-
progress.className = "upload-progress";
|
|
65
|
-
widget.appendChild(progress);
|
|
66
|
-
const deco = Decoration.widget(action.add.pos, widget, {
|
|
67
|
-
id: action.add.id
|
|
68
|
-
});
|
|
69
|
-
set = set.add(tr.doc, [deco]);
|
|
70
|
-
} else if (action && action.progress) {
|
|
71
|
-
const found = set.find(
|
|
72
|
-
void 0,
|
|
73
|
-
void 0,
|
|
74
|
-
(spec) => spec.id === action.progress.id
|
|
75
|
-
);
|
|
76
|
-
if (found.length) {
|
|
77
|
-
const widget = found[0].type.toDOM;
|
|
78
|
-
const progress = widget.querySelector(".upload-progress");
|
|
79
|
-
if (progress) {
|
|
80
|
-
progress.innerHTML = `${Math.round(action.progress.progress)}%`;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} else if (action && action.remove) {
|
|
84
|
-
set = set.remove(
|
|
85
|
-
set.find(void 0, void 0, (spec) => spec.id === action.remove.id)
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return set;
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
props: {
|
|
92
|
-
decorations(state) {
|
|
93
|
-
return this.getState(state);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
var findPlaceholder = (state, id) => {
|
|
98
|
-
const decos = uploadPlaceholderPlugin.getState(state);
|
|
99
|
-
if (!decos) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
const found = decos.find(void 0, void 0, (spec) => spec.id === id);
|
|
103
|
-
return found.length ? found[0].from : null;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// src/plugins/placehoder.tsx
|
|
107
|
-
import "prosemirror-model";
|
|
108
|
-
import { Plugin as Plugin3 } from "prosemirror-state";
|
|
1
|
+
// src/create_text_editor.tsx
|
|
2
|
+
import React2, {
|
|
3
|
+
useImperativeHandle
|
|
4
|
+
} from "react";
|
|
5
|
+
import "prosemirror-state";
|
|
109
6
|
import "prosemirror-view";
|
|
110
|
-
|
|
111
|
-
const nodes = [];
|
|
112
|
-
view.state.doc?.descendants((n) => {
|
|
113
|
-
nodes.push(n);
|
|
114
|
-
});
|
|
115
|
-
return nodes;
|
|
116
|
-
};
|
|
117
|
-
function placeholderPlugin(text) {
|
|
118
|
-
const update = (view) => {
|
|
119
|
-
const decos = uploadPlaceholderPlugin.getState(view.state);
|
|
120
|
-
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) {
|
|
121
|
-
view.dom.removeAttribute("data-placeholder");
|
|
122
|
-
} else {
|
|
123
|
-
view.dom.setAttribute("data-placeholder", text);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
return new Plugin3({
|
|
127
|
-
view(view) {
|
|
128
|
-
update(view);
|
|
129
|
-
return { update };
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// src/text_editor.tsx
|
|
135
|
-
import { DOMSerializer, DOMParser } from "prosemirror-model";
|
|
136
|
-
import { history } from "prosemirror-history";
|
|
137
|
-
|
|
138
|
-
// src/plugins/keymap.tsx
|
|
139
|
-
import { undo, redo } from "prosemirror-history";
|
|
140
|
-
import { chainCommands, splitBlockAs } from "prosemirror-commands";
|
|
141
|
-
import { splitListItem } from "prosemirror-schema-list";
|
|
142
|
-
function buildKeymap(schema) {
|
|
143
|
-
const keys = {};
|
|
144
|
-
function bind(key, cmd) {
|
|
145
|
-
keys[key] = cmd;
|
|
146
|
-
}
|
|
147
|
-
bind("Mod-z", undo);
|
|
148
|
-
bind("Shift-Mod-z", redo);
|
|
149
|
-
bind("Mod-y", redo);
|
|
150
|
-
const li = schema.nodes.list_item;
|
|
151
|
-
bind(
|
|
152
|
-
"Enter",
|
|
153
|
-
chainCommands(splitListItem(li), (state, dispatch) => {
|
|
154
|
-
const { $head } = state.selection;
|
|
155
|
-
if ($head.parent.type === state.schema.nodes.paragraph) {
|
|
156
|
-
splitBlockAs((n) => {
|
|
157
|
-
return {
|
|
158
|
-
type: n.type,
|
|
159
|
-
attrs: n.attrs
|
|
160
|
-
};
|
|
161
|
-
})(state, dispatch);
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
})
|
|
166
|
-
);
|
|
167
|
-
return keys;
|
|
168
|
-
}
|
|
7
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
169
8
|
|
|
170
9
|
// src/schema.tsx
|
|
171
10
|
import { Schema } from "prosemirror-model";
|
|
172
11
|
import { addListNodes } from "prosemirror-schema-list";
|
|
173
|
-
|
|
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: {} }) {
|
|
174
81
|
const customSchema = new Schema({
|
|
175
82
|
nodes: {
|
|
176
83
|
doc: { content: "block+" },
|
|
@@ -336,68 +243,233 @@ function createSchema() {
|
|
|
336
243
|
}
|
|
337
244
|
];
|
|
338
245
|
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
246
|
+
},
|
|
247
|
+
video: {
|
|
248
|
+
inline: false,
|
|
249
|
+
group: "block",
|
|
250
|
+
draggable: true,
|
|
343
251
|
attrs: {
|
|
344
|
-
|
|
345
|
-
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
|
+
}
|
|
346
269
|
},
|
|
347
|
-
inclusive: false,
|
|
348
270
|
parseDOM: [
|
|
349
271
|
{
|
|
350
|
-
tag: "
|
|
272
|
+
tag: "video",
|
|
351
273
|
getAttrs(dom) {
|
|
352
274
|
return {
|
|
353
|
-
|
|
354
|
-
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")
|
|
355
280
|
};
|
|
356
281
|
}
|
|
357
282
|
}
|
|
358
283
|
],
|
|
359
284
|
toDOM(node) {
|
|
360
|
-
const {
|
|
361
|
-
const target = "_blank";
|
|
362
|
-
const rel = "noopener noreferrer";
|
|
285
|
+
const { src, title, width, height, poster } = node.attrs;
|
|
363
286
|
return [
|
|
364
|
-
"
|
|
365
|
-
{
|
|
366
|
-
|
|
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
|
+
}
|
|
367
298
|
];
|
|
368
299
|
}
|
|
369
300
|
},
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
301
|
+
iframe: {
|
|
302
|
+
group: "block",
|
|
303
|
+
draggable: true,
|
|
304
|
+
attrs: {
|
|
305
|
+
src: { validate: "string" },
|
|
306
|
+
title: {
|
|
307
|
+
default: null,
|
|
308
|
+
validate: "string|null"
|
|
376
309
|
},
|
|
377
|
-
{
|
|
378
|
-
|
|
379
|
-
|
|
310
|
+
width: {
|
|
311
|
+
default: null,
|
|
312
|
+
validate: "number|null"
|
|
380
313
|
},
|
|
381
|
-
{
|
|
382
|
-
|
|
383
|
-
|
|
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"
|
|
384
333
|
}
|
|
385
|
-
|
|
386
|
-
toDOM() {
|
|
387
|
-
return ["strong", 0];
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
italic: {
|
|
334
|
+
},
|
|
391
335
|
parseDOM: [
|
|
392
|
-
{ tag: "em" },
|
|
393
|
-
{ tag: "i" },
|
|
394
|
-
{ style: "font-style=italic" },
|
|
395
336
|
{
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
+
],
|
|
472
|
+
toDOM() {
|
|
401
473
|
return ["em", 0];
|
|
402
474
|
}
|
|
403
475
|
},
|
|
@@ -412,22 +484,314 @@ function createSchema() {
|
|
|
412
484
|
toDOM() {
|
|
413
485
|
return ["u", 0];
|
|
414
486
|
}
|
|
415
|
-
}
|
|
416
|
-
|
|
487
|
+
},
|
|
488
|
+
...spec.marks
|
|
489
|
+
},
|
|
490
|
+
topNode: spec.topNode
|
|
417
491
|
});
|
|
418
492
|
const prosemirrorSchema = new Schema({
|
|
419
|
-
nodes: addListNodes(
|
|
420
|
-
customSchema.spec.nodes,
|
|
421
|
-
"paragraph block*",
|
|
422
|
-
"block"
|
|
423
|
-
),
|
|
493
|
+
nodes: addListNodes(customSchema.spec.nodes, "paragraph block*", "block"),
|
|
424
494
|
marks: customSchema.spec.marks
|
|
425
495
|
});
|
|
426
496
|
return prosemirrorSchema;
|
|
427
497
|
}
|
|
428
498
|
|
|
429
|
-
// src/
|
|
430
|
-
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
|
+
}
|
|
431
795
|
|
|
432
796
|
// src/cn.ts
|
|
433
797
|
function cn(...classes) {
|
|
@@ -436,15 +800,28 @@ function cn(...classes) {
|
|
|
436
800
|
|
|
437
801
|
// src/attach_file.tsx
|
|
438
802
|
import "prosemirror-view";
|
|
803
|
+
|
|
804
|
+
// src/base64_file_uploader.ts
|
|
805
|
+
var base64FileUploader = async (file) => {
|
|
806
|
+
const base64 = await new Promise((resolve, reject) => {
|
|
807
|
+
const reader = new FileReader();
|
|
808
|
+
reader.onload = () => {
|
|
809
|
+
resolve(reader.result);
|
|
810
|
+
};
|
|
811
|
+
reader.onerror = reject;
|
|
812
|
+
reader.readAsDataURL(file);
|
|
813
|
+
});
|
|
814
|
+
return {
|
|
815
|
+
src: base64,
|
|
816
|
+
alt: file.name
|
|
817
|
+
};
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// src/attach_file.tsx
|
|
439
821
|
function createAttachFile({
|
|
440
822
|
schema,
|
|
441
823
|
generateMetadata,
|
|
442
|
-
uploadFile =
|
|
443
|
-
return {
|
|
444
|
-
src: URL.createObjectURL(file),
|
|
445
|
-
alt: file.name
|
|
446
|
-
};
|
|
447
|
-
}
|
|
824
|
+
uploadFile = base64FileUploader
|
|
448
825
|
}) {
|
|
449
826
|
const attachEachFile = async (view, file, pos) => {
|
|
450
827
|
const metadata = generateMetadata ? await generateMetadata(file) : {};
|
|
@@ -496,9 +873,7 @@ function createAttachFile({
|
|
|
496
873
|
}
|
|
497
874
|
view.dispatch(tr2.replaceWith($pos, $pos, node));
|
|
498
875
|
} catch (e) {
|
|
499
|
-
view.dispatch(
|
|
500
|
-
tr.setMeta(uploadPlaceholderPlugin, { remove: { id } })
|
|
501
|
-
);
|
|
876
|
+
view.dispatch(tr.setMeta(uploadPlaceholderPlugin, { remove: { id } }));
|
|
502
877
|
}
|
|
503
878
|
};
|
|
504
879
|
return async (view, files, pos) => {
|
|
@@ -509,181 +884,218 @@ function createAttachFile({
|
|
|
509
884
|
};
|
|
510
885
|
}
|
|
511
886
|
|
|
512
|
-
// src/
|
|
513
|
-
|
|
514
|
-
|
|
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();
|
|
515
899
|
const prosemirrorParser = DOMParser.fromSchema(schema);
|
|
516
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) : "");
|
|
517
909
|
const attachFile = createAttachFile({
|
|
518
910
|
schema,
|
|
519
911
|
generateMetadata: options.attachFile?.generateMetadata,
|
|
520
912
|
uploadFile: options.attachFile?.uploadFile
|
|
521
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();
|
|
522
1008
|
function Component({
|
|
523
1009
|
ref,
|
|
524
|
-
|
|
525
|
-
editor,
|
|
526
|
-
mode = "html",
|
|
527
|
-
container,
|
|
1010
|
+
className,
|
|
528
1011
|
autoFocus,
|
|
529
|
-
name,
|
|
530
1012
|
placeholder,
|
|
531
|
-
className,
|
|
532
1013
|
defaultValue,
|
|
533
|
-
onClick,
|
|
534
1014
|
onChange,
|
|
535
|
-
updateDelay
|
|
1015
|
+
updateDelay,
|
|
536
1016
|
...props
|
|
537
1017
|
} = {}) {
|
|
538
1018
|
const containerRef = useRef(null);
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
1019
|
+
const controllerRef = useRef(null);
|
|
1020
|
+
useImperativeHandle(ref, () => {
|
|
1021
|
+
const container = containerRef.current;
|
|
1022
|
+
const textEditorController = createTextEditorController(
|
|
1023
|
+
container,
|
|
1024
|
+
schema,
|
|
1025
|
+
options,
|
|
1026
|
+
props
|
|
1027
|
+
);
|
|
1028
|
+
controllerRef.current = textEditorController;
|
|
1029
|
+
return textEditorController;
|
|
1030
|
+
});
|
|
1031
|
+
useEffect2(() => {
|
|
1032
|
+
const controller = controllerRef.current;
|
|
1033
|
+
if (!controller) {
|
|
543
1034
|
return;
|
|
544
1035
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const toInnerHTML = (value) => {
|
|
548
|
-
if (mode === "html") {
|
|
549
|
-
return value;
|
|
550
|
-
}
|
|
551
|
-
return value.split("\n").map((line) => `<p>${line}</p>`).join("");
|
|
552
|
-
};
|
|
553
|
-
wrapper.innerHTML = toInnerHTML(defaultValue ? String(defaultValue) : "");
|
|
554
|
-
const view = new EditorView3(element, {
|
|
555
|
-
...editor,
|
|
556
|
-
attributes: (state2) => {
|
|
557
|
-
const propsAttributes = (() => {
|
|
558
|
-
if (typeof editor?.attributes === "function") {
|
|
559
|
-
return editor.attributes(state2);
|
|
560
|
-
}
|
|
561
|
-
return editor?.attributes;
|
|
562
|
-
})();
|
|
563
|
-
return {
|
|
564
|
-
...propsAttributes,
|
|
565
|
-
class: cn(propsAttributes?.class, className),
|
|
566
|
-
spellcheck: propsAttributes?.spellcheck || "false"
|
|
567
|
-
};
|
|
568
|
-
},
|
|
569
|
-
state: EditorState2.create({
|
|
570
|
-
...state,
|
|
571
|
-
schema: state?.schema || schema,
|
|
572
|
-
doc: state?.doc || prosemirrorParser.parse(wrapper),
|
|
573
|
-
plugins: [
|
|
574
|
-
...state?.plugins || [],
|
|
575
|
-
history({
|
|
576
|
-
newGroupDelay: updateDelay
|
|
577
|
-
}),
|
|
578
|
-
keymap(buildKeymap(schema)),
|
|
579
|
-
keymap(baseKeymap),
|
|
580
|
-
uploadPlaceholderPlugin,
|
|
581
|
-
attachFile ? dragAndDropPlugin({
|
|
582
|
-
attachFile
|
|
583
|
-
}) : null,
|
|
584
|
-
placeholder && placeholderPlugin(placeholder)
|
|
585
|
-
].filter((e) => !!e)
|
|
586
|
-
}),
|
|
587
|
-
dispatchTransaction(tr) {
|
|
588
|
-
let result;
|
|
589
|
-
if (editor?.dispatchTransaction) {
|
|
590
|
-
result = editor.dispatchTransaction(tr);
|
|
591
|
-
} else {
|
|
592
|
-
view.updateState(view.state.apply(tr));
|
|
593
|
-
}
|
|
594
|
-
subject.next(tr);
|
|
595
|
-
return result;
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
function setValue(value) {
|
|
599
|
-
const wrap = document.createElement("div");
|
|
600
|
-
wrap.innerHTML = toInnerHTML(value);
|
|
601
|
-
const doc = prosemirrorParser.parse(wrap);
|
|
602
|
-
const tr = view.state.tr.replaceWith(
|
|
603
|
-
0,
|
|
604
|
-
view.state.doc.content.size,
|
|
605
|
-
doc.content
|
|
606
|
-
);
|
|
607
|
-
view.dispatch(tr);
|
|
608
|
-
}
|
|
609
|
-
function clear() {
|
|
610
|
-
const tr = view.state.tr.replaceWith(
|
|
611
|
-
0,
|
|
612
|
-
view.state.doc.content.size,
|
|
613
|
-
schema.nodes.doc.createAndFill()
|
|
614
|
-
);
|
|
615
|
-
view.dispatch(tr);
|
|
1036
|
+
if (autoFocus) {
|
|
1037
|
+
controller.view.focus();
|
|
616
1038
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
1039
|
+
}, []);
|
|
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
|
|
624
1046
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1047
|
+
));
|
|
1048
|
+
}
|
|
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>`;
|
|
628
1066
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
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
|
+
)
|
|
644
1090
|
}
|
|
645
|
-
});
|
|
646
|
-
if (autoFocus) {
|
|
647
|
-
view.focus();
|
|
648
1091
|
}
|
|
649
|
-
|
|
650
|
-
schema,
|
|
651
|
-
view,
|
|
652
|
-
subject,
|
|
653
|
-
set value(value) {
|
|
654
|
-
setValue(value);
|
|
655
|
-
},
|
|
656
|
-
get value() {
|
|
657
|
-
switch (mode) {
|
|
658
|
-
case "text":
|
|
659
|
-
return toTextContent();
|
|
660
|
-
default:
|
|
661
|
-
return toHTML();
|
|
662
|
-
}
|
|
663
|
-
},
|
|
664
|
-
clear
|
|
665
|
-
};
|
|
666
|
-
if (typeof ref === "function") {
|
|
667
|
-
ref(textEditorController);
|
|
668
|
-
} else if (ref) {
|
|
669
|
-
ref.current = textEditorController;
|
|
670
|
-
}
|
|
671
|
-
return () => {
|
|
672
|
-
sub.unsubscribe();
|
|
673
|
-
view.destroy();
|
|
674
|
-
element.innerHTML = "";
|
|
675
|
-
};
|
|
676
|
-
}, []);
|
|
677
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { ref: containerRef, className: container, ...props }), /* @__PURE__ */ React.createElement("input", { ref: inputRef, type: "hidden", name, onInput: onChange }));
|
|
678
|
-
}
|
|
679
|
-
return {
|
|
680
|
-
schema,
|
|
681
|
-
attachFile,
|
|
682
|
-
Component
|
|
1092
|
+
);
|
|
683
1093
|
};
|
|
684
1094
|
}
|
|
685
1095
|
export {
|
|
686
1096
|
createAttachFile,
|
|
1097
|
+
createInnerHTML,
|
|
687
1098
|
createSchema,
|
|
688
|
-
createTextEditor
|
|
1099
|
+
createTextEditor,
|
|
1100
|
+
createTextEditorView
|
|
689
1101
|
};
|