diff --git a/src/Cherry.js b/src/Cherry.js index 2202bd2e..9510cf6e 100644 --- a/src/Cherry.js +++ b/src/Cherry.js @@ -28,6 +28,7 @@ import getPosBydiffs from './utils/recount-pos'; import defaultConfig from './Cherry.config'; import './sass/cherry.scss'; import cloneDeep from 'lodash/cloneDeep'; +import Event from './Event'; import { urlProcessorProxy } from './UrlCache'; import { CherryStatic } from './CherryStatic'; @@ -70,6 +71,13 @@ export default class Cherry extends CherryStatic { this.options.editor.defaultModel = 'previewOnly'; } + /** + * @property + * @type {string} 实例ID + */ + this.instanceId = `cherry-${new Date().getTime()}${Math.random()}`; + this.options.instanceId = this.instanceId; + /** * @private * @type {Engine} @@ -83,6 +91,11 @@ export default class Cherry extends CherryStatic { * @private */ init() { + this.status = { + toolbar: 'show', + previewer: 'show', + editor: 'show', + }; let mountEl = document.getElementById(this.options.id); if (!mountEl) { @@ -147,6 +160,24 @@ export default class Cherry extends CherryStatic { this.switchModel(this.options.editor.defaultModel); this.cherryDomResize(); + Event.on(this.instanceId, Event.Events.toolbarHide, () => { + this.status.toolbar = 'hide'; + }); + Event.on(this.instanceId, Event.Events.toolbarShow, () => { + this.status.toolbar = 'show'; + }); + Event.on(this.instanceId, Event.Events.previewerClose, () => { + this.status.previewer = 'hide'; + }); + Event.on(this.instanceId, Event.Events.previewerOpen, () => { + this.status.previewer = 'show'; + }); + Event.on(this.instanceId, Event.Events.editorClose, () => { + this.status.editor = 'hide'; + }); + Event.on(this.instanceId, Event.Events.editorOpen, () => { + this.status.editor = 'show'; + }); } /** @@ -155,7 +186,7 @@ export default class Cherry extends CherryStatic { */ cherryDomResize() { const observer = new ResizeObserver((entries) => { - for (const entry of entries) { + for (const {} of entries) { setTimeout(() => this.editor.editor.refresh(), 10); } }); @@ -198,6 +229,23 @@ export default class Cherry extends CherryStatic { } } + /** + * 获取实例id + * @returns {string} + * @public + */ + getInstanceId() { + return this.instanceId; + } + + /** + * 获取编辑器状态 + * @returns {Object} + */ + getStatus() { + return this.status; + } + /** * 获取编辑区内的markdown源码内容 * @returns markdown源码内容 @@ -420,6 +468,7 @@ export default class Cherry extends CherryStatic { editor.appendChild(textArea); this.editor = new Editor({ + $cherry: this, editorDom: editor, wrapperDom: this.wrapperDom, value: this.options.value, @@ -456,6 +505,7 @@ export default class Cherry extends CherryStatic { const previewerMask = createElement('div', 'cherry-previewer-mask'); this.previewer = new Previewer({ + $cherry: this, virtualDragLineDom: virtualDragLine, editorMaskDom: editorMask, previewerMaskDom: previewerMask, @@ -487,10 +537,10 @@ export default class Cherry extends CherryStatic { /** * @private - * @param {Event} evt + * @param {Event} _evt * @param {import('codemirror').Editor} codemirror */ - editText(evt, codemirror) { + editText(_evt, codemirror) { try { if (this.timer) { clearTimeout(this.timer); diff --git a/src/Editor.js b/src/Editor.js index b096d502..c89fcb15 100644 --- a/src/Editor.js +++ b/src/Editor.js @@ -94,6 +94,8 @@ export default class Editor { Object.assign(this.options.codemirror, codemirror); } Object.assign(this.options, restOptions); + this.$cherry = this.options.$cherry; + this.instanceId = this.$cherry.getInstanceId(); } /** diff --git a/src/Event.js b/src/Event.js index 4b1157ea..7bce9f8a 100644 --- a/src/Event.js +++ b/src/Event.js @@ -26,6 +26,8 @@ export default new (class Event { Events = { previewerClose: 'previewer:close', previewerOpen: 'previewer:open', + editorClose: 'editor:close', + editorOpen: 'editor:open', toolbarHide: 'toolbar:hide', toolbarShow: 'toolbar:show', }; diff --git a/src/Previewer.js b/src/Previewer.js index 8ce1d843..a32f0016 100644 --- a/src/Previewer.js +++ b/src/Previewer.js @@ -60,11 +60,6 @@ export default class Previewer { * @param {Partial} options 预览区域设置 */ constructor(options) { - /** - * @property - * @type {string} 实例ID - */ - this.instanceId = `cherry-previewer-${new Date().getTime()}`; /** * @property * @type {import('~types/previewer').PreviewerOptions} @@ -88,6 +83,8 @@ export default class Previewer { }; Object.assign(this.options, options); + this.$cherry = this.options.$cherry; + this.instanceId = this.$cherry.getInstanceId(); /** * @property * @private @@ -642,12 +639,15 @@ export default class Previewer { this.update(this.options.previewerCache.html); } this.cleanHtmlCache(); + Event.emit(this.instanceId, Event.Events.previewerOpen); + Event.emit(this.instanceId, Event.Events.editorClose); } editOnly(dealToolbar = false) { this.$dealEditAndPreviewOnly(true); this.cleanHtmlCache(); Event.emit(this.instanceId, Event.Events.previewerClose); + Event.emit(this.instanceId, Event.Events.editorOpen); } recoverPreviewer(dealToolbar = false) { @@ -665,6 +665,7 @@ export default class Previewer { this.cleanHtmlCache(); Event.emit(this.instanceId, Event.Events.previewerOpen); + Event.emit(this.instanceId, Event.Events.editorOpen); setTimeout(() => this.editor.editor.refresh(), 0); } diff --git a/src/toolbars/PreviewerBubble.js b/src/toolbars/PreviewerBubble.js index 7c503168..924dd410 100644 --- a/src/toolbars/PreviewerBubble.js +++ b/src/toolbars/PreviewerBubble.js @@ -27,7 +27,6 @@ export default class PreviewerBubble { * @param {import('../Editor').default} editor */ constructor(previewer, editor) { - this.instanceId = `cherry-toolbar-${new Date().getTime()}`; /** * @property * @type {import('../Previewer').default} @@ -77,6 +76,10 @@ export default class PreviewerBubble { } $onClick(e) { + // 只有双栏编辑模式才出现该功能 + if (this.previewer.$cherry.getStatus().editor === 'hide') { + return; + } const { target } = e; this.$removeAllPreviewerBubbles(); if (typeof target.tagName === 'undefined') { diff --git a/src/utils/tableContentHander.js b/src/utils/tableContentHander.js index fdb5df13..33b3e2bd 100644 --- a/src/utils/tableContentHander.js +++ b/src/utils/tableContentHander.js @@ -94,14 +94,20 @@ const tableContentHander = { */ $collectTableCode() { const tableCodes = []; - this.codeMirror.getValue().replace(this.tableReg, function (...args) { - const match = args[0].replace(/^\n*/, ''); - const offsetBegin = args[args.length - 2] + args[0].match(/^\n*/)[0].length; - tableCodes.push({ - code: match, - offset: offsetBegin, + this.codeMirror + .getValue() + .replace(this.codeBlockReg, (whole, ...args) => { + // 先把代码块里的表格语法关键字干掉 + return whole.replace(/\|/g, '.'); + }) + .replace(this.tableReg, function (whole, ...args) { + const match = whole.replace(/^\n*/, ''); + const offsetBegin = args[args.length - 2] + whole.match(/^\n*/)[0].length; + tableCodes.push({ + code: match, + offset: offsetBegin, + }); }); - }); this.tableEditor.tableCodes = tableCodes; }, diff --git a/types/cherry.d.ts b/types/cherry.d.ts index bb7e94b7..ee76818b 100644 --- a/types/cherry.d.ts +++ b/types/cherry.d.ts @@ -35,6 +35,7 @@ export interface CherryOptions { id: string; /** 初始内容,引擎模式下不生效 */ value: string; + instanceId?: string; } export interface CherryExternalsOptions { diff --git a/types/editor.d.ts b/types/editor.d.ts index cb9f49d9..0e7f6b00 100644 --- a/types/editor.d.ts +++ b/types/editor.d.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import CodeMirror from 'codemirror'; +import Cherry from '../src/Cherry.js'; interface EditorEventMap { onBlur: FocusEvent; @@ -54,4 +55,5 @@ export type EditorConfiguration = { /** 预览区域跟随编辑器光标自动滚动 */ autoScrollByCursor: boolean; fileUpload?: (file: File, callback: (fileUrl: string) => void) => void; + $cherry?: Cherry; }; diff --git a/types/previewer.d.ts b/types/previewer.d.ts index 029df213..c0aa2318 100644 --- a/types/previewer.d.ts +++ b/types/previewer.d.ts @@ -1,3 +1,4 @@ +import Cherry from '../src/Cherry.js'; export type AfterUpdateCallbackFunc = () => void; export interface PreviewerOptions { @@ -25,4 +26,5 @@ export interface PreviewerOptions { previewerMaskDom: HTMLDivElement; /** 是否开启预览区域菜单 */ enablePreviewerBubble?: boolean; + $cherry?: Cherry; }