diff --git a/packages/ui-default/components/autocomplete/index.js b/packages/ui-default/components/autocomplete/index.js index 6dc0c1f7f..0fb335cf6 100644 --- a/packages/ui-default/components/autocomplete/index.js +++ b/packages/ui-default/components/autocomplete/index.js @@ -114,13 +114,9 @@ export default class AutoComplete extends DOMAttachedObject { } async getItems(val) { - if (this.cache[val] !== undefined) { - return this.cache[val]; - } + if (this.cache[val] !== undefined) return this.cache[val]; const data = await this.options.items(val); - if (this.options.cache) { - this.cache[val] = data; - } + if (this.options.cache) this.cache[val] = data; return data; } @@ -148,9 +144,7 @@ export default class AutoComplete extends DOMAttachedObject { } open() { - if (this.isOpen) { - return; - } + if (this.isOpen) return; this.dropInstance.open(); this.isOpen = true; } diff --git a/packages/ui-default/components/cmeditor/EditorComponent.jsx b/packages/ui-default/components/cmeditor/EditorComponent.jsx new file mode 100644 index 000000000..baea7a6c2 --- /dev/null +++ b/packages/ui-default/components/cmeditor/EditorComponent.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import Vditor from 'vditor'; +import { config } from './index'; + +export default class MarkdownEditor extends React.PureComponent { + constructor(props) { + super(props); + this.ref = React.createRef(); + } + + async componentDidMount() { + await new Promise((resolve) => { + this.editor = new Vditor(this.ref.current, { + ...config, + after: resolve, + input(value) { this.props.onChange(value); }, + cache: { id: Math.random().toString() }, + }); + }); + } + + componentWillUnmount() { + this.editor.destory(); + this.editor.detach(); + } + + value() { + return this.editor.getValue(); + } + + focus() { + return this.editor.focus(); + } + + render() { + return
; + } +} diff --git a/packages/ui-default/components/cmeditor/cmeditor.styl b/packages/ui-default/components/cmeditor/cmeditor.styl index 6c00da3f4..d28d5640b 100644 --- a/packages/ui-default/components/cmeditor/cmeditor.styl +++ b/packages/ui-default/components/cmeditor/cmeditor.styl @@ -1,91 +1,2 @@ -.simplemde - border: 1px solid $editor-border-color - margin: 0 0 1rem - position: relative - height: rem(200px) - box-sizing: border-box - overflow: hidden - -.cm-s-simplemde - border: 0 - min-height: 0 - height: auto - padding: 0 - position: absolute - left: 0 - top: 40px - width: 100% - bottom: 0 - color: inherit - -.simplemde-toolbar - position: absolute - left: 0 - top: 0 - width: 100% - border-radius: 0 - border: 0 - opacity: 0 - background: $toolbar-bg - transform: translateY(-20px) - transition: opacity .5s, transform .5s - transition-timing-function: ease-out-cubic - - &:hover - opacity: 1 - -.toolbar--visible .simplemde-toolbar - opacity: 1 - transform: translateY(0) - -.cm-s-simplemde .CodeMirror-scroll - height: 100% - min-height: 0 - font-family: $code-font-family - color: $text-1-color - -.simplemde-toolbar:before, -.simplemde-toolbar:after - display: none - -.simplemde-toolbar - padding: rem(5px) - -.simplemde-toolbar i.separator - border-right: 0 - -.simplemde-toolbar a - color: $toolbar-fore !important - border: 0 - background: none - border-radius: 0 - font-size: rem($font-size-icon) - outline: 0 - - &:hover - background: $toolbar-bg-hover - color: $toolbar-fore-hover !important - border: 0 - - &.active - background: $toolbar-bg-selected - color: $toolbar-fore-selected !important - border: 0 - -.simplemde-toolbar.disabled-for-preview a:not(.no-disable) - background: none - opacity: $toolbar-disabled-opacity - -.cm-s-simplemde .CodeMirror-code .cm-comment - background: none - -.simplemde-statusbar - display: none - -.simplemde-preview - background: #FFF - -.no-heading .cm-s-simplemde .CodeMirror-code - .cm-header-1, .cm-header-2, .cm-header-3, .cm-header-4 - font-size: 1em - line-height: 1.5 +.vditor + margin-bottom: rem(10px) diff --git a/packages/ui-default/components/cmeditor/index.js b/packages/ui-default/components/cmeditor/index.js index bf6cea053..7f935684a 100644 --- a/packages/ui-default/components/cmeditor/index.js +++ b/packages/ui-default/components/cmeditor/index.js @@ -1,65 +1,83 @@ import _ from 'lodash'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; +export const config = { + toolbar: [ + 'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|', + 'list', 'ordered-list', 'check', 'outdent', 'indent', '|', + 'quote', 'line', 'code', 'inline-code', '|', + 'upload', 'record', 'table', '|', + 'edit-mode', 'content-theme', 'code-theme', 'export', 'preview', + ], + mode: 'ir', + toolbarConfig: { + pin: true, + }, + counter: { + enable: true, + max: 65536, + }, +}; + export default class CmEditor extends DOMAttachedObject { static DOMAttachKey = 'vjCmEditorInstance'; constructor($dom, options = {}) { super($dom); - this.editor = null; this.options = options; this.init(); } async init() { - const { default: VjCmEditor } = await import('./vjcmeditor'); - - const hasFocus = this.$dom.is(':focus'); - const srcHeight = this.$dom.outerHeight(); - - this.editor = new VjCmEditor({ - ...this.options, - element: this.$dom.get(0), + const { default: Vditor } = await import('vditor'); + const { $dom } = this; + const hasFocus = $dom.is(':focus'); + const origin = $dom.get(0); + const ele = document.createElement('div'); + const value = $dom.text(); + await new Promise((resolve) => { + this.editor = new Vditor(ele, { + ...config, + after: resolve, + input(v) { $dom.text(v); }, + value, + cache: { id: Math.random().toString() }, + upload: { + accept: 'image/*,.mp3, .wav, .zip', + url: '/api/upload/editor', + linkToImgUrl: '/api/upload/fetch', + filename(name) { + return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '') + .replace(/[?\\/:|<>*[\]()$%{}@~]/g, '') + .replace('/\\s/g', ''); + }, + }, + }); }); - this.moveToEnd(); - - const $editor = $(this.editor.wrapper); - $editor.css('height', srcHeight); - $editor.addClass('toolbar--visible'); - - if (hasFocus) { - this.focus(); - } - } - - isValid() { - return (this.editor !== null); + $(ele).addClass('textbox'); + $dom.hide(); + origin.parentElement.appendChild(ele); + if (hasFocus) this.focus(); + this.isValid = true; } ensureValid() { - if (!this.isValid()) { - throw new Error('VjCmEditor is not loaded'); - } + if (!this.isValid) throw new Error('Editor is not loaded'); } value(...argv) { this.ensureValid(); const ret = this.editor.value(...argv); - if (argv.length > 0) { - this.moveToEnd(); - } return ret; } + destory() { + this.editor.destory(); + } + focus() { this.ensureValid(); - this.editor.codemirror.focus(); - } - - moveToEnd() { - this.ensureValid(); - const cm = this.editor.codemirror; - cm.setCursor(cm.lineCount(), 0); + this.editor.focus(); } } diff --git a/packages/ui-default/components/cmeditor/vjcmeditor.js b/packages/ui-default/components/cmeditor/vjcmeditor.js deleted file mode 100644 index 008b02650..000000000 --- a/packages/ui-default/components/cmeditor/vjcmeditor.js +++ /dev/null @@ -1,120 +0,0 @@ -import _ from 'lodash'; -import SimpleMDE from 'vj-simplemde'; - -import request from 'vj/utils/request'; -import i18n from 'vj/utils/i18n'; - -export default class VjCmEditor extends SimpleMDE { - constructor(options = {}) { - super({ - autoDownloadFontAwesome: false, - spellChecker: false, - forceSync: true, - toolbar: [ - { - name: 'bold', - action: SimpleMDE.toggleBold, - className: 'icon icon-bold', - title: i18n('Bold'), - }, - { - name: 'italic', - action: SimpleMDE.toggleItalic, - className: 'icon icon-italic', - title: i18n('Italic'), - }, - '|', - { - name: 'quote', - action: SimpleMDE.toggleBlockquote, - className: 'icon icon-quote', - title: i18n('Quote'), - }, - { - name: 'unordered-list', - action: SimpleMDE.toggleUnorderedList, - className: 'icon icon-unordered_list', - title: i18n('Unordered List'), - }, - { - name: 'ordered-list', - action: SimpleMDE.toggleOrderedList, - className: 'icon icon-ordered_list', - title: i18n('Ordered List'), - }, - '|', - { - name: 'code', - action: () => this.insertCodeBlock(), - className: 'icon icon-code', - title: i18n('Insert Code'), - default: true, - }, - { - name: 'link', - action: SimpleMDE.drawLink, - className: 'icon icon-link', - title: i18n('Create Link'), - default: true, - }, - { - name: 'image', - action: SimpleMDE.drawImage, - className: 'icon icon-insert--image', - title: i18n('Insert Image'), - default: true, - }, - '|', - { - name: 'preview', - action: SimpleMDE.togglePreview, - preAction: SimpleMDE.preRenderPreview, - className: 'icon icon-preview no-disable', - title: i18n('Toggle Preview'), - default: true, - }, - ], - commonmark: { - safe: true, - }, - ...options, - }); - } - - async markdown(text) { - // FIXME preview wont update after first toggle - const data = await request.ajax({ - url: '/markdown', - method: 'post', - data: $.param({ text }, true), - dataType: 'text', - }); - _.defer(this.preparePreview.bind(this)); - return data; - } - - preparePreview() { - const $preview = $(this.wrapper).find('.simplemde-preview'); - $preview.addClass('typo'); - $preview.attr('data-emoji-enabled', 'true'); - $preview.trigger('vjContentNew'); - } - - insertCodeBlock() { - const text = this.codemirror.getSelection(); - const startPoint = this.codemirror.getCursor('start'); - const endPoint = this.codemirror.getCursor('end'); - const leadingLines = (startPoint.line === 0 && startPoint.ch === 0) ? 0 : 2; - // eslint-disable-next-line prefer-template - const startText = _.repeat('\n', leadingLines) + '```cpp\n'; - const endText = '\n```\n'; - this.codemirror.replaceSelection(`${startText}${text}${endText}`); - startPoint.line += leadingLines + 1; - startPoint.ch = 0; - if (startPoint !== endPoint) { - endPoint.line += leadingLines + 1; - } - this.codemirror.setSelection(startPoint, endPoint); - this.codemirror.focus(); - } -} diff --git a/packages/ui-default/components/discussion/comments.page.styl b/packages/ui-default/components/discussion/comments.page.styl index 4cb8106bc..0967e1019 100644 --- a/packages/ui-default/components/discussion/comments.page.styl +++ b/packages/ui-default/components/discussion/comments.page.styl @@ -8,11 +8,11 @@ $downvote-color = red display: none .dczcomments__box .textbox - height: rem(180px) + min-height: rem(180px) resize: vertical .textbox[name="dczcomments__dummy-box"] - height: rem(60px) + min-height: rem(60px) resize: none font-size: rem($font-size-title) color: $comment-placeholder-color diff --git a/packages/ui-default/components/form/textbox.page.styl b/packages/ui-default/components/form/textbox.page.styl index feda6bc00..6ee8822a7 100644 --- a/packages/ui-default/components/form/textbox.page.styl +++ b/packages/ui-default/components/form/textbox.page.styl @@ -23,6 +23,9 @@ input.textbox, textarea.textbox textarea.textbox height: rem(200px) +div.textbox + min-height: rem(200px) + .data-table input.textbox, textarea.textbox margin-bottom: 0 diff --git a/packages/ui-default/components/highlighter/codemirror.page.styl b/packages/ui-default/components/highlighter/codemirror.page.styl index c18494ba8..8daa5f351 100644 --- a/packages/ui-default/components/highlighter/codemirror.page.styl +++ b/packages/ui-default/components/highlighter/codemirror.page.styl @@ -7,6 +7,4 @@ span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; } span.cm-variable { color: black; } span.cm-meta { color: #2b91af; } span.cm-link { color: #3a3; } -.CodeMirror-activeline-background { background: #e8f2ff; } -.CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; } } diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 5754fce01..9d53522e8 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -34,7 +34,6 @@ "chalk": "^4.1.0", "classnames": "^2.3.1", "clipboard": "^2.0.8", - "codemirror": "^5.59.4", "copy-webpack-plugin": "^6.1.1", "css-loader": "^4.2.2", "diff-dom": "^4.2.2", @@ -95,8 +94,8 @@ "timeago-react": "^3.0.2", "timeago.js": "^4.0.2", "uuid": "^8.3.2", + "vditor": "^3.8.4", "vinyl-buffer": "^1.0.1", - "vj-simplemde": "0.0.8", "wastyle": "^0.0.5", "webpack": "^4.44.2", "webpack-bundle-analyzer": "^4.4.0", diff --git a/packages/ui-default/pages/problem_edit.page.js b/packages/ui-default/pages/problem_edit.page.js index f383a971b..04ad9511e 100644 --- a/packages/ui-default/pages/problem_edit.page.js +++ b/packages/ui-default/pages/problem_edit.page.js @@ -57,7 +57,6 @@ function parseCategorySelection() { $txt.val().split(',').map((name) => name.trim()).forEach((name) => { if (!name) return; const [category, subcategory] = findCategory(name); - console.log(name, category, subcategory); if (!category) tags.push(name); else if (!subcategory) { categories[category].select = true; diff --git a/packages/ui-default/theme/98.js b/packages/ui-default/theme/98.js index cf5363872..f358ccdd0 100644 --- a/packages/ui-default/theme/98.js +++ b/packages/ui-default/theme/98.js @@ -1,6 +1,4 @@ import 'normalize.css/normalize.css'; -import 'codemirror/lib/codemirror.css'; -import 'vj-simplemde/src/css/simplemde.css'; import 'pickadate/lib/themes/classic.css'; import 'pickadate/lib/themes/classic.date.css'; import 'pickadate/lib/themes/classic.time.css'; diff --git a/packages/ui-default/theme/default.js b/packages/ui-default/theme/default.js index 1a27f0085..7623e0ea9 100644 --- a/packages/ui-default/theme/default.js +++ b/packages/ui-default/theme/default.js @@ -1,6 +1,5 @@ import 'normalize.css/normalize.css'; -import 'codemirror/lib/codemirror.css'; -import 'vj-simplemde/src/css/simplemde.css'; +import 'vditor/dist/index.css'; import 'pickadate/lib/themes/classic.css'; import 'pickadate/lib/themes/classic.date.css'; import 'pickadate/lib/themes/classic.time.css'; diff --git a/packages/ui-default/utils/cloneAttributes.js b/packages/ui-default/utils/cloneAttributes.js new file mode 100644 index 000000000..2748600d4 --- /dev/null +++ b/packages/ui-default/utils/cloneAttributes.js @@ -0,0 +1,6 @@ +export default function cloneAttributes(from, to) { + const attributes = from.prop('attributes'); + $.each(attributes, function () { + to.attr(this.name, this.value); + }); +}