remons-markdown-editor 1.0.0-beta.3 → 1.0.0-beta.4
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/index.cjs +377 -373
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +0 -220
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +89 -37
- package/dist/index.d.ts +89 -37
- package/dist/index.js +368 -365
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +103 -4
package/dist/index.cjs
CHANGED
|
@@ -35,18 +35,20 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/MarkdownEditor.tsx
|
|
38
|
-
var
|
|
38
|
+
var import_react10 = require("react");
|
|
39
|
+
var import_antd6 = require("antd");
|
|
39
40
|
|
|
40
41
|
// src/components/Editor/index.tsx
|
|
41
42
|
var import_react = require("react");
|
|
42
43
|
var import_codemirror = require("codemirror");
|
|
43
|
-
var
|
|
44
|
+
var import_state3 = require("@codemirror/state");
|
|
44
45
|
|
|
45
46
|
// src/components/Editor/codemirrorExtensions.ts
|
|
46
47
|
var import_view2 = require("@codemirror/view");
|
|
47
48
|
var import_commands = require("@codemirror/commands");
|
|
48
49
|
var import_lang_markdown = require("@codemirror/lang-markdown");
|
|
49
50
|
var import_theme_one_dark = require("@codemirror/theme-one-dark");
|
|
51
|
+
var import_state2 = require("@codemirror/state");
|
|
50
52
|
|
|
51
53
|
// src/components/Editor/containerWidget.ts
|
|
52
54
|
var import_view = require("@codemirror/view");
|
|
@@ -124,8 +126,6 @@ function findContainers(docText) {
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
// src/registry/componentRegistry.tsx
|
|
127
|
-
var import_antd = require("antd");
|
|
128
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
129
129
|
var ComponentRegistry = class {
|
|
130
130
|
constructor() {
|
|
131
131
|
this.schemas = /* @__PURE__ */ new Map();
|
|
@@ -168,80 +168,6 @@ var ComponentRegistry = class {
|
|
|
168
168
|
}
|
|
169
169
|
};
|
|
170
170
|
var componentRegistry = new ComponentRegistry();
|
|
171
|
-
componentRegistry.register("linkCard", {
|
|
172
|
-
name: "linkCard",
|
|
173
|
-
label: "\u94FE\u63A5\u5361\u7247",
|
|
174
|
-
fields: [
|
|
175
|
-
{ key: "title", defaultValue: "\u94FE\u63A5\u6807\u9898" },
|
|
176
|
-
{ key: "link", defaultValue: "https://example.com" },
|
|
177
|
-
{ key: "description", defaultValue: "\u94FE\u63A5\u63CF\u8FF0" },
|
|
178
|
-
{ key: "theme", defaultValue: "default" }
|
|
179
|
-
],
|
|
180
|
-
renderDialog: (props, onChange) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
181
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
|
|
182
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u6807\u9898" }),
|
|
183
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input, { value: props.title, onChange: (e) => onChange("title", e.target.value) })
|
|
184
|
-
] }),
|
|
185
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
|
|
186
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u94FE\u63A5" }),
|
|
187
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input, { value: props.link, onChange: (e) => onChange("link", e.target.value) })
|
|
188
|
-
] }),
|
|
189
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
|
|
190
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u63CF\u8FF0" }),
|
|
191
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input.TextArea, { rows: 3, value: props.description, onChange: (e) => onChange("description", e.target.value) })
|
|
192
|
-
] }),
|
|
193
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
194
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u4E3B\u9898" }),
|
|
195
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
196
|
-
import_antd.Select,
|
|
197
|
-
{
|
|
198
|
-
value: props.theme,
|
|
199
|
-
onChange: (v) => onChange("theme", v),
|
|
200
|
-
options: [
|
|
201
|
-
{ label: "\u9ED8\u8BA4", value: "default" },
|
|
202
|
-
{ label: "\u4E3B\u8981", value: "primary" },
|
|
203
|
-
{ label: "\u8B66\u544A", value: "warning" }
|
|
204
|
-
]
|
|
205
|
-
}
|
|
206
|
-
)
|
|
207
|
-
] })
|
|
208
|
-
] })
|
|
209
|
-
});
|
|
210
|
-
componentRegistry.register("note", {
|
|
211
|
-
name: "note",
|
|
212
|
-
label: "\u6CE8\u91CA",
|
|
213
|
-
fields: [
|
|
214
|
-
{ key: "type", defaultValue: "info" },
|
|
215
|
-
{ key: "title", defaultValue: "\u6CE8\u610F" },
|
|
216
|
-
{ key: "content", defaultValue: "\u5907\u6CE8\u5185\u5BB9" }
|
|
217
|
-
],
|
|
218
|
-
renderDialog: (props, onChange) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
219
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
|
|
220
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u7C7B\u578B" }),
|
|
221
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
222
|
-
import_antd.Select,
|
|
223
|
-
{
|
|
224
|
-
value: props.type,
|
|
225
|
-
onChange: (v) => onChange("type", v),
|
|
226
|
-
options: [
|
|
227
|
-
{ label: "\u4FE1\u606F", value: "info" },
|
|
228
|
-
{ label: "\u63D0\u793A", value: "tip" },
|
|
229
|
-
{ label: "\u8B66\u544A", value: "warning" },
|
|
230
|
-
{ label: "\u5371\u9669", value: "danger" }
|
|
231
|
-
]
|
|
232
|
-
}
|
|
233
|
-
)
|
|
234
|
-
] }),
|
|
235
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
|
|
236
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u6807\u9898" }),
|
|
237
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input, { value: props.title, onChange: (e) => onChange("title", e.target.value) })
|
|
238
|
-
] }),
|
|
239
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
240
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u5185\u5BB9" }),
|
|
241
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input.TextArea, { rows: 3, value: props.content, onChange: (e) => onChange("content", e.target.value) })
|
|
242
|
-
] })
|
|
243
|
-
] })
|
|
244
|
-
});
|
|
245
171
|
var componentRegistry_default = componentRegistry;
|
|
246
172
|
|
|
247
173
|
// src/components/Editor/containerWidget.ts
|
|
@@ -387,13 +313,151 @@ var containerWidgetPlugin = import_view.ViewPlugin.fromClass(
|
|
|
387
313
|
}
|
|
388
314
|
);
|
|
389
315
|
|
|
316
|
+
// src/components/Toolbar/buttons.tsx
|
|
317
|
+
function insertAtCursor(view, before, after = "", placeholder = "") {
|
|
318
|
+
const { from, to } = view.state.selection.main;
|
|
319
|
+
const selectedText = view.state.doc.sliceString(from, to);
|
|
320
|
+
const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
|
|
321
|
+
view.dispatch({ changes: { from, to, insert } });
|
|
322
|
+
if (!selectedText && placeholder) {
|
|
323
|
+
view.dispatch({ selection: { anchor: from + before.length } });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function insertAtLineStart(view, text) {
|
|
327
|
+
const { from } = view.state.selection.main;
|
|
328
|
+
const line = view.state.doc.lineAt(from);
|
|
329
|
+
view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
|
|
330
|
+
view.dispatch({ selection: { anchor: line.from + text.length } });
|
|
331
|
+
}
|
|
332
|
+
async function uploadFileToServer(file, uploadConfig) {
|
|
333
|
+
var _a;
|
|
334
|
+
if (uploadConfig.onUploadImage) {
|
|
335
|
+
return uploadConfig.onUploadImage(file);
|
|
336
|
+
}
|
|
337
|
+
if (uploadConfig.uploadUrl) {
|
|
338
|
+
const formData = new FormData();
|
|
339
|
+
formData.append("file", file);
|
|
340
|
+
const response = await fetch(uploadConfig.uploadUrl, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
body: formData
|
|
343
|
+
});
|
|
344
|
+
if (!response.ok) return null;
|
|
345
|
+
const result = await response.json();
|
|
346
|
+
if (uploadConfig.onGetUploadUrl) {
|
|
347
|
+
return uploadConfig.onGetUploadUrl(result);
|
|
348
|
+
}
|
|
349
|
+
return ((_a = result.data) == null ? void 0 : _a.path) || null;
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
function isUploadConfigured(uploadConfig) {
|
|
354
|
+
return !!((uploadConfig == null ? void 0 : uploadConfig.uploadUrl) || (uploadConfig == null ? void 0 : uploadConfig.onUploadImage));
|
|
355
|
+
}
|
|
356
|
+
function uploadImage(view, uploadConfig) {
|
|
357
|
+
const input = document.createElement("input");
|
|
358
|
+
input.type = "file";
|
|
359
|
+
input.accept = "image/*";
|
|
360
|
+
input.style.display = "none";
|
|
361
|
+
input.addEventListener("change", async () => {
|
|
362
|
+
if (!input.files || input.files.length === 0) return;
|
|
363
|
+
const file = input.files[0];
|
|
364
|
+
const { from, to } = view.state.selection.main;
|
|
365
|
+
const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
|
|
366
|
+
const url = await uploadFileToServer(file, uploadConfig);
|
|
367
|
+
if (url) {
|
|
368
|
+
const insert = ``;
|
|
369
|
+
view.dispatch({ changes: { from, to, insert } });
|
|
370
|
+
}
|
|
371
|
+
input.remove();
|
|
372
|
+
});
|
|
373
|
+
document.body.appendChild(input);
|
|
374
|
+
input.click();
|
|
375
|
+
}
|
|
376
|
+
function wrapSelection(view, prefix, suffix) {
|
|
377
|
+
const { from, to, empty } = view.state.selection.main;
|
|
378
|
+
if (empty) return;
|
|
379
|
+
const selected = view.state.doc.sliceString(from, to);
|
|
380
|
+
view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
|
|
381
|
+
}
|
|
382
|
+
function toggleWrap(view, openTag, closeTag) {
|
|
383
|
+
const { from, to, empty } = view.state.selection.main;
|
|
384
|
+
const doc = view.state.doc;
|
|
385
|
+
if (!empty) {
|
|
386
|
+
const selected = doc.sliceString(from, to);
|
|
387
|
+
if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
|
|
388
|
+
const inner = selected.slice(openTag.length, -closeTag.length);
|
|
389
|
+
view.dispatch({ changes: { from, to, insert: inner } });
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
{
|
|
394
|
+
const cursorPos = empty ? from : to;
|
|
395
|
+
const line = doc.lineAt(cursorPos);
|
|
396
|
+
const lineText = doc.sliceString(line.from, line.to);
|
|
397
|
+
const openIdx = lineText.indexOf(openTag);
|
|
398
|
+
const closeIdx = lineText.lastIndexOf(closeTag);
|
|
399
|
+
if (openIdx >= 0 && closeIdx > openIdx) {
|
|
400
|
+
const absOpen = line.from + openIdx;
|
|
401
|
+
const absClose = line.from + closeIdx;
|
|
402
|
+
if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
|
|
403
|
+
const inner = doc.sliceString(absOpen + openTag.length, absClose);
|
|
404
|
+
view.dispatch({
|
|
405
|
+
changes: {
|
|
406
|
+
from: absOpen,
|
|
407
|
+
to: absClose + closeTag.length,
|
|
408
|
+
insert: inner
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
wrapSelection(view, openTag, closeTag);
|
|
416
|
+
}
|
|
417
|
+
|
|
390
418
|
// src/components/Editor/codemirrorExtensions.ts
|
|
419
|
+
var uploadConfigFacet = import_state2.Facet.define({
|
|
420
|
+
combine: (values) => values[values.length - 1]
|
|
421
|
+
});
|
|
422
|
+
var messageFacet = import_state2.Facet.define({
|
|
423
|
+
combine: (values) => values[values.length - 1]
|
|
424
|
+
});
|
|
425
|
+
var pasteHandler = import_view2.EditorView.domEventHandlers({
|
|
426
|
+
paste: (event, view) => {
|
|
427
|
+
var _a;
|
|
428
|
+
const files = (_a = event.clipboardData) == null ? void 0 : _a.files;
|
|
429
|
+
if (!files) return false;
|
|
430
|
+
const imageFiles = Array.from(files).filter((f) => f.type.startsWith("image/"));
|
|
431
|
+
if (imageFiles.length === 0) return false;
|
|
432
|
+
event.preventDefault();
|
|
433
|
+
const uploadConfig = view.state.facet(uploadConfigFacet);
|
|
434
|
+
const showMessage = view.state.facet(messageFacet);
|
|
435
|
+
if (!isUploadConfigured(uploadConfig)) {
|
|
436
|
+
showMessage == null ? void 0 : showMessage();
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
const { from, to } = view.state.selection.main;
|
|
440
|
+
let offset = 0;
|
|
441
|
+
for (const file of imageFiles) {
|
|
442
|
+
uploadFileToServer(file, uploadConfig).then((url) => {
|
|
443
|
+
if (url) {
|
|
444
|
+
const name = file.name.replace(/\.[^.]+$/, "");
|
|
445
|
+
const insert = ``;
|
|
446
|
+
view.dispatch({ changes: { from: from + offset, to: to + offset, insert } });
|
|
447
|
+
offset += insert.length;
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
});
|
|
391
454
|
var getCodeMirrorExtensions = (opts) => {
|
|
392
455
|
const extensions = [
|
|
393
456
|
(0, import_lang_markdown.markdown)(),
|
|
394
457
|
import_theme_one_dark.oneDark,
|
|
395
458
|
import_view2.keymap.of([...import_commands.defaultKeymap, import_commands.indentWithTab]),
|
|
396
459
|
import_view2.EditorView.lineWrapping,
|
|
460
|
+
pasteHandler,
|
|
397
461
|
import_view2.EditorView.baseTheme({
|
|
398
462
|
".cm-scroller": {
|
|
399
463
|
height: "100%",
|
|
@@ -438,26 +502,32 @@ var getCodeMirrorExtensions = (opts) => {
|
|
|
438
502
|
if (opts == null ? void 0 : opts.onEditContainer) {
|
|
439
503
|
extensions.push(containerEditCallback.of(opts.onEditContainer));
|
|
440
504
|
}
|
|
505
|
+
if (opts == null ? void 0 : opts.uploadConfig) {
|
|
506
|
+
extensions.push(uploadConfigFacet.of(opts.uploadConfig));
|
|
507
|
+
}
|
|
508
|
+
if (opts == null ? void 0 : opts.onMessageNoUploadConfig) {
|
|
509
|
+
extensions.push(messageFacet.of(opts.onMessageNoUploadConfig));
|
|
510
|
+
}
|
|
441
511
|
return extensions;
|
|
442
512
|
};
|
|
443
513
|
|
|
444
514
|
// src/components/Editor/index.tsx
|
|
445
|
-
var
|
|
446
|
-
function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }) {
|
|
515
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
516
|
+
function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer, uploadConfig, onMessageNoUploadConfig }) {
|
|
447
517
|
const editorRef = (0, import_react.useRef)(null);
|
|
448
518
|
const viewRef = (0, import_react.useRef)(null);
|
|
449
519
|
(0, import_react.useEffect)(() => {
|
|
450
520
|
if (!editorRef.current) return;
|
|
451
521
|
const extensions = [
|
|
452
522
|
import_codemirror.basicSetup,
|
|
453
|
-
...getCodeMirrorExtensions({ onEditContainer }),
|
|
523
|
+
...getCodeMirrorExtensions({ onEditContainer, uploadConfig, onMessageNoUploadConfig }),
|
|
454
524
|
import_codemirror.EditorView.updateListener.of((update) => {
|
|
455
525
|
if (update.docChanged) {
|
|
456
526
|
onChange(update.state.doc.toString());
|
|
457
527
|
}
|
|
458
528
|
})
|
|
459
529
|
];
|
|
460
|
-
const state =
|
|
530
|
+
const state = import_state3.EditorState.create({
|
|
461
531
|
doc: value,
|
|
462
532
|
extensions
|
|
463
533
|
});
|
|
@@ -483,14 +553,14 @@ function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }
|
|
|
483
553
|
});
|
|
484
554
|
}
|
|
485
555
|
}, [value]);
|
|
486
|
-
return /* @__PURE__ */ (0,
|
|
556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: editorRef, className: "editor" });
|
|
487
557
|
}
|
|
488
558
|
|
|
489
559
|
// src/components/Preview/index.tsx
|
|
490
560
|
var import_react2 = require("react");
|
|
491
561
|
var import_remons_render_markdown = __toESM(require("remons-render-markdown"), 1);
|
|
492
562
|
var import_dist = require("remons-render-markdown/dist/index.css");
|
|
493
|
-
var
|
|
563
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
494
564
|
var Preview = (0, import_react2.forwardRef)(({ content, previewOptions }, ref) => {
|
|
495
565
|
(0, import_react2.useEffect)(() => {
|
|
496
566
|
(0, import_remons_render_markdown.initHighlighter)({
|
|
@@ -498,24 +568,24 @@ var Preview = (0, import_react2.forwardRef)(({ content, previewOptions }, ref) =
|
|
|
498
568
|
...(previewOptions == null ? void 0 : previewOptions.languages) ? previewOptions == null ? void 0 : previewOptions.languages : {}
|
|
499
569
|
});
|
|
500
570
|
}, [previewOptions == null ? void 0 : previewOptions.languages]);
|
|
501
|
-
return /* @__PURE__ */ (0,
|
|
571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, className: "preview", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_remons_render_markdown.default, { content, ...previewOptions }) });
|
|
502
572
|
});
|
|
503
573
|
Preview.displayName = "Preview";
|
|
504
574
|
var Preview_default = Preview;
|
|
505
575
|
|
|
506
576
|
// src/components/Panel/SplitPanels.tsx
|
|
507
|
-
var
|
|
508
|
-
var
|
|
577
|
+
var import_antd = require("antd");
|
|
578
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
509
579
|
function SplitPanels({ leftPanel, rightPanel }) {
|
|
510
|
-
return /* @__PURE__ */ (0,
|
|
511
|
-
/* @__PURE__ */ (0,
|
|
512
|
-
/* @__PURE__ */ (0,
|
|
580
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd.Splitter, { className: "split-panels", children: [
|
|
581
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd.Splitter.Panel, { defaultSize: "50%", min: "20%", max: "80%", children: leftPanel }),
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd.Splitter.Panel, { children: rightPanel })
|
|
513
583
|
] });
|
|
514
584
|
}
|
|
515
585
|
|
|
516
586
|
// src/components/Toolbar/index.tsx
|
|
517
587
|
var import_react6 = require("react");
|
|
518
|
-
var
|
|
588
|
+
var import_antd4 = require("antd");
|
|
519
589
|
var import_icons4 = require("@ant-design/icons");
|
|
520
590
|
|
|
521
591
|
// src/components/Toolbar/hooks/useToolbar.ts
|
|
@@ -523,102 +593,8 @@ var import_react3 = require("react");
|
|
|
523
593
|
|
|
524
594
|
// src/components/Toolbar/config.ts
|
|
525
595
|
var import_icons = require("@ant-design/icons");
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
function insertAtCursor(view, before, after = "", placeholder = "") {
|
|
529
|
-
const { from, to } = view.state.selection.main;
|
|
530
|
-
const selectedText = view.state.doc.sliceString(from, to);
|
|
531
|
-
const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
|
|
532
|
-
view.dispatch({ changes: { from, to, insert } });
|
|
533
|
-
if (!selectedText && placeholder) {
|
|
534
|
-
view.dispatch({ selection: { anchor: from + before.length } });
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
function insertAtLineStart(view, text) {
|
|
538
|
-
const { from } = view.state.selection.main;
|
|
539
|
-
const line = view.state.doc.lineAt(from);
|
|
540
|
-
view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
|
|
541
|
-
view.dispatch({ selection: { anchor: line.from + text.length } });
|
|
542
|
-
}
|
|
543
|
-
async function uploadFileToServer(file) {
|
|
544
|
-
var _a;
|
|
545
|
-
const formData = new FormData();
|
|
546
|
-
formData.append("file", file);
|
|
547
|
-
const response = await fetch("https://remons.cn:3008/content/uploadMarkdownImg", {
|
|
548
|
-
method: "POST",
|
|
549
|
-
body: formData
|
|
550
|
-
});
|
|
551
|
-
if (!response.ok) return null;
|
|
552
|
-
const result = await response.json();
|
|
553
|
-
if (result.code === 200 && result.success && ((_a = result.data) == null ? void 0 : _a.path)) {
|
|
554
|
-
return `https://remons.cn:3008${result.data.path}`;
|
|
555
|
-
}
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
function uploadImage(view) {
|
|
559
|
-
const input = document.createElement("input");
|
|
560
|
-
input.type = "file";
|
|
561
|
-
input.accept = "image/*";
|
|
562
|
-
input.style.display = "none";
|
|
563
|
-
input.addEventListener("change", async () => {
|
|
564
|
-
if (!input.files || input.files.length === 0) return;
|
|
565
|
-
const file = input.files[0];
|
|
566
|
-
const { from, to } = view.state.selection.main;
|
|
567
|
-
const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
|
|
568
|
-
const url = await uploadFileToServer(file);
|
|
569
|
-
if (url) {
|
|
570
|
-
const insert = ``;
|
|
571
|
-
view.dispatch({ changes: { from, to, insert } });
|
|
572
|
-
}
|
|
573
|
-
input.remove();
|
|
574
|
-
});
|
|
575
|
-
document.body.appendChild(input);
|
|
576
|
-
input.click();
|
|
577
|
-
}
|
|
578
|
-
function wrapSelection(view, prefix, suffix) {
|
|
579
|
-
const { from, to, empty } = view.state.selection.main;
|
|
580
|
-
if (empty) return;
|
|
581
|
-
const selected = view.state.doc.sliceString(from, to);
|
|
582
|
-
view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
|
|
583
|
-
}
|
|
584
|
-
function toggleWrap(view, openTag, closeTag) {
|
|
585
|
-
const { from, to, empty } = view.state.selection.main;
|
|
586
|
-
const doc = view.state.doc;
|
|
587
|
-
if (!empty) {
|
|
588
|
-
const selected = doc.sliceString(from, to);
|
|
589
|
-
if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
|
|
590
|
-
const inner = selected.slice(openTag.length, -closeTag.length);
|
|
591
|
-
view.dispatch({ changes: { from, to, insert: inner } });
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
{
|
|
596
|
-
const cursorPos = empty ? from : to;
|
|
597
|
-
const line = doc.lineAt(cursorPos);
|
|
598
|
-
const lineText = doc.sliceString(line.from, line.to);
|
|
599
|
-
const openIdx = lineText.indexOf(openTag);
|
|
600
|
-
const closeIdx = lineText.lastIndexOf(closeTag);
|
|
601
|
-
if (openIdx >= 0 && closeIdx > openIdx) {
|
|
602
|
-
const absOpen = line.from + openIdx;
|
|
603
|
-
const absClose = line.from + closeIdx;
|
|
604
|
-
if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
|
|
605
|
-
const inner = doc.sliceString(absOpen + openTag.length, absClose);
|
|
606
|
-
view.dispatch({
|
|
607
|
-
changes: {
|
|
608
|
-
from: absOpen,
|
|
609
|
-
to: absClose + closeTag.length,
|
|
610
|
-
insert: inner
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
wrapSelection(view, openTag, closeTag);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// src/components/Toolbar/config.ts
|
|
621
|
-
function createToolbarConfig() {
|
|
596
|
+
function createToolbarConfig(options) {
|
|
597
|
+
const hasUploadConfig = isUploadConfigured(options);
|
|
622
598
|
return [
|
|
623
599
|
{
|
|
624
600
|
id: "heading",
|
|
@@ -643,7 +619,14 @@ function createToolbarConfig() {
|
|
|
643
619
|
{ id: "ol", label: "\u6709\u5E8F\u5217\u8868", icon: import_icons.OrderedListOutlined, handler: (v) => insertAtLineStart(v, "1. ") },
|
|
644
620
|
{ separatorBefore: true },
|
|
645
621
|
{ id: "link", label: "\u94FE\u63A5", icon: import_icons.LinkOutlined, handler: (v) => insertAtCursor(v, "[", "](url)", "\u6587\u672C") },
|
|
646
|
-
|
|
622
|
+
// 图片按钮:未配置上传时禁用并提示
|
|
623
|
+
{
|
|
624
|
+
id: "image",
|
|
625
|
+
label: hasUploadConfig ? "\u56FE\u7247" : "\u8BF7\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u5730\u5740",
|
|
626
|
+
icon: import_icons.PictureOutlined,
|
|
627
|
+
disabled: !hasUploadConfig,
|
|
628
|
+
handler: hasUploadConfig ? (v) => uploadImage(v, { uploadUrl: options == null ? void 0 : options.uploadUrl, onUploadImage: options == null ? void 0 : options.onUploadImage }) : void 0
|
|
629
|
+
},
|
|
647
630
|
{ separatorBefore: true },
|
|
648
631
|
{ id: "code", label: "\u884C\u5185\u4EE3\u7801", icon: import_icons.FieldStringOutlined, handler: (v) => wrapSelection(v, "`", "`") },
|
|
649
632
|
{
|
|
@@ -658,7 +641,6 @@ function createToolbarConfig() {
|
|
|
658
641
|
}
|
|
659
642
|
},
|
|
660
643
|
{ id: "quote", label: "\u5F15\u7528", icon: import_icons.BlockOutlined, handler: (v) => insertAtLineStart(v, "> ") },
|
|
661
|
-
{ id: "textContainer", label: "\u6587\u5B57\u5BB9\u5668", icon: import_icons.TagsOutlined, handler: (v) => toggleWrap(v, "<text-container>", "</text-container>") },
|
|
662
644
|
{
|
|
663
645
|
id: "hr",
|
|
664
646
|
label: "\u5206\u5272\u7EBF",
|
|
@@ -672,13 +654,20 @@ function createToolbarConfig() {
|
|
|
672
654
|
}
|
|
673
655
|
|
|
674
656
|
// src/components/Toolbar/hooks/useToolbar.ts
|
|
675
|
-
function useToolbar(editorView,
|
|
657
|
+
function useToolbar(editorView, options) {
|
|
658
|
+
const { extraItems, uploadConfig } = options || {};
|
|
676
659
|
const config = (0, import_react3.useMemo)(() => {
|
|
677
|
-
const base = createToolbarConfig();
|
|
678
|
-
return extraItems ? [...base,
|
|
679
|
-
}, [extraItems]);
|
|
660
|
+
const base = createToolbarConfig(uploadConfig);
|
|
661
|
+
return extraItems ? [...base, ...extraItems] : base;
|
|
662
|
+
}, [extraItems, uploadConfig]);
|
|
680
663
|
const handleClick = (0, import_react3.useCallback)((button) => {
|
|
681
|
-
if (
|
|
664
|
+
if (!editorView) return;
|
|
665
|
+
if (button.wrapTags) {
|
|
666
|
+
const [openTag, closeTag] = button.wrapTags;
|
|
667
|
+
toggleWrap(editorView, openTag, closeTag);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (button.handler) {
|
|
682
671
|
editorView.focus();
|
|
683
672
|
button.handler(editorView, editorView.state);
|
|
684
673
|
}
|
|
@@ -687,16 +676,16 @@ function useToolbar(editorView, extraItems) {
|
|
|
687
676
|
}
|
|
688
677
|
|
|
689
678
|
// src/components/Toolbar/renderers/ToolbarItemRenderer.tsx
|
|
690
|
-
var
|
|
679
|
+
var import_antd2 = require("antd");
|
|
691
680
|
var import_icons2 = require("@ant-design/icons");
|
|
692
|
-
var
|
|
681
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
693
682
|
function ToolbarItemRenderer({ item, onClick }) {
|
|
694
683
|
const Icon = item.icon;
|
|
695
684
|
if (item.children && item.children.length > 0) {
|
|
696
|
-
return /* @__PURE__ */ (0,
|
|
697
|
-
item.separatorBefore && /* @__PURE__ */ (0,
|
|
698
|
-
/* @__PURE__ */ (0,
|
|
699
|
-
|
|
685
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
686
|
+
item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-separator" }),
|
|
687
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Tooltip, { title: item.label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
688
|
+
import_antd2.Dropdown,
|
|
700
689
|
{
|
|
701
690
|
menu: {
|
|
702
691
|
items: item.children.map((child) => {
|
|
@@ -704,10 +693,10 @@ function ToolbarItemRenderer({ item, onClick }) {
|
|
|
704
693
|
return {
|
|
705
694
|
type: "item",
|
|
706
695
|
key: (_a = child.id) != null ? _a : "",
|
|
707
|
-
label: /* @__PURE__ */ (0,
|
|
696
|
+
label: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { onClick: () => onClick(child), children: [
|
|
708
697
|
child.icon && (() => {
|
|
709
698
|
const ChildIcon = child.icon;
|
|
710
|
-
return /* @__PURE__ */ (0,
|
|
699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChildIcon, {});
|
|
711
700
|
})(),
|
|
712
701
|
" ",
|
|
713
702
|
child.label
|
|
@@ -716,34 +705,35 @@ function ToolbarItemRenderer({ item, onClick }) {
|
|
|
716
705
|
})
|
|
717
706
|
},
|
|
718
707
|
placement: "bottomLeft",
|
|
719
|
-
children: /* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
721
|
-
/* @__PURE__ */ (0,
|
|
708
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_antd2.Button, { className: "toolbar-button", type: "text", size: "small", children: [
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, {}),
|
|
710
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.DownOutlined, { style: { marginLeft: 2, fontSize: 10 } })
|
|
722
711
|
] })
|
|
723
712
|
}
|
|
724
|
-
) })
|
|
713
|
+
) }) })
|
|
725
714
|
] });
|
|
726
715
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
/* @__PURE__ */ (0,
|
|
730
|
-
|
|
716
|
+
const isDisabled = item.disabled;
|
|
717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
718
|
+
item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-separator" }),
|
|
719
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Tooltip, { title: item.label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
720
|
+
import_antd2.Button,
|
|
731
721
|
{
|
|
732
722
|
id: item.id,
|
|
733
723
|
className: "toolbar-button",
|
|
734
724
|
type: "text",
|
|
735
725
|
size: "small",
|
|
736
|
-
|
|
737
|
-
onClick: () => onClick(item),
|
|
738
|
-
children: /* @__PURE__ */ (0,
|
|
726
|
+
disabled: isDisabled,
|
|
727
|
+
onClick: () => !isDisabled && onClick(item),
|
|
728
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, {})
|
|
739
729
|
}
|
|
740
|
-
) })
|
|
730
|
+
) }) })
|
|
741
731
|
] });
|
|
742
732
|
}
|
|
743
733
|
|
|
744
734
|
// src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
|
|
745
735
|
var import_react5 = __toESM(require("react"), 1);
|
|
746
|
-
var
|
|
736
|
+
var import_antd3 = require("antd");
|
|
747
737
|
var import_icons3 = require("@ant-design/icons");
|
|
748
738
|
|
|
749
739
|
// src/components/Toolbar/renderers/TablePicker/index.tsx
|
|
@@ -753,24 +743,27 @@ var import_react4 = require("react");
|
|
|
753
743
|
var index_module_default = {};
|
|
754
744
|
|
|
755
745
|
// src/components/Toolbar/renderers/TablePicker/index.tsx
|
|
756
|
-
var
|
|
757
|
-
var MAX_ROW =
|
|
758
|
-
var MAX_COL =
|
|
759
|
-
var REFRESH_TIME = 200;
|
|
746
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
747
|
+
var MAX_ROW = 8;
|
|
748
|
+
var MAX_COL = 8;
|
|
760
749
|
function TablePicker({ onSelect }) {
|
|
761
750
|
const [hoverSize, setHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
|
|
762
|
-
const [prevHoverSize, setPrevHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
|
|
763
751
|
const [confirmedSize, setConfirmedSize] = (0, import_react4.useState)({ row: 0, col: 0 });
|
|
764
752
|
const maxColRef = (0, import_react4.useRef)(0);
|
|
753
|
+
const prevRowRef = (0, import_react4.useRef)(0);
|
|
765
754
|
const timerIdRef = (0, import_react4.useRef)(null);
|
|
766
755
|
const refreshTimerRef = (0, import_react4.useRef)(null);
|
|
767
756
|
const commitRef = (0, import_react4.useRef)(null);
|
|
768
|
-
const clearTimer = (0, import_react4.useCallback)((
|
|
757
|
+
const clearTimer = (0, import_react4.useCallback)(() => {
|
|
769
758
|
if (timerIdRef.current) {
|
|
759
|
+
clearTimeout(timerIdRef.current);
|
|
770
760
|
timerIdRef.current = null;
|
|
771
761
|
}
|
|
772
|
-
|
|
773
|
-
|
|
762
|
+
}, []);
|
|
763
|
+
const clearFlash = (0, import_react4.useCallback)(() => {
|
|
764
|
+
if (refreshTimerRef.current) {
|
|
765
|
+
clearInterval(refreshTimerRef.current);
|
|
766
|
+
refreshTimerRef.current = null;
|
|
774
767
|
}
|
|
775
768
|
}, []);
|
|
776
769
|
(0, import_react4.useEffect)(
|
|
@@ -781,25 +774,21 @@ function TablePicker({ onSelect }) {
|
|
|
781
774
|
[]
|
|
782
775
|
);
|
|
783
776
|
function extend(col, row) {
|
|
777
|
+
if (row < prevRowRef.current || col < maxColRef.current) {
|
|
778
|
+
maxColRef.current = 0;
|
|
779
|
+
}
|
|
784
780
|
if (col > maxColRef.current) {
|
|
785
781
|
maxColRef.current = col;
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
setConfirmedSize({ row, col: maxColRef.current });
|
|
792
|
-
}
|
|
793
|
-
if (row === 1) {
|
|
794
|
-
commitRef.current = { cols: col, rows: 1 };
|
|
795
|
-
setConfirmedSize({ row: 1, col });
|
|
796
|
-
}
|
|
782
|
+
}
|
|
783
|
+
setHoverSize({ row, col });
|
|
784
|
+
setConfirmedSize({ row, col: maxColRef.current });
|
|
785
|
+
if (row === 1) {
|
|
786
|
+
commitRef.current = { cols: maxColRef.current, rows: 1 };
|
|
797
787
|
}
|
|
798
788
|
}
|
|
799
789
|
function flash(col, dir, limit, finalCallback) {
|
|
800
790
|
let current = dir === 1 ? 1 : limit;
|
|
801
791
|
const step = dir === 1 ? 1 : -1;
|
|
802
|
-
let timer = 0;
|
|
803
792
|
refreshTimerRef.current = setInterval(() => {
|
|
804
793
|
setHoverSize({ row: current, col });
|
|
805
794
|
if (current > confirmedSize.row || dir === -1) {
|
|
@@ -808,45 +797,43 @@ function TablePicker({ onSelect }) {
|
|
|
808
797
|
current += step;
|
|
809
798
|
if (dir === 1 && current > limit || dir === -1 && current < 1) {
|
|
810
799
|
clearInterval(refreshTimerRef.current);
|
|
811
|
-
|
|
812
|
-
|
|
800
|
+
refreshTimerRef.current = null;
|
|
801
|
+
const finalRow = current - step;
|
|
802
|
+
setHoverSize({ row: finalRow, col });
|
|
803
|
+
finalCallback(col, finalRow);
|
|
813
804
|
}
|
|
814
805
|
}, 50);
|
|
815
|
-
clearTimeout(timer);
|
|
816
|
-
return () => {
|
|
817
|
-
clearInterval(refreshTimerRef.current);
|
|
818
|
-
};
|
|
819
806
|
}
|
|
820
807
|
function onCellEnterCheck(index) {
|
|
821
808
|
clearTimer();
|
|
809
|
+
clearFlash();
|
|
822
810
|
const row = Math.floor(index / MAX_COL) + 1;
|
|
823
811
|
const col = index % MAX_COL + 1;
|
|
824
|
-
const
|
|
825
|
-
|
|
812
|
+
const prevRow = prevRowRef.current;
|
|
813
|
+
const isJump = Math.abs(row - prevRow) > 1;
|
|
814
|
+
const isDown = row > prevRow;
|
|
815
|
+
prevRowRef.current = row;
|
|
816
|
+
if (!isJump) {
|
|
826
817
|
extend(col, row);
|
|
827
|
-
} else {
|
|
828
|
-
if (
|
|
818
|
+
} else if (isDown) {
|
|
819
|
+
if (col > maxColRef.current || maxColRef.current >= MAX_COL) {
|
|
829
820
|
const finalRow = Math.min(row, MAX_ROW);
|
|
830
821
|
flash(col, 1, finalRow, (c, r) => {
|
|
831
822
|
setConfirmedSize({ row: r, col: c });
|
|
832
823
|
});
|
|
833
|
-
} else
|
|
834
|
-
|
|
835
|
-
setConfirmedSize({ row: r, col: c });
|
|
836
|
-
if (r === 1) {
|
|
837
|
-
commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
|
|
838
|
-
setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
|
|
839
|
-
}
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
if (col >= MAX_COL && row >= prevHoverSize.row) {
|
|
844
|
-
if (row !== prevHoverSize.row) {
|
|
845
|
-
maxColRef.current = 0;
|
|
846
|
-
setPrevHoverSize({ row: 0, col: 0 });
|
|
824
|
+
} else {
|
|
825
|
+
extend(col, row);
|
|
847
826
|
}
|
|
827
|
+
} else {
|
|
828
|
+
maxColRef.current = 0;
|
|
829
|
+
flash(col, -1, row, (c, r) => {
|
|
830
|
+
setConfirmedSize({ row: r, col: c });
|
|
831
|
+
if (r === 1) {
|
|
832
|
+
commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
|
|
833
|
+
setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
|
|
834
|
+
}
|
|
835
|
+
});
|
|
848
836
|
}
|
|
849
|
-
setPrevHoverSize({ row, col });
|
|
850
837
|
}
|
|
851
838
|
function onMouseDown() {
|
|
852
839
|
const size = commitRef.current || { cols: maxColRef.current, rows: confirmedSize.row };
|
|
@@ -854,13 +841,13 @@ function TablePicker({ onSelect }) {
|
|
|
854
841
|
onSelect(size);
|
|
855
842
|
}
|
|
856
843
|
}
|
|
857
|
-
return /* @__PURE__ */ (0,
|
|
844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
858
845
|
"div",
|
|
859
846
|
{
|
|
860
847
|
className: index_module_default.container,
|
|
861
848
|
onMouseDown,
|
|
862
849
|
children: [
|
|
863
|
-
/* @__PURE__ */ (0,
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
864
851
|
"div",
|
|
865
852
|
{
|
|
866
853
|
className: index_module_default.grid,
|
|
@@ -869,7 +856,7 @@ function TablePicker({ onSelect }) {
|
|
|
869
856
|
const row = Math.floor(i / MAX_COL) + 1;
|
|
870
857
|
const col = i % MAX_COL + 1;
|
|
871
858
|
const isSelected = row <= hoverSize.row && col <= maxColRef.current;
|
|
872
|
-
return /* @__PURE__ */ (0,
|
|
859
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
873
860
|
"div",
|
|
874
861
|
{
|
|
875
862
|
className: `${index_module_default.cell} ${isSelected ? index_module_default.cellActive : ""}`,
|
|
@@ -880,7 +867,7 @@ function TablePicker({ onSelect }) {
|
|
|
880
867
|
})
|
|
881
868
|
}
|
|
882
869
|
),
|
|
883
|
-
/* @__PURE__ */ (0,
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: index_module_default.label, children: [
|
|
884
871
|
hoverSize.row,
|
|
885
872
|
" x ",
|
|
886
873
|
maxColRef.current || 1
|
|
@@ -891,7 +878,7 @@ function TablePicker({ onSelect }) {
|
|
|
891
878
|
}
|
|
892
879
|
|
|
893
880
|
// src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
|
|
894
|
-
var
|
|
881
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
895
882
|
function TableDropdown({ editorView }) {
|
|
896
883
|
const [open, setOpen] = import_react5.default.useState(false);
|
|
897
884
|
const openCountRef = (0, import_react5.useRef)(0);
|
|
@@ -910,15 +897,15 @@ ${dataRows}
|
|
|
910
897
|
const { from } = editorView.state.selection.main;
|
|
911
898
|
editorView.dispatch({ changes: { from, insert: table } });
|
|
912
899
|
}, [editorView]);
|
|
913
|
-
return /* @__PURE__ */ (0,
|
|
914
|
-
|
|
900
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
901
|
+
import_antd3.Dropdown,
|
|
915
902
|
{
|
|
916
903
|
trigger: ["click"],
|
|
917
904
|
open,
|
|
918
905
|
onOpenChange: setOpen,
|
|
919
906
|
popupRender: () => {
|
|
920
907
|
openCountRef.current++;
|
|
921
|
-
return /* @__PURE__ */ (0,
|
|
908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { padding: 4 }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
922
909
|
TablePicker,
|
|
923
910
|
{
|
|
924
911
|
onSelect: (size) => {
|
|
@@ -930,44 +917,41 @@ ${dataRows}
|
|
|
930
917
|
) });
|
|
931
918
|
},
|
|
932
919
|
placement: "bottomLeft",
|
|
933
|
-
children: /* @__PURE__ */ (0,
|
|
920
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_antd3.Button, { className: "toolbar-button", type: "text", size: "small", title: "\u8868\u683C", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.TableOutlined, {}) })
|
|
934
921
|
}
|
|
935
922
|
);
|
|
936
923
|
}
|
|
937
924
|
|
|
938
925
|
// src/components/Toolbar/index.tsx
|
|
939
|
-
var
|
|
940
|
-
function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
|
|
941
|
-
const { config, handleClick } = useToolbar(editorView, extraItems);
|
|
926
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
927
|
+
function Toolbar({ editorView, fullscreen = true, onToggleFullscreen, extraItems, uploadConfig }) {
|
|
928
|
+
const { config, handleClick } = useToolbar(editorView, { extraItems, uploadConfig });
|
|
942
929
|
const renderFullscreen = (0, import_react6.useCallback)(() => {
|
|
943
930
|
if (!onToggleFullscreen) return null;
|
|
944
931
|
const Icon = fullscreen ? import_icons4.FullscreenExitOutlined : import_icons4.FullscreenOutlined;
|
|
945
|
-
return /* @__PURE__ */ (0,
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
)
|
|
960
|
-
] });
|
|
932
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
933
|
+
import_antd4.Button,
|
|
934
|
+
{
|
|
935
|
+
className: "toolbar-button",
|
|
936
|
+
type: "text",
|
|
937
|
+
size: "small",
|
|
938
|
+
title: fullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
|
|
939
|
+
onClick: () => {
|
|
940
|
+
onToggleFullscreen();
|
|
941
|
+
},
|
|
942
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, {})
|
|
943
|
+
}
|
|
944
|
+
);
|
|
961
945
|
}, [fullscreen, onToggleFullscreen]);
|
|
962
946
|
function isSeparator(item) {
|
|
963
947
|
return !!(!item.id && item.separatorBefore);
|
|
964
948
|
}
|
|
965
|
-
return /* @__PURE__ */ (0,
|
|
949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "toolbar", children: [
|
|
966
950
|
config.map((item, idx) => {
|
|
967
951
|
if (isSeparator(item)) {
|
|
968
|
-
return /* @__PURE__ */ (0,
|
|
952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "toolbar-separator" }, `sep-${idx}`);
|
|
969
953
|
}
|
|
970
|
-
return /* @__PURE__ */ (0,
|
|
954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
971
955
|
ToolbarItemRenderer,
|
|
972
956
|
{
|
|
973
957
|
item,
|
|
@@ -976,16 +960,15 @@ function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
|
|
|
976
960
|
item.id
|
|
977
961
|
);
|
|
978
962
|
}),
|
|
979
|
-
/* @__PURE__ */ (0,
|
|
980
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TableDropdown, { editorView }),
|
|
963
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableDropdown, { editorView }),
|
|
981
964
|
renderFullscreen()
|
|
982
965
|
] });
|
|
983
966
|
}
|
|
984
967
|
|
|
985
968
|
// src/components/Dialog/EditDialog.tsx
|
|
986
969
|
var import_react7 = require("react");
|
|
987
|
-
var
|
|
988
|
-
var
|
|
970
|
+
var import_antd5 = require("antd");
|
|
971
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
989
972
|
function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
990
973
|
const schema = componentRegistry_default.get(typeName);
|
|
991
974
|
const [formProps, setFormProps] = (0, import_react7.useState)({});
|
|
@@ -1013,8 +996,8 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
|
1013
996
|
if (!schema) {
|
|
1014
997
|
return null;
|
|
1015
998
|
}
|
|
1016
|
-
return /* @__PURE__ */ (0,
|
|
1017
|
-
|
|
999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1000
|
+
import_antd5.Modal,
|
|
1018
1001
|
{
|
|
1019
1002
|
title: `${schema.label} \u5C5E\u6027\u7F16\u8F91`,
|
|
1020
1003
|
open: visible,
|
|
@@ -1054,36 +1037,11 @@ ${propsLine}
|
|
|
1054
1037
|
|
|
1055
1038
|
// src/MarkdownEditor.tsx
|
|
1056
1039
|
var import_icons5 = require("@ant-design/icons");
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
onChange,
|
|
1062
|
-
showPreview = true,
|
|
1063
|
-
fullscreen: fullscreenProp = false,
|
|
1064
|
-
previewOptions
|
|
1065
|
-
}, ref) {
|
|
1066
|
-
const [content, setContent] = (0, import_react8.useState)(value != null ? value : defaultValue);
|
|
1067
|
-
const [editorView, setEditorView] = (0, import_react8.useState)(null);
|
|
1068
|
-
const scrollDOMRef = (0, import_react8.useRef)(null);
|
|
1069
|
-
const previewElRef = (0, import_react8.useRef)(null);
|
|
1040
|
+
|
|
1041
|
+
// src/hooks/useFullscreen.ts
|
|
1042
|
+
var import_react8 = require("react");
|
|
1043
|
+
function useFullscreen() {
|
|
1070
1044
|
const [fullscreen, setFullscreen] = (0, import_react8.useState)(false);
|
|
1071
|
-
const [dialogVisible, setDialogVisible] = (0, import_react8.useState)(false);
|
|
1072
|
-
const [dialogTypeName, setDialogTypeName] = (0, import_react8.useState)("");
|
|
1073
|
-
const [dialogProps, setDialogProps] = (0, import_react8.useState)({});
|
|
1074
|
-
const [dialogPropsRange, setDialogPropsRange] = (0, import_react8.useState)([0, 0]);
|
|
1075
|
-
(0, import_react8.useEffect)(() => {
|
|
1076
|
-
if (value !== void 0) {
|
|
1077
|
-
setContent(value);
|
|
1078
|
-
}
|
|
1079
|
-
}, [value]);
|
|
1080
|
-
const internalSetContent = (0, import_react8.useCallback)((newContent) => {
|
|
1081
|
-
setContent(newContent);
|
|
1082
|
-
onChange == null ? void 0 : onChange(newContent);
|
|
1083
|
-
}, [onChange]);
|
|
1084
|
-
(0, import_react8.useImperativeHandle)(ref, () => ({
|
|
1085
|
-
getContent: () => content
|
|
1086
|
-
}), [content]);
|
|
1087
1045
|
const toggleFullscreen = (0, import_react8.useCallback)(() => {
|
|
1088
1046
|
if (document.fullscreenElement) {
|
|
1089
1047
|
document.exitFullscreen().catch((e) => {
|
|
@@ -1110,40 +1068,29 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1110
1068
|
document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
|
|
1111
1069
|
};
|
|
1112
1070
|
}, []);
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
icon: () => iconNode,
|
|
1124
|
-
handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
|
|
1125
|
-
};
|
|
1126
|
-
});
|
|
1127
|
-
const handleScrollReady = (0, import_react8.useCallback)((scrollDOM) => {
|
|
1128
|
-
scrollDOMRef.current = scrollDOM;
|
|
1129
|
-
}, []);
|
|
1130
|
-
(0, import_react8.useEffect)(() => {
|
|
1131
|
-
const source = scrollDOMRef.current;
|
|
1132
|
-
const target = previewElRef.current;
|
|
1071
|
+
return { fullscreen, toggleFullscreen };
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/hooks/useScrollSync.ts
|
|
1075
|
+
var import_react9 = require("react");
|
|
1076
|
+
function useScrollSync(sourceRef, targetRef) {
|
|
1077
|
+
const rafIdRef = (0, import_react9.useRef)(null);
|
|
1078
|
+
(0, import_react9.useEffect)(() => {
|
|
1079
|
+
const source = sourceRef.current;
|
|
1080
|
+
const target = targetRef.current;
|
|
1133
1081
|
if (!source || !target) return;
|
|
1134
|
-
let rafId = null;
|
|
1135
1082
|
const listener = () => {
|
|
1136
|
-
if (
|
|
1137
|
-
|
|
1138
|
-
const s =
|
|
1139
|
-
const t =
|
|
1083
|
+
if (rafIdRef.current) return;
|
|
1084
|
+
rafIdRef.current = requestAnimationFrame(() => {
|
|
1085
|
+
const s = sourceRef.current;
|
|
1086
|
+
const t = targetRef.current;
|
|
1140
1087
|
if (!s || !t) {
|
|
1141
|
-
|
|
1088
|
+
rafIdRef.current = null;
|
|
1142
1089
|
return;
|
|
1143
1090
|
}
|
|
1144
1091
|
const sourceScrollHeight = s.scrollHeight - s.clientHeight;
|
|
1145
1092
|
if (sourceScrollHeight <= 0) {
|
|
1146
|
-
|
|
1093
|
+
rafIdRef.current = null;
|
|
1147
1094
|
return;
|
|
1148
1095
|
}
|
|
1149
1096
|
const ratio = s.scrollTop / sourceScrollHeight;
|
|
@@ -1151,60 +1098,115 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1151
1098
|
if (targetScrollHeight > 0) {
|
|
1152
1099
|
t.scrollTop = ratio * targetScrollHeight;
|
|
1153
1100
|
}
|
|
1154
|
-
|
|
1101
|
+
rafIdRef.current = null;
|
|
1155
1102
|
});
|
|
1156
1103
|
};
|
|
1157
1104
|
source.addEventListener("scroll", listener, { passive: true });
|
|
1158
1105
|
return () => {
|
|
1159
1106
|
source.removeEventListener("scroll", listener);
|
|
1160
|
-
if (
|
|
1107
|
+
if (rafIdRef.current) cancelAnimationFrame(rafIdRef.current);
|
|
1108
|
+
};
|
|
1109
|
+
}, [sourceRef, targetRef]);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// src/MarkdownEditor.tsx
|
|
1113
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1114
|
+
var MarkdownEditor_default = (0, import_react10.forwardRef)(function MarkdownEditor({
|
|
1115
|
+
defaultValue = "",
|
|
1116
|
+
value,
|
|
1117
|
+
onChange,
|
|
1118
|
+
showPreview = true,
|
|
1119
|
+
fullscreen: fullscreenProp = false,
|
|
1120
|
+
previewOptions,
|
|
1121
|
+
uploadConfig
|
|
1122
|
+
}, ref) {
|
|
1123
|
+
const [content, setContent] = (0, import_react10.useState)(value != null ? value : defaultValue);
|
|
1124
|
+
const [editorView, setEditorView] = (0, import_react10.useState)(null);
|
|
1125
|
+
const scrollDOMRef = (0, import_react10.useRef)(null);
|
|
1126
|
+
const previewElRef = (0, import_react10.useRef)(null);
|
|
1127
|
+
const { fullscreen, toggleFullscreen } = useFullscreen();
|
|
1128
|
+
useScrollSync(scrollDOMRef, previewElRef);
|
|
1129
|
+
const [dialogVisible, setDialogVisible] = (0, import_react10.useState)(false);
|
|
1130
|
+
const [dialogTypeName, setDialogTypeName] = (0, import_react10.useState)("");
|
|
1131
|
+
const [dialogProps, setDialogProps] = (0, import_react10.useState)({});
|
|
1132
|
+
const [dialogPropsRange, setDialogPropsRange] = (0, import_react10.useState)([0, 0]);
|
|
1133
|
+
(0, import_react10.useEffect)(() => {
|
|
1134
|
+
if (value !== void 0) {
|
|
1135
|
+
setContent(value);
|
|
1136
|
+
}
|
|
1137
|
+
}, [value]);
|
|
1138
|
+
const internalSetContent = (0, import_react10.useCallback)((newContent) => {
|
|
1139
|
+
setContent(newContent);
|
|
1140
|
+
onChange == null ? void 0 : onChange(newContent);
|
|
1141
|
+
}, [onChange]);
|
|
1142
|
+
(0, import_react10.useImperativeHandle)(ref, () => ({
|
|
1143
|
+
getContent: () => content
|
|
1144
|
+
}), [content]);
|
|
1145
|
+
const extraToolbarItems = componentRegistry_default.getAll().map((schema) => {
|
|
1146
|
+
var _a, _b, _c;
|
|
1147
|
+
const defaultProps = {};
|
|
1148
|
+
for (const field of (_a = schema == null ? void 0 : schema.fields) != null ? _a : []) {
|
|
1149
|
+
defaultProps[field.key] = (_b = field.defaultValue) != null ? _b : "";
|
|
1150
|
+
}
|
|
1151
|
+
const iconNode = (_c = schema.icon) != null ? _c : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_icons5.ReadOutlined, {});
|
|
1152
|
+
return {
|
|
1153
|
+
id: `container-${schema.name}`,
|
|
1154
|
+
label: schema.label,
|
|
1155
|
+
icon: () => iconNode,
|
|
1156
|
+
handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
|
|
1161
1157
|
};
|
|
1162
|
-
}
|
|
1163
|
-
const
|
|
1158
|
+
});
|
|
1159
|
+
const handleScrollReady = (0, import_react10.useCallback)((scrollDOM) => {
|
|
1160
|
+
scrollDOMRef.current = scrollDOM;
|
|
1161
|
+
}, []);
|
|
1162
|
+
const handlePreviewRef = (0, import_react10.useCallback)((node) => {
|
|
1164
1163
|
previewElRef.current = node;
|
|
1165
1164
|
}, []);
|
|
1166
|
-
const handleViewReady = (0,
|
|
1165
|
+
const handleViewReady = (0, import_react10.useCallback)((view) => {
|
|
1167
1166
|
setEditorView(view);
|
|
1168
1167
|
}, []);
|
|
1169
|
-
const handleEditContainer = (0,
|
|
1168
|
+
const handleEditContainer = (0, import_react10.useCallback)((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
|
|
1170
1169
|
setDialogTypeName(typeName);
|
|
1171
1170
|
setDialogProps({ ...currentProps });
|
|
1172
1171
|
setDialogPropsRange([propsLineFrom, propsLineTo]);
|
|
1173
1172
|
setDialogVisible(true);
|
|
1174
1173
|
}, []);
|
|
1175
|
-
const handleDialogSave = (0,
|
|
1174
|
+
const handleDialogSave = (0, import_react10.useCallback)((newProps) => {
|
|
1176
1175
|
setDialogVisible(false);
|
|
1177
1176
|
if (editorView) {
|
|
1178
1177
|
updateContainerProps(editorView, dialogPropsRange[0], dialogPropsRange[1], newProps);
|
|
1179
1178
|
}
|
|
1180
1179
|
}, [editorView, dialogPropsRange]);
|
|
1181
|
-
const handleDialogClose = (0,
|
|
1180
|
+
const handleDialogClose = (0, import_react10.useCallback)(() => {
|
|
1182
1181
|
setDialogVisible(false);
|
|
1183
1182
|
}, []);
|
|
1184
|
-
return /* @__PURE__ */ (0,
|
|
1185
|
-
/* @__PURE__ */ (0,
|
|
1183
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: fullscreen ? "app fullscreen" : "app", children: [
|
|
1184
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1186
1185
|
Toolbar,
|
|
1187
1186
|
{
|
|
1188
1187
|
editorView,
|
|
1189
1188
|
fullscreen,
|
|
1190
1189
|
onToggleFullscreen: fullscreenProp ? toggleFullscreen : void 0,
|
|
1191
|
-
extraItems: extraToolbarItems
|
|
1190
|
+
extraItems: extraToolbarItems,
|
|
1191
|
+
uploadConfig
|
|
1192
1192
|
}
|
|
1193
1193
|
),
|
|
1194
|
-
showPreview ? /* @__PURE__ */ (0,
|
|
1194
|
+
showPreview ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1195
1195
|
SplitPanels,
|
|
1196
1196
|
{
|
|
1197
|
-
leftPanel: /* @__PURE__ */ (0,
|
|
1197
|
+
leftPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1198
1198
|
Editor,
|
|
1199
1199
|
{
|
|
1200
1200
|
value: content,
|
|
1201
1201
|
onChange: internalSetContent,
|
|
1202
1202
|
onScrollReady: handleScrollReady,
|
|
1203
1203
|
onViewReady: handleViewReady,
|
|
1204
|
-
onEditContainer: handleEditContainer
|
|
1204
|
+
onEditContainer: handleEditContainer,
|
|
1205
|
+
uploadConfig,
|
|
1206
|
+
onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
|
|
1205
1207
|
}
|
|
1206
1208
|
),
|
|
1207
|
-
rightPanel: /* @__PURE__ */ (0,
|
|
1209
|
+
rightPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1208
1210
|
Preview_default,
|
|
1209
1211
|
{
|
|
1210
1212
|
content,
|
|
@@ -1213,17 +1215,19 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1213
1215
|
}
|
|
1214
1216
|
)
|
|
1215
1217
|
}
|
|
1216
|
-
) : /* @__PURE__ */ (0,
|
|
1218
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1217
1219
|
Editor,
|
|
1218
1220
|
{
|
|
1219
1221
|
value: content,
|
|
1220
1222
|
onChange: internalSetContent,
|
|
1221
1223
|
onScrollReady: handleScrollReady,
|
|
1222
1224
|
onViewReady: handleViewReady,
|
|
1223
|
-
onEditContainer: handleEditContainer
|
|
1225
|
+
onEditContainer: handleEditContainer,
|
|
1226
|
+
uploadConfig,
|
|
1227
|
+
onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
|
|
1224
1228
|
}
|
|
1225
1229
|
),
|
|
1226
|
-
/* @__PURE__ */ (0,
|
|
1230
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1227
1231
|
EditDialog,
|
|
1228
1232
|
{
|
|
1229
1233
|
visible: dialogVisible,
|
|
@@ -1237,10 +1241,10 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1237
1241
|
});
|
|
1238
1242
|
|
|
1239
1243
|
// src/hooks/useRegisterToolbar.ts
|
|
1240
|
-
var
|
|
1244
|
+
var import_react11 = require("react");
|
|
1241
1245
|
function useRegisterToolbar(schemas) {
|
|
1242
1246
|
const schemaList = Array.isArray(schemas) ? schemas : [schemas];
|
|
1243
|
-
(0,
|
|
1247
|
+
(0, import_react11.useEffect)(() => {
|
|
1244
1248
|
schemaList.forEach((schema) => {
|
|
1245
1249
|
componentRegistry_default.register(schema.name, schema);
|
|
1246
1250
|
});
|