remons-markdown-editor 1.0.0-beta.2 → 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 +386 -377
- 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 +84 -25
- package/dist/index.d.ts +84 -25
- package/dist/index.js +381 -373
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/readme.md +164 -7
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,36 +553,39 @@ 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
|
-
var import_remons_markdown_plugins = require("remons-markdown-plugins");
|
|
492
561
|
var import_remons_render_markdown = __toESM(require("remons-render-markdown"), 1);
|
|
493
|
-
var import_style = require("remons-markdown-plugins/style.css");
|
|
494
562
|
var import_dist = require("remons-render-markdown/dist/index.css");
|
|
495
|
-
var
|
|
496
|
-
(0,
|
|
497
|
-
|
|
498
|
-
|
|
563
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
564
|
+
var Preview = (0, import_react2.forwardRef)(({ content, previewOptions }, ref) => {
|
|
565
|
+
(0, import_react2.useEffect)(() => {
|
|
566
|
+
(0, import_remons_render_markdown.initHighlighter)({
|
|
567
|
+
...import_remons_render_markdown.languagesCommon,
|
|
568
|
+
...(previewOptions == null ? void 0 : previewOptions.languages) ? previewOptions == null ? void 0 : previewOptions.languages : {}
|
|
569
|
+
});
|
|
570
|
+
}, [previewOptions == null ? void 0 : previewOptions.languages]);
|
|
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 }) });
|
|
499
572
|
});
|
|
500
573
|
Preview.displayName = "Preview";
|
|
501
574
|
var Preview_default = Preview;
|
|
502
575
|
|
|
503
576
|
// src/components/Panel/SplitPanels.tsx
|
|
504
|
-
var
|
|
505
|
-
var
|
|
577
|
+
var import_antd = require("antd");
|
|
578
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
506
579
|
function SplitPanels({ leftPanel, rightPanel }) {
|
|
507
|
-
return /* @__PURE__ */ (0,
|
|
508
|
-
/* @__PURE__ */ (0,
|
|
509
|
-
/* @__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 })
|
|
510
583
|
] });
|
|
511
584
|
}
|
|
512
585
|
|
|
513
586
|
// src/components/Toolbar/index.tsx
|
|
514
587
|
var import_react6 = require("react");
|
|
515
|
-
var
|
|
588
|
+
var import_antd4 = require("antd");
|
|
516
589
|
var import_icons4 = require("@ant-design/icons");
|
|
517
590
|
|
|
518
591
|
// src/components/Toolbar/hooks/useToolbar.ts
|
|
@@ -520,102 +593,8 @@ var import_react3 = require("react");
|
|
|
520
593
|
|
|
521
594
|
// src/components/Toolbar/config.ts
|
|
522
595
|
var import_icons = require("@ant-design/icons");
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
function insertAtCursor(view, before, after = "", placeholder = "") {
|
|
526
|
-
const { from, to } = view.state.selection.main;
|
|
527
|
-
const selectedText = view.state.doc.sliceString(from, to);
|
|
528
|
-
const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
|
|
529
|
-
view.dispatch({ changes: { from, to, insert } });
|
|
530
|
-
if (!selectedText && placeholder) {
|
|
531
|
-
view.dispatch({ selection: { anchor: from + before.length } });
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
function insertAtLineStart(view, text) {
|
|
535
|
-
const { from } = view.state.selection.main;
|
|
536
|
-
const line = view.state.doc.lineAt(from);
|
|
537
|
-
view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
|
|
538
|
-
view.dispatch({ selection: { anchor: line.from + text.length } });
|
|
539
|
-
}
|
|
540
|
-
async function uploadFileToServer(file) {
|
|
541
|
-
var _a;
|
|
542
|
-
const formData = new FormData();
|
|
543
|
-
formData.append("file", file);
|
|
544
|
-
const response = await fetch("https://remons.cn:3008/content/uploadMarkdownImg", {
|
|
545
|
-
method: "POST",
|
|
546
|
-
body: formData
|
|
547
|
-
});
|
|
548
|
-
if (!response.ok) return null;
|
|
549
|
-
const result = await response.json();
|
|
550
|
-
if (result.code === 200 && result.success && ((_a = result.data) == null ? void 0 : _a.path)) {
|
|
551
|
-
return `https://remons.cn:3008${result.data.path}`;
|
|
552
|
-
}
|
|
553
|
-
return null;
|
|
554
|
-
}
|
|
555
|
-
function uploadImage(view) {
|
|
556
|
-
const input = document.createElement("input");
|
|
557
|
-
input.type = "file";
|
|
558
|
-
input.accept = "image/*";
|
|
559
|
-
input.style.display = "none";
|
|
560
|
-
input.addEventListener("change", async () => {
|
|
561
|
-
if (!input.files || input.files.length === 0) return;
|
|
562
|
-
const file = input.files[0];
|
|
563
|
-
const { from, to } = view.state.selection.main;
|
|
564
|
-
const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
|
|
565
|
-
const url = await uploadFileToServer(file);
|
|
566
|
-
if (url) {
|
|
567
|
-
const insert = ``;
|
|
568
|
-
view.dispatch({ changes: { from, to, insert } });
|
|
569
|
-
}
|
|
570
|
-
input.remove();
|
|
571
|
-
});
|
|
572
|
-
document.body.appendChild(input);
|
|
573
|
-
input.click();
|
|
574
|
-
}
|
|
575
|
-
function wrapSelection(view, prefix, suffix) {
|
|
576
|
-
const { from, to, empty } = view.state.selection.main;
|
|
577
|
-
if (empty) return;
|
|
578
|
-
const selected = view.state.doc.sliceString(from, to);
|
|
579
|
-
view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
|
|
580
|
-
}
|
|
581
|
-
function toggleWrap(view, openTag, closeTag) {
|
|
582
|
-
const { from, to, empty } = view.state.selection.main;
|
|
583
|
-
const doc = view.state.doc;
|
|
584
|
-
if (!empty) {
|
|
585
|
-
const selected = doc.sliceString(from, to);
|
|
586
|
-
if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
|
|
587
|
-
const inner = selected.slice(openTag.length, -closeTag.length);
|
|
588
|
-
view.dispatch({ changes: { from, to, insert: inner } });
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
{
|
|
593
|
-
const cursorPos = empty ? from : to;
|
|
594
|
-
const line = doc.lineAt(cursorPos);
|
|
595
|
-
const lineText = doc.sliceString(line.from, line.to);
|
|
596
|
-
const openIdx = lineText.indexOf(openTag);
|
|
597
|
-
const closeIdx = lineText.lastIndexOf(closeTag);
|
|
598
|
-
if (openIdx >= 0 && closeIdx > openIdx) {
|
|
599
|
-
const absOpen = line.from + openIdx;
|
|
600
|
-
const absClose = line.from + closeIdx;
|
|
601
|
-
if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
|
|
602
|
-
const inner = doc.sliceString(absOpen + openTag.length, absClose);
|
|
603
|
-
view.dispatch({
|
|
604
|
-
changes: {
|
|
605
|
-
from: absOpen,
|
|
606
|
-
to: absClose + closeTag.length,
|
|
607
|
-
insert: inner
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
wrapSelection(view, openTag, closeTag);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// src/components/Toolbar/config.ts
|
|
618
|
-
function createToolbarConfig() {
|
|
596
|
+
function createToolbarConfig(options) {
|
|
597
|
+
const hasUploadConfig = isUploadConfigured(options);
|
|
619
598
|
return [
|
|
620
599
|
{
|
|
621
600
|
id: "heading",
|
|
@@ -640,7 +619,14 @@ function createToolbarConfig() {
|
|
|
640
619
|
{ id: "ol", label: "\u6709\u5E8F\u5217\u8868", icon: import_icons.OrderedListOutlined, handler: (v) => insertAtLineStart(v, "1. ") },
|
|
641
620
|
{ separatorBefore: true },
|
|
642
621
|
{ id: "link", label: "\u94FE\u63A5", icon: import_icons.LinkOutlined, handler: (v) => insertAtCursor(v, "[", "](url)", "\u6587\u672C") },
|
|
643
|
-
|
|
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
|
+
},
|
|
644
630
|
{ separatorBefore: true },
|
|
645
631
|
{ id: "code", label: "\u884C\u5185\u4EE3\u7801", icon: import_icons.FieldStringOutlined, handler: (v) => wrapSelection(v, "`", "`") },
|
|
646
632
|
{
|
|
@@ -655,7 +641,6 @@ function createToolbarConfig() {
|
|
|
655
641
|
}
|
|
656
642
|
},
|
|
657
643
|
{ id: "quote", label: "\u5F15\u7528", icon: import_icons.BlockOutlined, handler: (v) => insertAtLineStart(v, "> ") },
|
|
658
|
-
{ id: "textContainer", label: "\u6587\u5B57\u5BB9\u5668", icon: import_icons.TagsOutlined, handler: (v) => toggleWrap(v, "<text-container>", "</text-container>") },
|
|
659
644
|
{
|
|
660
645
|
id: "hr",
|
|
661
646
|
label: "\u5206\u5272\u7EBF",
|
|
@@ -669,13 +654,20 @@ function createToolbarConfig() {
|
|
|
669
654
|
}
|
|
670
655
|
|
|
671
656
|
// src/components/Toolbar/hooks/useToolbar.ts
|
|
672
|
-
function useToolbar(editorView,
|
|
657
|
+
function useToolbar(editorView, options) {
|
|
658
|
+
const { extraItems, uploadConfig } = options || {};
|
|
673
659
|
const config = (0, import_react3.useMemo)(() => {
|
|
674
|
-
const base = createToolbarConfig();
|
|
675
|
-
return extraItems ? [...base,
|
|
676
|
-
}, [extraItems]);
|
|
660
|
+
const base = createToolbarConfig(uploadConfig);
|
|
661
|
+
return extraItems ? [...base, ...extraItems] : base;
|
|
662
|
+
}, [extraItems, uploadConfig]);
|
|
677
663
|
const handleClick = (0, import_react3.useCallback)((button) => {
|
|
678
|
-
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) {
|
|
679
671
|
editorView.focus();
|
|
680
672
|
button.handler(editorView, editorView.state);
|
|
681
673
|
}
|
|
@@ -684,16 +676,16 @@ function useToolbar(editorView, extraItems) {
|
|
|
684
676
|
}
|
|
685
677
|
|
|
686
678
|
// src/components/Toolbar/renderers/ToolbarItemRenderer.tsx
|
|
687
|
-
var
|
|
679
|
+
var import_antd2 = require("antd");
|
|
688
680
|
var import_icons2 = require("@ant-design/icons");
|
|
689
|
-
var
|
|
681
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
690
682
|
function ToolbarItemRenderer({ item, onClick }) {
|
|
691
683
|
const Icon = item.icon;
|
|
692
684
|
if (item.children && item.children.length > 0) {
|
|
693
|
-
return /* @__PURE__ */ (0,
|
|
694
|
-
item.separatorBefore && /* @__PURE__ */ (0,
|
|
695
|
-
/* @__PURE__ */ (0,
|
|
696
|
-
|
|
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,
|
|
697
689
|
{
|
|
698
690
|
menu: {
|
|
699
691
|
items: item.children.map((child) => {
|
|
@@ -701,10 +693,10 @@ function ToolbarItemRenderer({ item, onClick }) {
|
|
|
701
693
|
return {
|
|
702
694
|
type: "item",
|
|
703
695
|
key: (_a = child.id) != null ? _a : "",
|
|
704
|
-
label: /* @__PURE__ */ (0,
|
|
696
|
+
label: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { onClick: () => onClick(child), children: [
|
|
705
697
|
child.icon && (() => {
|
|
706
698
|
const ChildIcon = child.icon;
|
|
707
|
-
return /* @__PURE__ */ (0,
|
|
699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChildIcon, {});
|
|
708
700
|
})(),
|
|
709
701
|
" ",
|
|
710
702
|
child.label
|
|
@@ -713,34 +705,35 @@ function ToolbarItemRenderer({ item, onClick }) {
|
|
|
713
705
|
})
|
|
714
706
|
},
|
|
715
707
|
placement: "bottomLeft",
|
|
716
|
-
children: /* @__PURE__ */ (0,
|
|
717
|
-
/* @__PURE__ */ (0,
|
|
718
|
-
/* @__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 } })
|
|
719
711
|
] })
|
|
720
712
|
}
|
|
721
|
-
) })
|
|
713
|
+
) }) })
|
|
722
714
|
] });
|
|
723
715
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
/* @__PURE__ */ (0,
|
|
727
|
-
|
|
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,
|
|
728
721
|
{
|
|
729
722
|
id: item.id,
|
|
730
723
|
className: "toolbar-button",
|
|
731
724
|
type: "text",
|
|
732
725
|
size: "small",
|
|
733
|
-
|
|
734
|
-
onClick: () => onClick(item),
|
|
735
|
-
children: /* @__PURE__ */ (0,
|
|
726
|
+
disabled: isDisabled,
|
|
727
|
+
onClick: () => !isDisabled && onClick(item),
|
|
728
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, {})
|
|
736
729
|
}
|
|
737
|
-
) })
|
|
730
|
+
) }) })
|
|
738
731
|
] });
|
|
739
732
|
}
|
|
740
733
|
|
|
741
734
|
// src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
|
|
742
735
|
var import_react5 = __toESM(require("react"), 1);
|
|
743
|
-
var
|
|
736
|
+
var import_antd3 = require("antd");
|
|
744
737
|
var import_icons3 = require("@ant-design/icons");
|
|
745
738
|
|
|
746
739
|
// src/components/Toolbar/renderers/TablePicker/index.tsx
|
|
@@ -750,24 +743,27 @@ var import_react4 = require("react");
|
|
|
750
743
|
var index_module_default = {};
|
|
751
744
|
|
|
752
745
|
// src/components/Toolbar/renderers/TablePicker/index.tsx
|
|
753
|
-
var
|
|
754
|
-
var MAX_ROW =
|
|
755
|
-
var MAX_COL =
|
|
756
|
-
var REFRESH_TIME = 200;
|
|
746
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
747
|
+
var MAX_ROW = 8;
|
|
748
|
+
var MAX_COL = 8;
|
|
757
749
|
function TablePicker({ onSelect }) {
|
|
758
750
|
const [hoverSize, setHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
|
|
759
|
-
const [prevHoverSize, setPrevHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
|
|
760
751
|
const [confirmedSize, setConfirmedSize] = (0, import_react4.useState)({ row: 0, col: 0 });
|
|
761
752
|
const maxColRef = (0, import_react4.useRef)(0);
|
|
753
|
+
const prevRowRef = (0, import_react4.useRef)(0);
|
|
762
754
|
const timerIdRef = (0, import_react4.useRef)(null);
|
|
763
755
|
const refreshTimerRef = (0, import_react4.useRef)(null);
|
|
764
756
|
const commitRef = (0, import_react4.useRef)(null);
|
|
765
|
-
const clearTimer = (0, import_react4.useCallback)((
|
|
757
|
+
const clearTimer = (0, import_react4.useCallback)(() => {
|
|
766
758
|
if (timerIdRef.current) {
|
|
759
|
+
clearTimeout(timerIdRef.current);
|
|
767
760
|
timerIdRef.current = null;
|
|
768
761
|
}
|
|
769
|
-
|
|
770
|
-
|
|
762
|
+
}, []);
|
|
763
|
+
const clearFlash = (0, import_react4.useCallback)(() => {
|
|
764
|
+
if (refreshTimerRef.current) {
|
|
765
|
+
clearInterval(refreshTimerRef.current);
|
|
766
|
+
refreshTimerRef.current = null;
|
|
771
767
|
}
|
|
772
768
|
}, []);
|
|
773
769
|
(0, import_react4.useEffect)(
|
|
@@ -778,25 +774,21 @@ function TablePicker({ onSelect }) {
|
|
|
778
774
|
[]
|
|
779
775
|
);
|
|
780
776
|
function extend(col, row) {
|
|
777
|
+
if (row < prevRowRef.current || col < maxColRef.current) {
|
|
778
|
+
maxColRef.current = 0;
|
|
779
|
+
}
|
|
781
780
|
if (col > maxColRef.current) {
|
|
782
781
|
maxColRef.current = col;
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
setConfirmedSize({ row, col: maxColRef.current });
|
|
789
|
-
}
|
|
790
|
-
if (row === 1) {
|
|
791
|
-
commitRef.current = { cols: col, rows: 1 };
|
|
792
|
-
setConfirmedSize({ row: 1, col });
|
|
793
|
-
}
|
|
782
|
+
}
|
|
783
|
+
setHoverSize({ row, col });
|
|
784
|
+
setConfirmedSize({ row, col: maxColRef.current });
|
|
785
|
+
if (row === 1) {
|
|
786
|
+
commitRef.current = { cols: maxColRef.current, rows: 1 };
|
|
794
787
|
}
|
|
795
788
|
}
|
|
796
789
|
function flash(col, dir, limit, finalCallback) {
|
|
797
790
|
let current = dir === 1 ? 1 : limit;
|
|
798
791
|
const step = dir === 1 ? 1 : -1;
|
|
799
|
-
let timer = 0;
|
|
800
792
|
refreshTimerRef.current = setInterval(() => {
|
|
801
793
|
setHoverSize({ row: current, col });
|
|
802
794
|
if (current > confirmedSize.row || dir === -1) {
|
|
@@ -805,45 +797,43 @@ function TablePicker({ onSelect }) {
|
|
|
805
797
|
current += step;
|
|
806
798
|
if (dir === 1 && current > limit || dir === -1 && current < 1) {
|
|
807
799
|
clearInterval(refreshTimerRef.current);
|
|
808
|
-
|
|
809
|
-
|
|
800
|
+
refreshTimerRef.current = null;
|
|
801
|
+
const finalRow = current - step;
|
|
802
|
+
setHoverSize({ row: finalRow, col });
|
|
803
|
+
finalCallback(col, finalRow);
|
|
810
804
|
}
|
|
811
805
|
}, 50);
|
|
812
|
-
clearTimeout(timer);
|
|
813
|
-
return () => {
|
|
814
|
-
clearInterval(refreshTimerRef.current);
|
|
815
|
-
};
|
|
816
806
|
}
|
|
817
807
|
function onCellEnterCheck(index) {
|
|
818
808
|
clearTimer();
|
|
809
|
+
clearFlash();
|
|
819
810
|
const row = Math.floor(index / MAX_COL) + 1;
|
|
820
811
|
const col = index % MAX_COL + 1;
|
|
821
|
-
const
|
|
822
|
-
|
|
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) {
|
|
823
817
|
extend(col, row);
|
|
824
|
-
} else {
|
|
825
|
-
if (
|
|
818
|
+
} else if (isDown) {
|
|
819
|
+
if (col > maxColRef.current || maxColRef.current >= MAX_COL) {
|
|
826
820
|
const finalRow = Math.min(row, MAX_ROW);
|
|
827
821
|
flash(col, 1, finalRow, (c, r) => {
|
|
828
822
|
setConfirmedSize({ row: r, col: c });
|
|
829
823
|
});
|
|
830
|
-
} else
|
|
831
|
-
|
|
832
|
-
setConfirmedSize({ row: r, col: c });
|
|
833
|
-
if (r === 1) {
|
|
834
|
-
commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
|
|
835
|
-
setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
|
|
836
|
-
}
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (col >= MAX_COL && row >= prevHoverSize.row) {
|
|
841
|
-
if (row !== prevHoverSize.row) {
|
|
842
|
-
maxColRef.current = 0;
|
|
843
|
-
setPrevHoverSize({ row: 0, col: 0 });
|
|
824
|
+
} else {
|
|
825
|
+
extend(col, row);
|
|
844
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
|
+
});
|
|
845
836
|
}
|
|
846
|
-
setPrevHoverSize({ row, col });
|
|
847
837
|
}
|
|
848
838
|
function onMouseDown() {
|
|
849
839
|
const size = commitRef.current || { cols: maxColRef.current, rows: confirmedSize.row };
|
|
@@ -851,13 +841,13 @@ function TablePicker({ onSelect }) {
|
|
|
851
841
|
onSelect(size);
|
|
852
842
|
}
|
|
853
843
|
}
|
|
854
|
-
return /* @__PURE__ */ (0,
|
|
844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
855
845
|
"div",
|
|
856
846
|
{
|
|
857
847
|
className: index_module_default.container,
|
|
858
848
|
onMouseDown,
|
|
859
849
|
children: [
|
|
860
|
-
/* @__PURE__ */ (0,
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
861
851
|
"div",
|
|
862
852
|
{
|
|
863
853
|
className: index_module_default.grid,
|
|
@@ -866,7 +856,7 @@ function TablePicker({ onSelect }) {
|
|
|
866
856
|
const row = Math.floor(i / MAX_COL) + 1;
|
|
867
857
|
const col = i % MAX_COL + 1;
|
|
868
858
|
const isSelected = row <= hoverSize.row && col <= maxColRef.current;
|
|
869
|
-
return /* @__PURE__ */ (0,
|
|
859
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
870
860
|
"div",
|
|
871
861
|
{
|
|
872
862
|
className: `${index_module_default.cell} ${isSelected ? index_module_default.cellActive : ""}`,
|
|
@@ -877,7 +867,7 @@ function TablePicker({ onSelect }) {
|
|
|
877
867
|
})
|
|
878
868
|
}
|
|
879
869
|
),
|
|
880
|
-
/* @__PURE__ */ (0,
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: index_module_default.label, children: [
|
|
881
871
|
hoverSize.row,
|
|
882
872
|
" x ",
|
|
883
873
|
maxColRef.current || 1
|
|
@@ -888,7 +878,7 @@ function TablePicker({ onSelect }) {
|
|
|
888
878
|
}
|
|
889
879
|
|
|
890
880
|
// src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
|
|
891
|
-
var
|
|
881
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
892
882
|
function TableDropdown({ editorView }) {
|
|
893
883
|
const [open, setOpen] = import_react5.default.useState(false);
|
|
894
884
|
const openCountRef = (0, import_react5.useRef)(0);
|
|
@@ -907,15 +897,15 @@ ${dataRows}
|
|
|
907
897
|
const { from } = editorView.state.selection.main;
|
|
908
898
|
editorView.dispatch({ changes: { from, insert: table } });
|
|
909
899
|
}, [editorView]);
|
|
910
|
-
return /* @__PURE__ */ (0,
|
|
911
|
-
|
|
900
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
901
|
+
import_antd3.Dropdown,
|
|
912
902
|
{
|
|
913
903
|
trigger: ["click"],
|
|
914
904
|
open,
|
|
915
905
|
onOpenChange: setOpen,
|
|
916
906
|
popupRender: () => {
|
|
917
907
|
openCountRef.current++;
|
|
918
|
-
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)(
|
|
919
909
|
TablePicker,
|
|
920
910
|
{
|
|
921
911
|
onSelect: (size) => {
|
|
@@ -927,44 +917,41 @@ ${dataRows}
|
|
|
927
917
|
) });
|
|
928
918
|
},
|
|
929
919
|
placement: "bottomLeft",
|
|
930
|
-
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, {}) })
|
|
931
921
|
}
|
|
932
922
|
);
|
|
933
923
|
}
|
|
934
924
|
|
|
935
925
|
// src/components/Toolbar/index.tsx
|
|
936
|
-
var
|
|
937
|
-
function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
|
|
938
|
-
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 });
|
|
939
929
|
const renderFullscreen = (0, import_react6.useCallback)(() => {
|
|
940
930
|
if (!onToggleFullscreen) return null;
|
|
941
931
|
const Icon = fullscreen ? import_icons4.FullscreenExitOutlined : import_icons4.FullscreenOutlined;
|
|
942
|
-
return /* @__PURE__ */ (0,
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
)
|
|
957
|
-
] });
|
|
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
|
+
);
|
|
958
945
|
}, [fullscreen, onToggleFullscreen]);
|
|
959
946
|
function isSeparator(item) {
|
|
960
947
|
return !!(!item.id && item.separatorBefore);
|
|
961
948
|
}
|
|
962
|
-
return /* @__PURE__ */ (0,
|
|
949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "toolbar", children: [
|
|
963
950
|
config.map((item, idx) => {
|
|
964
951
|
if (isSeparator(item)) {
|
|
965
|
-
return /* @__PURE__ */ (0,
|
|
952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "toolbar-separator" }, `sep-${idx}`);
|
|
966
953
|
}
|
|
967
|
-
return /* @__PURE__ */ (0,
|
|
954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
968
955
|
ToolbarItemRenderer,
|
|
969
956
|
{
|
|
970
957
|
item,
|
|
@@ -973,16 +960,15 @@ function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
|
|
|
973
960
|
item.id
|
|
974
961
|
);
|
|
975
962
|
}),
|
|
976
|
-
/* @__PURE__ */ (0,
|
|
977
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TableDropdown, { editorView }),
|
|
963
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableDropdown, { editorView }),
|
|
978
964
|
renderFullscreen()
|
|
979
965
|
] });
|
|
980
966
|
}
|
|
981
967
|
|
|
982
968
|
// src/components/Dialog/EditDialog.tsx
|
|
983
969
|
var import_react7 = require("react");
|
|
984
|
-
var
|
|
985
|
-
var
|
|
970
|
+
var import_antd5 = require("antd");
|
|
971
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
986
972
|
function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
987
973
|
const schema = componentRegistry_default.get(typeName);
|
|
988
974
|
const [formProps, setFormProps] = (0, import_react7.useState)({});
|
|
@@ -1010,8 +996,8 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
|
1010
996
|
if (!schema) {
|
|
1011
997
|
return null;
|
|
1012
998
|
}
|
|
1013
|
-
return /* @__PURE__ */ (0,
|
|
1014
|
-
|
|
999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1000
|
+
import_antd5.Modal,
|
|
1015
1001
|
{
|
|
1016
1002
|
title: `${schema.label} \u5C5E\u6027\u7F16\u8F91`,
|
|
1017
1003
|
open: visible,
|
|
@@ -1051,35 +1037,11 @@ ${propsLine}
|
|
|
1051
1037
|
|
|
1052
1038
|
// src/MarkdownEditor.tsx
|
|
1053
1039
|
var import_icons5 = require("@ant-design/icons");
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
onChange,
|
|
1059
|
-
showPreview = true,
|
|
1060
|
-
fullscreen: fullscreenProp = false
|
|
1061
|
-
}, ref) {
|
|
1062
|
-
const [content, setContent] = (0, import_react8.useState)(value != null ? value : defaultValue);
|
|
1063
|
-
const [editorView, setEditorView] = (0, import_react8.useState)(null);
|
|
1064
|
-
const scrollDOMRef = (0, import_react8.useRef)(null);
|
|
1065
|
-
const previewElRef = (0, import_react8.useRef)(null);
|
|
1040
|
+
|
|
1041
|
+
// src/hooks/useFullscreen.ts
|
|
1042
|
+
var import_react8 = require("react");
|
|
1043
|
+
function useFullscreen() {
|
|
1066
1044
|
const [fullscreen, setFullscreen] = (0, import_react8.useState)(false);
|
|
1067
|
-
const [dialogVisible, setDialogVisible] = (0, import_react8.useState)(false);
|
|
1068
|
-
const [dialogTypeName, setDialogTypeName] = (0, import_react8.useState)("");
|
|
1069
|
-
const [dialogProps, setDialogProps] = (0, import_react8.useState)({});
|
|
1070
|
-
const [dialogPropsRange, setDialogPropsRange] = (0, import_react8.useState)([0, 0]);
|
|
1071
|
-
(0, import_react8.useEffect)(() => {
|
|
1072
|
-
if (value !== void 0) {
|
|
1073
|
-
setContent(value);
|
|
1074
|
-
}
|
|
1075
|
-
}, [value]);
|
|
1076
|
-
const internalSetContent = (0, import_react8.useCallback)((newContent) => {
|
|
1077
|
-
setContent(newContent);
|
|
1078
|
-
onChange == null ? void 0 : onChange(newContent);
|
|
1079
|
-
}, [onChange]);
|
|
1080
|
-
(0, import_react8.useImperativeHandle)(ref, () => ({
|
|
1081
|
-
getContent: () => content
|
|
1082
|
-
}), [content]);
|
|
1083
1045
|
const toggleFullscreen = (0, import_react8.useCallback)(() => {
|
|
1084
1046
|
if (document.fullscreenElement) {
|
|
1085
1047
|
document.exitFullscreen().catch((e) => {
|
|
@@ -1106,40 +1068,29 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1106
1068
|
document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
|
|
1107
1069
|
};
|
|
1108
1070
|
}, []);
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
icon: () => iconNode,
|
|
1120
|
-
handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
|
|
1121
|
-
};
|
|
1122
|
-
});
|
|
1123
|
-
const handleScrollReady = (0, import_react8.useCallback)((scrollDOM) => {
|
|
1124
|
-
scrollDOMRef.current = scrollDOM;
|
|
1125
|
-
}, []);
|
|
1126
|
-
(0, import_react8.useEffect)(() => {
|
|
1127
|
-
const source = scrollDOMRef.current;
|
|
1128
|
-
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;
|
|
1129
1081
|
if (!source || !target) return;
|
|
1130
|
-
let rafId = null;
|
|
1131
1082
|
const listener = () => {
|
|
1132
|
-
if (
|
|
1133
|
-
|
|
1134
|
-
const s =
|
|
1135
|
-
const t =
|
|
1083
|
+
if (rafIdRef.current) return;
|
|
1084
|
+
rafIdRef.current = requestAnimationFrame(() => {
|
|
1085
|
+
const s = sourceRef.current;
|
|
1086
|
+
const t = targetRef.current;
|
|
1136
1087
|
if (!s || !t) {
|
|
1137
|
-
|
|
1088
|
+
rafIdRef.current = null;
|
|
1138
1089
|
return;
|
|
1139
1090
|
}
|
|
1140
1091
|
const sourceScrollHeight = s.scrollHeight - s.clientHeight;
|
|
1141
1092
|
if (sourceScrollHeight <= 0) {
|
|
1142
|
-
|
|
1093
|
+
rafIdRef.current = null;
|
|
1143
1094
|
return;
|
|
1144
1095
|
}
|
|
1145
1096
|
const ratio = s.scrollTop / sourceScrollHeight;
|
|
@@ -1147,78 +1098,136 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1147
1098
|
if (targetScrollHeight > 0) {
|
|
1148
1099
|
t.scrollTop = ratio * targetScrollHeight;
|
|
1149
1100
|
}
|
|
1150
|
-
|
|
1101
|
+
rafIdRef.current = null;
|
|
1151
1102
|
});
|
|
1152
1103
|
};
|
|
1153
1104
|
source.addEventListener("scroll", listener, { passive: true });
|
|
1154
1105
|
return () => {
|
|
1155
1106
|
source.removeEventListener("scroll", listener);
|
|
1156
|
-
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)
|
|
1157
1157
|
};
|
|
1158
|
-
}
|
|
1159
|
-
const
|
|
1158
|
+
});
|
|
1159
|
+
const handleScrollReady = (0, import_react10.useCallback)((scrollDOM) => {
|
|
1160
|
+
scrollDOMRef.current = scrollDOM;
|
|
1161
|
+
}, []);
|
|
1162
|
+
const handlePreviewRef = (0, import_react10.useCallback)((node) => {
|
|
1160
1163
|
previewElRef.current = node;
|
|
1161
1164
|
}, []);
|
|
1162
|
-
const handleViewReady = (0,
|
|
1165
|
+
const handleViewReady = (0, import_react10.useCallback)((view) => {
|
|
1163
1166
|
setEditorView(view);
|
|
1164
1167
|
}, []);
|
|
1165
|
-
const handleEditContainer = (0,
|
|
1168
|
+
const handleEditContainer = (0, import_react10.useCallback)((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
|
|
1166
1169
|
setDialogTypeName(typeName);
|
|
1167
1170
|
setDialogProps({ ...currentProps });
|
|
1168
1171
|
setDialogPropsRange([propsLineFrom, propsLineTo]);
|
|
1169
1172
|
setDialogVisible(true);
|
|
1170
1173
|
}, []);
|
|
1171
|
-
const handleDialogSave = (0,
|
|
1174
|
+
const handleDialogSave = (0, import_react10.useCallback)((newProps) => {
|
|
1172
1175
|
setDialogVisible(false);
|
|
1173
1176
|
if (editorView) {
|
|
1174
1177
|
updateContainerProps(editorView, dialogPropsRange[0], dialogPropsRange[1], newProps);
|
|
1175
1178
|
}
|
|
1176
1179
|
}, [editorView, dialogPropsRange]);
|
|
1177
|
-
const handleDialogClose = (0,
|
|
1180
|
+
const handleDialogClose = (0, import_react10.useCallback)(() => {
|
|
1178
1181
|
setDialogVisible(false);
|
|
1179
1182
|
}, []);
|
|
1180
|
-
return /* @__PURE__ */ (0,
|
|
1181
|
-
/* @__PURE__ */ (0,
|
|
1183
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: fullscreen ? "app fullscreen" : "app", children: [
|
|
1184
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1182
1185
|
Toolbar,
|
|
1183
1186
|
{
|
|
1184
1187
|
editorView,
|
|
1185
1188
|
fullscreen,
|
|
1186
1189
|
onToggleFullscreen: fullscreenProp ? toggleFullscreen : void 0,
|
|
1187
|
-
extraItems: extraToolbarItems
|
|
1190
|
+
extraItems: extraToolbarItems,
|
|
1191
|
+
uploadConfig
|
|
1188
1192
|
}
|
|
1189
1193
|
),
|
|
1190
|
-
showPreview ? /* @__PURE__ */ (0,
|
|
1194
|
+
showPreview ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1191
1195
|
SplitPanels,
|
|
1192
1196
|
{
|
|
1193
|
-
leftPanel: /* @__PURE__ */ (0,
|
|
1197
|
+
leftPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1194
1198
|
Editor,
|
|
1195
1199
|
{
|
|
1196
1200
|
value: content,
|
|
1197
1201
|
onChange: internalSetContent,
|
|
1198
1202
|
onScrollReady: handleScrollReady,
|
|
1199
1203
|
onViewReady: handleViewReady,
|
|
1200
|
-
onEditContainer: handleEditContainer
|
|
1204
|
+
onEditContainer: handleEditContainer,
|
|
1205
|
+
uploadConfig,
|
|
1206
|
+
onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
|
|
1201
1207
|
}
|
|
1202
1208
|
),
|
|
1203
|
-
rightPanel: /* @__PURE__ */ (0,
|
|
1209
|
+
rightPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1204
1210
|
Preview_default,
|
|
1205
1211
|
{
|
|
1206
1212
|
content,
|
|
1207
|
-
ref: handlePreviewRef
|
|
1213
|
+
ref: handlePreviewRef,
|
|
1214
|
+
previewOptions
|
|
1208
1215
|
}
|
|
1209
1216
|
)
|
|
1210
1217
|
}
|
|
1211
|
-
) : /* @__PURE__ */ (0,
|
|
1218
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1212
1219
|
Editor,
|
|
1213
1220
|
{
|
|
1214
1221
|
value: content,
|
|
1215
1222
|
onChange: internalSetContent,
|
|
1216
1223
|
onScrollReady: handleScrollReady,
|
|
1217
1224
|
onViewReady: handleViewReady,
|
|
1218
|
-
onEditContainer: handleEditContainer
|
|
1225
|
+
onEditContainer: handleEditContainer,
|
|
1226
|
+
uploadConfig,
|
|
1227
|
+
onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
|
|
1219
1228
|
}
|
|
1220
1229
|
),
|
|
1221
|
-
/* @__PURE__ */ (0,
|
|
1230
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1222
1231
|
EditDialog,
|
|
1223
1232
|
{
|
|
1224
1233
|
visible: dialogVisible,
|
|
@@ -1232,10 +1241,10 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
|
|
|
1232
1241
|
});
|
|
1233
1242
|
|
|
1234
1243
|
// src/hooks/useRegisterToolbar.ts
|
|
1235
|
-
var
|
|
1244
|
+
var import_react11 = require("react");
|
|
1236
1245
|
function useRegisterToolbar(schemas) {
|
|
1237
1246
|
const schemaList = Array.isArray(schemas) ? schemas : [schemas];
|
|
1238
|
-
(0,
|
|
1247
|
+
(0, import_react11.useEffect)(() => {
|
|
1239
1248
|
schemaList.forEach((schema) => {
|
|
1240
1249
|
componentRegistry_default.register(schema.name, schema);
|
|
1241
1250
|
});
|