From a7e1f960944954c3d0b148b41f308988f0935289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sunsonliu=28=E5=88=98=E9=98=B3=29?= Date: Mon, 13 Mar 2023 01:30:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E5=92=8C=E6=89=8B=E9=A3=8E=E7=90=B4=E8=AF=AD?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/markdown/basic.md | 32 ++++++--------------- src/core/hooks/Detail.js | 56 ++++++++++++++++-------------------- src/core/hooks/Panel.js | 14 +++++---- src/toolbars/hooks/Detail.js | 18 ++++++++---- src/toolbars/hooks/Panel.js | 41 ++++++++++++++++++++------ src/utils/regexp.js | 18 ++++-------- 6 files changed, 92 insertions(+), 87 deletions(-) diff --git a/examples/markdown/basic.md b/examples/markdown/basic.md index 631d811b..ac978516 100644 --- a/examples/markdown/basic.md +++ b/examples/markdown/basic.md @@ -292,17 +292,13 @@ **说明** 使用连续三个冒号`:::`和关键字(`[primary | info | warning | danger | success]`)来声明 ``` -:::primary // [primary | info | warning | danger | success] -标题 -: +:::primary // [primary | info | warning | danger | success] 标题 内容 ::: ``` **效果** -:::p -标题 -: +:::p 标题 内容 ::: :::success @@ -317,33 +313,21 @@ **说明** 使用连续三个加号`+++`和关键字(`[ + | - ]`)来声明,关键字`+`表示默认收起,关键字`-`表示默认展开 ``` -++++ -点击展开更多 -: ++++ 点击展开更多 内容 -+- -默认展开 -: +++- 默认展开 内容 -++ -默认收起 -: +++ 默认收起 内容 +++ ``` **效果** -++++ -点击展开更多 -: ++++ 点击展开更多 内容 -+- -默认展开 -: +++- 默认展开 内容 -++ -默认收起 -: +++ 默认收起 内容 +++ diff --git a/src/core/hooks/Detail.js b/src/core/hooks/Detail.js index 4a34e1fc..e17e38a4 100644 --- a/src/core/hooks/Detail.js +++ b/src/core/hooks/Detail.js @@ -19,18 +19,12 @@ import { getDetailRule } from '@/utils/regexp'; import { blockNames } from '@/utils/sanitize'; /** - * +++(+|-) - * 点击查看详情 - * : + * +++(-) 点击查看详情 * body * body - * +- - * 标题(默认收起内容) - * : + * ++ 标题(默认收起内容) * 内容 - * ++ - * 标题(默认展开内容) - * : + * ++- 标题(默认展开内容) * 内容2 * +++ */ @@ -42,10 +36,10 @@ export default class Detail extends ParagraphBase { } makeHtml(str, sentenceMakeFunc) { - return str.replace(this.RULE.reg, (match, preLines, isShow, content) => { + return str.replace(this.RULE.reg, (match, preLines, isOpen, title, content) => { const lineCount = this.getLineCount(match, preLines); const sign = this.$engine.md5(match); - const { type, html } = this.$getDetailInfo(isShow, content, sentenceMakeFunc); + const { type, html } = this.$getDetailInfo(isOpen, title, content, sentenceMakeFunc); const ret = this.pushCache( `
${html}
`, sign, @@ -55,27 +49,29 @@ export default class Detail extends ParagraphBase { }); } - $getDetailInfo(isShow, str, sentenceMakeFunc) { - const type = /\n\s*(\+\+|\+-)\s*\n/.test(str) ? 'multiple' : 'single'; - const arr = str.split(/\n\s*(\+\+|\+-)\s*\n/); - let defaultShow = isShow !== '+'; + $getDetailInfo(isOpen, title, str, sentenceMakeFunc) { + const type = /\n\s*(\+\+|\+\+-)\s*[^\n]+\n/.test(str) ? 'multiple' : 'single'; + const arr = str.split(/\n\s*(\+\+[-]{0,1}\s*[^\n]+)\n/); + let defaultOpen = isOpen === '-'; + let currentTitle = title; let html = ''; if (type === 'multiple') { arr.forEach((item) => { - if (/(\+\+|\+-)/.test(item)) { - defaultShow = item !== '++'; + if (/\+\+/.test(item)) { + defaultOpen = /\+\+-/.test(item); + currentTitle = item.replace(/\+\+[-]{0,1}\s*([^\n]+)$/, '$1'); return true; } - html += this.$getDetailHtml(defaultShow, item, sentenceMakeFunc); + html += this.$getDetailHtml(defaultOpen, currentTitle, item, sentenceMakeFunc); }); } else { - html = this.$getDetailHtml(defaultShow, str, sentenceMakeFunc); + html = this.$getDetailHtml(defaultOpen, currentTitle, str, sentenceMakeFunc); } return { type, html }; } - $getDetailHtml(defaultShow, str, sentenceMakeFunc) { - let ret = `
`; + $getDetailHtml(defaultOpen, title, str, sentenceMakeFunc) { + let ret = `
`; const paragraphProcessor = (str) => { if (str.trim() === '') { return ''; @@ -90,16 +86,14 @@ export default class Detail extends ParagraphBase { } return `<${domName}>${this.$cleanParagraph(html)}`; }; - str.replace(/(^[\w\W]+?)\n\s*:\s*\n([\w\W]+$)/, (match, title, body) => { - ret += `${sentenceMakeFunc(title).html}`; - let $body = ''; - if (this.isContainsCache(body)) { - $body = this.makeExcludingCached(body, paragraphProcessor); - } else { - $body = paragraphProcessor(body); - } - ret += `
${$body}
`; - }); + ret += `${sentenceMakeFunc(title).html}`; + let $body = ''; + if (this.isContainsCache(str)) { + $body = this.makeExcludingCached(str, paragraphProcessor); + } else { + $body = paragraphProcessor(str); + } + ret += `
${$body}
`; ret += `
`; return ret; } diff --git a/src/core/hooks/Panel.js b/src/core/hooks/Panel.js index d9b82e6e..2a62353a 100644 --- a/src/core/hooks/Panel.js +++ b/src/core/hooks/Panel.js @@ -53,11 +53,7 @@ export default class Panel extends ParagraphBase { } $getPanelInfo(name, str, sentenceMakeFunc) { - const ret = { type: this.$getTargetType(name), title: '', body: str }; - str.replace(/^([^\n]+)\n\s*:\s*\n([\w\W]+)$/, (match, title, body) => { - ret.title = sentenceMakeFunc(title).html; - ret.body = body; - }); + const ret = { type: this.$getTargetType(name), title: sentenceMakeFunc(this.$getTitle(name)).html, body: str }; ret.title = `
${ ret.title }
`; @@ -85,8 +81,14 @@ export default class Panel extends ParagraphBase { return ret; } + $getTitle(name) { + const $name = name.trim(); + return /\s/.test($name) ? $name.replace(/[^\s]+\s/, '') : ''; + } + $getTargetType(name) { - switch (name.toLowerCase()) { + const $name = /\s/.test(name.trim()) ? name.trim().replace(/\s.*$/, '') : name; + switch ($name.trim().toLowerCase()) { case 'primary': case 'p': return 'primary'; diff --git a/src/toolbars/hooks/Detail.js b/src/toolbars/hooks/Detail.js index 447fa03b..4048a728 100644 --- a/src/toolbars/hooks/Detail.js +++ b/src/toolbars/hooks/Detail.js @@ -34,11 +34,11 @@ export default class Detail extends MenuBase { onClick(selection) { let $selection = getSelection(this.editor.editor, selection, 'line', true) || - '点击展开更多\n:\n内容\n+-\n默认展开\n:\n内容\n++\n默认收起\n:\n内容'; + '点击展开更多\n内容\n++- 默认展开\n内容\n++ 默认收起\n内容'; this.detailRule.lastIndex = 0; if (!this.detailRule.test($selection)) { // 如果没有命中手风琴语法,则尝试扩大选区 - this.getMoreSelection('\n', '\n', () => { + this.getMoreSelection('+++ ', '\n', () => { const newSelection = this.editor.editor.getSelection(); this.detailRule.lastIndex = 0; const isMatch = this.detailRule.test(newSelection); @@ -52,13 +52,19 @@ export default class Detail extends MenuBase { if (this.detailRule.test($selection)) { // 如果命中了手风琴语法,则去掉手风琴语法 this.detailRule.lastIndex = 0; - return $selection.replace(this.detailRule, (match, preLines, isShow, content) => { - return content; + return $selection.replace(this.detailRule, (match, preLines, isOpen, title, content) => { + return `${title}\n${content}`; }); } + // 去掉开头的空格 + $selection = $selection.replace(/^\s+/, ''); + // 如果选中的内容不包含换行,则强制增加一个换行 + if (!/\n/.test($selection)) { + $selection = `${$selection}\n${$selection}`; + } this.registerAfterClickCb(() => { - this.setLessSelection('\n', '\n'); + this.setLessSelection('+++ ', '\n'); }); - return `++++\n${$selection}\n+++`.replace(/\n{2,}\+\+\+/g, '\n+++'); + return `+++ ${$selection}\n+++`.replace(/\n{2,}\+\+\+/g, '\n+++'); } } diff --git a/src/toolbars/hooks/Panel.js b/src/toolbars/hooks/Panel.js index 33cac4c3..d5abc436 100644 --- a/src/toolbars/hooks/Panel.js +++ b/src/toolbars/hooks/Panel.js @@ -62,12 +62,22 @@ export default class Panel extends MenuBase { let ret = false; this.panelRule.lastIndex = 0; str.replace(this.panelRule, (match, preLines, name, content) => { - ret = name ? name : ''; + const $name = /\s/.test(name.trim()) ? name.trim().replace(/\s.*$/, '') : name; + ret = $name ? $name.trim().toLowerCase() : ''; return match; }); return ret; } + $getTitle(str) { + this.panelRule.lastIndex = 0; + str.replace(this.panelRule, (match, preLines, name, content) => { + const $name = name.trim(); + return /\s/.test($name) ? $name.replace(/[^\s]+\s/, '') : ''; + }); + return ''; + } + /** * 响应点击事件 * @param {string} selection 被用户选中的文本内容 @@ -75,16 +85,18 @@ export default class Panel extends MenuBase { * @returns {string} 回填到编辑器光标位置/选中文本区域的内容 */ onClick(selection, shortKey = '') { - let $selection = getSelection(this.editor.editor, selection, 'line', true) || '标题\n:\n内容'; + let $selection = getSelection(this.editor.editor, selection, 'line', true) || '内容'; let currentName = this.$getNameFromStr($selection); + let title = this.$getTitle($selection); if (currentName === false) { // 如果没有命中面板语法,则尝试扩大选区 - this.getMoreSelection('\n', '\n', () => { + this.getMoreSelection('::: ', '\n', () => { const newSelection = this.editor.editor.getSelection(); const isMatch = this.$getNameFromStr(newSelection); if (isMatch !== false) { $selection = newSelection; currentName = isMatch; + title = this.$getTitle(newSelection); } return isMatch !== false; }); @@ -95,21 +107,34 @@ export default class Panel extends MenuBase { // 去掉面板语法 this.panelRule.lastIndex = 0; return $selection.replace(this.panelRule, (match, preLines, name, content) => { - return content; + const $name = name.trim(); + const $title = /\s/.test($name) ? $name.replace(/[^\s]+\s/, '') : ''; + return `${$title}\n${content}`; }); } // 修改name this.registerAfterClickCb(() => { - this.setLessSelection('\n', '\n'); + this.setLessSelection('::: ', '\n'); }); this.panelRule.lastIndex = 0; return $selection.replace(this.panelRule, (match, preLines, name, content) => { - return `:::${shortKey}\n${content.replace(/\n+$/, '')}\n:::`; + const $name = name.trim(); + const $title = /\s/.test($name) ? $name.replace(/[^\s]+\s/, '') : ''; + return `::: ${shortKey} ${$title}\n${content.replace(/\n+$/, '')}\n:::`; }); } this.registerAfterClickCb(() => { - this.setLessSelection('\n', '\n'); + this.setLessSelection('::: ', '\n'); }); - return `:::${shortKey}\n${$selection}\n:::`.replace(/\n{2,}:::/g, '\n:::'); + $selection = $selection.replace(/^\n+/, ''); + if (/\n/.test($selection)) { + if (!title) { + title = $selection.replace(/\n[\w\W]+$/, ''); + $selection = $selection.replace(/^[^\n]+\n/, ''); + } + } else { + title = title ? title : '标题'; + } + return `::: ${shortKey} ${title}\n${$selection}\n:::`.replace(/\n{2,}:::/g, '\n:::'); } } diff --git a/src/utils/regexp.js b/src/utils/regexp.js index 9de2a1df..251254cc 100644 --- a/src/utils/regexp.js +++ b/src/utils/regexp.js @@ -211,7 +211,7 @@ export function getListFromStr(selection, type) { */ export function getPanelRule() { const ret = { - begin: /(?:^|\n)(\n*(?:[^\S\n]*)):::([^:\n\s]+?)\s*\n/, + begin: /(?:^|\n)(\n*(?:[^\S\n]*)):::([^:][^\n]+?)\s*\n/, content: /([\w\W]*?)/, end: /\n[ \t]*:::[ \t]*(?=$|\n+)/, }; @@ -222,26 +222,20 @@ export function getPanelRule() { /** * 手风琴/detail语法的识别正则 * 例: - * +++(+|-) - * 点击查看详情 - * : + * +++(-) 点击查看详情 * body * body - * +- - * 标题(默认收起内容) - * : + * ++ 标题(默认收起内容) * 内容 - * ++ - * 标题(默认展开内容) - * : + * ++- 标题(默认展开内容) * 内容2 * +++ * @returns {object} */ export function getDetailRule() { const ret = { - begin: /(?:^|\n)(\n*(?:[^\S\n]*))\+\+\+(\+|-)\s*\n/, - content: /([\w\W]+?\n:\n[\w\W]+?)/, + begin: /(?:^|\n)(\n*(?:[^\S\n]*))\+\+\+([-]{0,1})\s+([^\n]+)\n/, + content: /([\w\W]+?)/, end: /\n[ \t]*\+\+\+[ \t]*(?=$|\n+)/, }; ret.reg = new RegExp(ret.begin.source + ret.content.source + ret.end.source, 'g');