diff --git a/examples/formula.html b/examples/formula.html
new file mode 100644
index 00000000..f1031505
--- /dev/null
+++ b/examples/formula.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Cherry Editor - Markdown Editor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/images/frac.svg b/examples/images/frac.svg
new file mode 100644
index 00000000..7f3b9b00
--- /dev/null
+++ b/examples/images/frac.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/images/minus.svg b/examples/images/minus.svg
new file mode 100644
index 00000000..3f9bd7ee
--- /dev/null
+++ b/examples/images/minus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/images/plus.svg b/examples/images/plus.svg
new file mode 100644
index 00000000..e203fcb2
--- /dev/null
+++ b/examples/images/plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/markdown/formula.md b/examples/markdown/formula.md
new file mode 100644
index 00000000..e8350d6d
--- /dev/null
+++ b/examples/markdown/formula.md
@@ -0,0 +1,27 @@
+# Formula
+
+## 数学公式
+
+### symbol
+
+$+$
+$-$
+
+### fraction
+
+$\frac{a}{b}$
+
+## 物理公式
+
+### symbol
+
+$\vec{a}$
+
+$\vec{v}$
+
+### mechanics
+
+$\vec{F}=m\vec{a}$
+
+$W=\int_{C} \vec{F} \cdot \mathrm{d} \vec{s}$
+
diff --git a/examples/scripts/formula.js b/examples/scripts/formula.js
new file mode 100644
index 00000000..84edbb40
--- /dev/null
+++ b/examples/scripts/formula.js
@@ -0,0 +1,314 @@
+/**
+ * 自定义一个语法
+ */
+var CustomHookA = Cherry.createSyntaxHook('codeBlock', Cherry.constants.HOOKS_TYPE_LIST.PAR, {
+ makeHtml(str) {
+ console.warn('custom hook', 'hello');
+ return str;
+ },
+ rule(str) {
+ const regex = {
+ begin: '',
+ content: '',
+ end: '',
+ };
+ regex.reg = new RegExp(regex.begin + regex.content + regex.end, 'g');
+ return regex;
+ },
+});
+/**
+ * 自定义一个自定义菜单
+ * 点第一次时,把选中的文字变成同时加粗和斜体
+ * 保持光标选区不变,点第二次时,把加粗斜体的文字变成普通文本
+ */
+var customMenuA = Cherry.createMenuHook('加粗斜体', {
+ iconName: 'font',
+ onClick: function (selection) {
+ // 获取用户选中的文字,调用getSelection方法后,如果用户没有选中任何文字,会尝试获取光标所在位置的单词或句子
+ let $selection = this.getSelection(selection) || '同时加粗斜体';
+ // 如果是单选,并且选中内容的开始结束内没有加粗语法,则扩大选中范围
+ if (!this.isSelections && !/^\s*(\*\*\*)[\s\S]+(\1)/.test($selection)) {
+ this.getMoreSelection('***', '***', () => {
+ const newSelection = this.editor.editor.getSelection();
+ const isBoldItalic = /^\s*(\*\*\*)[\s\S]+(\1)/.test(newSelection);
+ if (isBoldItalic) {
+ $selection = newSelection;
+ }
+ return isBoldItalic;
+ });
+ }
+ // 如果选中的文本中已经有加粗语法了,则去掉加粗语法
+ if (/^\s*(\*\*\*)[\s\S]+(\1)/.test($selection)) {
+ return $selection.replace(/(^)(\s*)(\*\*\*)([^\n]+)(\3)(\s*)($)/gm, '$1$4$7');
+ }
+ /**
+ * 注册缩小选区的规则
+ * 注册后,插入“***TEXT***”,选中状态会变成“***【TEXT】***”
+ * 如果不注册,插入后效果为:“【***TEXT***】”
+ */
+ this.registerAfterClickCb(() => {
+ this.setLessSelection('***', '***');
+ });
+ return $selection.replace(/(^)([^\n]+)($)/gm, '$1***$2***$3');
+ },
+});
+/**
+ * 定义一个空壳,用于自行规划cherry已有工具栏的层级结构
+ */
+var customMenuB = Cherry.createMenuHook('实验室', {
+ iconName: '',
+});
+/**
+ * 定义一个自带二级菜单的工具栏
+ */
+var customMenuC = Cherry.createMenuHook('帮助中心', {
+ iconName: 'question',
+ onClick: (selection, type) => {
+ switch (type) {
+ case 'shortKey':
+ return `${selection}快捷键看这里:https://codemirror.net/5/demo/sublime.html`;
+ case 'github':
+ return `${selection}我们在这里:https://github.com/Tencent/cherry-markdown`;
+ case 'release':
+ return `${selection}我们在这里:https://github.com/Tencent/cherry-markdown/releases`;
+ default:
+ return selection;
+ }
+ },
+ subMenuConfig: [
+ {
+ noIcon: true,
+ name: '快捷键',
+ onclick: (event) => {
+ cherry.toolbar.menus.hooks.customMenuCName.fire(null, 'shortKey');
+ },
+ },
+ {
+ noIcon: true,
+ name: '联系我们',
+ onclick: (event) => {
+ cherry.toolbar.menus.hooks.customMenuCName.fire(null, 'github');
+ },
+ },
+ {
+ noIcon: true,
+ name: '更新日志',
+ onclick: (event) => {
+ cherry.toolbar.menus.hooks.customMenuCName.fire(null, 'release');
+ },
+ },
+ ],
+});
+
+var basicConfig = {
+ id: 'markdown',
+ externals: {
+ echarts: window.echarts,
+ katex: window.katex,
+ MathJax: window.MathJax,
+ },
+ isPreviewOnly: false,
+ engine: {
+ global: {
+ urlProcessor(url, srcType) {
+ console.log(`url-processor`, url, srcType);
+ return url;
+ },
+ },
+ syntax: {
+ codeBlock: {
+ theme: 'twilight',
+ },
+ table: {
+ enableChart: false,
+ // chartEngine: Engine Class
+ },
+ fontEmphasis: {
+ allowWhitespace: false, // 是否允许首尾空格
+ },
+ strikethrough: {
+ needWhitespace: false, // 是否必须有前后空格
+ },
+ mathBlock: {
+ engine: 'MathJax', // katex或MathJax
+ src: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js', // 如果使用MathJax plugins,则需要使用该url通过script标签引入
+ },
+ inlineMath: {
+ engine: 'MathJax', // katex或MathJax
+ },
+ emoji: {
+ useUnicode: false,
+ customResourceURL: 'https://github.githubassets.com/images/icons/emoji/unicode/${code}.png?v8',
+ upperCase: true,
+ },
+ formula: {
+ config: {
+ math: {
+ title: '数学公式',
+ subCategory: {
+ symbol: {
+ title: '符号',
+ formulas: [
+ {
+ name: '加号',
+ latex: '+',
+ img: 'images/plus.svg',
+ imgStyle: 'width: 30px; height: 30px;',
+ imgClass: 'custom-img-class',
+ formulaStyle: 'border: 1px solid red;',
+ formulaClass: 'custom-formula-class',
+ },
+ {
+ name: '减号',
+ latex: '-',
+ img: 'images/minus.svg',
+ },
+ ],
+ },
+ fraction: {
+ title: '分式',
+ formulas: [
+ {
+ name: '分式',
+ latex: '\\frac{a}{b}',
+ img: 'images/frac.svg',
+ },
+ ],
+ },
+ },
+ },
+ physics: {
+ title: '物理公式',
+ subCategory: {
+ symbol: {
+ title: '符号',
+ formulas: [
+ {
+ name: '加速度',
+ latex: '\\vec{a}',
+ },
+ {
+ name: '速度',
+ latex: '\\vec{v}',
+ },
+ ],
+ },
+ // 力学公式
+ mechanics: {
+ title: '力学',
+ formulas: [
+ {
+ name: '牛顿第二定律',
+ latex: '\\vec{F}=m\\vec{a}',
+ },
+ {
+ name: '功',
+ latex: 'W=\\int_{C} \\vec{F} \\cdot \\mathrm{d} \\vec{s}',
+ },
+ ],
+ },
+ },
+ },
+ },
+ },
+ // toc: {
+ // tocStyle: 'nested'
+ // }
+ // 'header': {
+ // strict: false
+ // }
+ },
+ customSyntax: {
+ // SyntaxHookClass
+ CustomHook: {
+ syntaxClass: CustomHookA,
+ force: false,
+ after: 'br',
+ },
+ },
+ },
+ toolbars: {
+ toolbar: [
+ 'bold',
+ 'italic',
+ {
+ strikethrough: ['strikethrough', 'underline', 'sub', 'sup', 'ruby', 'customMenuAName'],
+ },
+ 'size',
+ '|',
+ 'color',
+ 'header',
+ '|',
+ 'drawIo',
+ '|',
+ 'ol',
+ 'ul',
+ 'checklist',
+ 'panel',
+ 'justify',
+ 'detail',
+ '|',
+ 'formula',
+ {
+ insert: [
+ 'image',
+ 'audio',
+ 'video',
+ 'link',
+ 'hr',
+ 'br',
+ 'code',
+ 'formula',
+ 'toc',
+ 'table',
+ 'pdf',
+ 'word',
+ 'ruby',
+ ],
+ },
+ 'graph',
+ 'togglePreview',
+ 'settings',
+ 'codeTheme',
+ 'export',
+ {
+ customMenuBName: ['ruby', 'audio', 'video', 'customMenuAName'],
+ },
+ 'customMenuCName',
+ 'theme',
+ ],
+ toolbarRight: ['fullScreen', '|'],
+ bubble: ['bold', 'italic', 'underline', 'strikethrough', 'sub', 'sup', 'quote', 'ruby', '|', 'size', 'color'], // array or false
+ sidebar: ['mobilePreview', 'copy', 'theme'],
+ customMenu: {
+ customMenuAName: customMenuA,
+ customMenuBName: customMenuB,
+ customMenuCName: customMenuC,
+ },
+ },
+ drawioIframeUrl: './drawio_demo.html',
+ editor: {
+ defaultModel: 'edit&preview',
+ },
+ previewer: {
+ // 自定义markdown预览区域class
+ // className: 'markdown'
+ },
+ keydown: [],
+ //extensions: [],
+ callback: {
+ changeString2Pinyin: pinyin,
+ },
+ editor: {
+ id: 'cherry-text',
+ name: 'cherry-text',
+ autoSave2Textarea: true,
+ },
+};
+
+fetch('./markdown/formula.md')
+ .then((response) => response.text())
+ .then((value) => {
+ var config = Object.assign({}, basicConfig, { value: value });
+ window.cherry = new Cherry(config);
+ });
diff --git a/src/sass/bubble_formula.scss b/src/sass/bubble_formula.scss
index 8190e4b2..c24d8499 100644
--- a/src/sass/bubble_formula.scss
+++ b/src/sass/bubble_formula.scss
@@ -1,10 +1,9 @@
.cherry-insert-formula-wrappler {
- width: 500px;
- height: 300px;
+ width: 500px !important; // 覆盖默认.cherry-dropdown样式
+ height: 300px !important;
padding: 15px;
display: flex;
- background-color: #fff;
- position: fixed;
+ position: fixed !important;
z-index: 9999999;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
box-sizing: border-box;
@@ -32,7 +31,9 @@
text-decoration: none;
user-select: none;
user-select: none;
- color: black;
+ }
+ &:not(:first-child) {
+ margin-top: 10px;
}
}
@@ -106,7 +107,6 @@
cursor: pointer;
display: inline-block;
font-weight: 400;
- color: #212529;
text-align: center;
vertical-align: middle;
user-select: none;
diff --git a/src/toolbars/BubbleFormula.js b/src/toolbars/BubbleFormula.js
index 40e0506e..d04e348c 100644
--- a/src/toolbars/BubbleFormula.js
+++ b/src/toolbars/BubbleFormula.js
@@ -51,12 +51,17 @@ export default class BubbleFormula {
*/
afterClick(latex) {}
- constructor() {
+ /**
+ * @param {Object.} formulaConfig
+ */
+ constructor(formulaConfig = {}) {
+ this.formulaConfig = formulaConfig;
this.init();
this.initEventListeners();
}
generateBubbleFormulaHtmlStr() {
+ console.log('this.formulaConfig: ', this.formulaConfig);
const entries = Object.entries(this.formulaConfig || {});
const liStr = entries
.map(
@@ -90,7 +95,7 @@ export default class BubbleFormula {
})
.join('');
const formulaCategaryFuncStr = `${formulaCategaryFuncInnerStr}
`;
- const formulaCategaryBtnStr = ``;
+ const formulaCategaryBtnStr = ``;
return `${formulaCategaryBtnStr}${formulaCategaryFuncStr}
`;
})
.join('');
@@ -103,11 +108,13 @@ export default class BubbleFormula {
}
init() {
- this.dom = document.createElement('div');
- this.dom.className = ['cherry-insert-formula', 'cherry-insert-formula-wrappler'].join(' ');
- this.dom.innerHTML = this.generateBubbleFormulaHtmlStr();
- // 实例化后,将容器插入到富文本编辑器中,默认隐藏
- this.dom.style.display = 'none';
+ if (Object.keys(this.formulaConfig).length) {
+ this.dom = document.createElement('div');
+ this.dom.className = ['cherry-dropdown', 'cherry-insert-formula', 'cherry-insert-formula-wrappler'].join(' ');
+ this.dom.innerHTML = this.generateBubbleFormulaHtmlStr();
+ // 实例化后,将容器插入到富文本编辑器中,默认隐藏
+ this.dom.style.display = 'none';
+ }
}
/**
diff --git a/src/toolbars/Toolbar.js b/src/toolbars/Toolbar.js
index 286ba92c..50452289 100644
--- a/src/toolbars/Toolbar.js
+++ b/src/toolbars/Toolbar.js
@@ -168,6 +168,7 @@ export default class Toolbar {
* 展开/收起二级菜单
*/
toggleSubMenu(name) {
+ console.log('name: ', name);
if (!this.subMenus[name]) {
// 如果没有二级菜单,则先画出来,然后再显示
this.hideAllSubMenu();
diff --git a/src/toolbars/hooks/Formula.js b/src/toolbars/hooks/Formula.js
index b2089e1a..d9d66c49 100644
--- a/src/toolbars/hooks/Formula.js
+++ b/src/toolbars/hooks/Formula.js
@@ -22,7 +22,7 @@ export default class Formula extends MenuBase {
constructor($cherry) {
super($cherry);
this.setName('formula', 'insertFormula');
- this.subBubbleFormulaMenu = new BubbleFormula();
+ this.subBubbleFormulaMenu = new BubbleFormula($cherry?.options?.engine?.syntax?.formula?.config || {});
$cherry.editor.options.wrapperDom.appendChild(this.subBubbleFormulaMenu.dom);
this.catchOnce = '';
}
diff --git a/types/syntax.d.ts b/types/syntax.d.ts
index f3566350..6cf40f5f 100644
--- a/types/syntax.d.ts
+++ b/types/syntax.d.ts
@@ -44,9 +44,4 @@ export interface BasicHookRegexpRule {
}
// TODO:
-export type HookRegexpRule = {
- begin: string;
- end: string;
- content: string;
- reg?: RegExp;
-};
+export type HookRegexpRule = {};