Replace SimpleMDE with custom Markdown editor
This commit is contained in:
2
assets/css/app.min.css
vendored
2
assets/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
7
assets/css/vendor.min.css
vendored
7
assets/css/vendor.min.css
vendored
File diff suppressed because one or more lines are too long
4
assets/js/app.min.js
vendored
4
assets/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
158
assets/js/components/text-editor.js
Normal file
158
assets/js/components/text-editor.js
Normal file
@@ -0,0 +1,158 @@
|
||||
Vue.component('texteditor', {
|
||||
props: ['text', 'name', 'labelPreview', 'labelWrite', 'placeholder', 'css', 'tabindex', 'required', 'autofocus'],
|
||||
template:
|
||||
'<div class="text-editor">' +
|
||||
'<div class="text-editor-toolbar">' +
|
||||
'<button v-if="!preview" v-on:click.prevent="togglePreview"><i class="fa fa-fw fa-eye"></i>{{ labelPreview }}</button>' +
|
||||
'<button v-if="preview" v-on:click.prevent="toggleEditor"><i class="fa fa-fw fa-pencil-square-o"></i>{{ labelWrite }}</button>' +
|
||||
'<button :disabled="isPreview" v-on:click.prevent="insertBoldTag"><i class="fa fa-bold fa-fw"></i></button>' +
|
||||
'<button :disabled="isPreview" v-on:click.prevent="insertItalicTag"><i class="fa fa-italic fa-fw"></i></button>' +
|
||||
'<button :disabled="isPreview" v-on:click.prevent="insertStrikethroughTag"><i class="fa fa-strikethrough fa-fw"></i></button>' +
|
||||
'<button :disabled="isPreview" v-on:click.prevent="insertQuoteTag"><i class="fa fa-quote-right fa-fw"></i></button>' +
|
||||
'<button :disabled="isPreview" v-on:click.prevent="insertBulletListTag"><i class="fa fa-list-ul fa-fw"></i></button>' +
|
||||
'<button :disabled="isPreview" v-on:click.prevent="insertCodeTag"><i class="fa fa-code fa-fw"></i></button>' +
|
||||
'</div>' +
|
||||
'<div v-show="!preview" class="text-editor-write-area">' +
|
||||
'<textarea ' +
|
||||
'v-model="text" ' +
|
||||
'name="{{ name }}" ' +
|
||||
'id="{{ getId }}" ' +
|
||||
'class="{{ css }}" ' +
|
||||
'tabindex="{{ tabindex }}" ' +
|
||||
':autofocus="hasAutofocus" ' +
|
||||
'placeholder="{{ placeholder }}" ' +
|
||||
'></textarea>' +
|
||||
'</div>' +
|
||||
'<div v-show="preview" class="text-editor-preview-area markdown">{{{ renderedText }}}</div>' +
|
||||
'</div>'
|
||||
,
|
||||
data: function() {
|
||||
return {
|
||||
id: null,
|
||||
preview: false,
|
||||
renderedText: '',
|
||||
textarea: null,
|
||||
selectionStart: 0,
|
||||
selectionEnd: 0
|
||||
};
|
||||
},
|
||||
ready: function() {
|
||||
this.textarea = document.getElementById(this.id);
|
||||
},
|
||||
computed: {
|
||||
hasAutofocus: function() {
|
||||
return this.autofocus === '1';
|
||||
},
|
||||
isPreview: function() {
|
||||
return this.preview;
|
||||
},
|
||||
getId: function() {
|
||||
if (! this.id) {
|
||||
var i = 0;
|
||||
var uniqueId;
|
||||
|
||||
while (true) {
|
||||
i++;
|
||||
uniqueId = 'text-editor-textarea-' + i;
|
||||
|
||||
if (! document.getElementById(uniqueId)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.id = uniqueId;
|
||||
}
|
||||
|
||||
return this.id;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleEditor: function() {
|
||||
this.preview = false;
|
||||
},
|
||||
togglePreview: function() {
|
||||
this.preview = true;
|
||||
this.renderedText = marked(this.text, {sanitize: true});
|
||||
},
|
||||
insertBoldTag: function() {
|
||||
this.insertEnclosedTag('**');
|
||||
},
|
||||
insertItalicTag: function() {
|
||||
this.insertEnclosedTag('_');
|
||||
},
|
||||
insertStrikethroughTag: function() {
|
||||
this.insertEnclosedTag('~~');
|
||||
},
|
||||
insertQuoteTag: function() {
|
||||
this.insertPrependTag('> ');
|
||||
},
|
||||
insertBulletListTag: function() {
|
||||
this.insertPrependTag('* ');
|
||||
},
|
||||
insertCodeTag: function() {
|
||||
this.insertBlockTag('```');
|
||||
},
|
||||
replaceTextRange: function(s, start, end, substitute) {
|
||||
return s.substring(0, start) + substitute + s.substring(end);
|
||||
},
|
||||
getSelectedText: function() {
|
||||
return this.text.substring(this.textarea.selectionStart, this.textarea.selectionEnd);
|
||||
},
|
||||
insertEnclosedTag: function(tag) {
|
||||
var selectedText = this.getSelectedText();
|
||||
|
||||
this.insertText(tag + selectedText + tag);
|
||||
this.setCursorBeforeClosingTag(tag);
|
||||
},
|
||||
insertPrependTag: function(tag) {
|
||||
var selectedText = this.getSelectedText();
|
||||
|
||||
if (selectedText.indexOf('\n') === -1) {
|
||||
this.insertText('\n' + tag + selectedText);
|
||||
} else {
|
||||
var lines = selectedText.split('\n');
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].indexOf(tag) === -1) {
|
||||
lines[i] = tag + lines[i];
|
||||
}
|
||||
}
|
||||
|
||||
this.insertText(lines.join('\n'));
|
||||
}
|
||||
},
|
||||
insertBlockTag: function(tag) {
|
||||
var selectedText = this.getSelectedText();
|
||||
|
||||
this.insertText('\n' + tag + '\n' + selectedText + '\n' + tag);
|
||||
this.setCursorBeforeClosingTag(tag, 2);
|
||||
},
|
||||
insertText: function(replacedText) {
|
||||
var result = false;
|
||||
|
||||
this.selectionStart = this.textarea.selectionStart;
|
||||
this.selectionEnd = this.textarea.selectionEnd;
|
||||
this.textarea.focus();
|
||||
|
||||
if (document.queryCommandSupported('insertText')) {
|
||||
result = document.execCommand('insertText', false, replacedText);
|
||||
}
|
||||
|
||||
if (! result) {
|
||||
try {
|
||||
document.execCommand("ms-beginUndoUnit");
|
||||
} catch (error) {}
|
||||
|
||||
this.textarea.value = this.replaceTextRange(this.text, this.textarea.selectionStart, this.textarea.selectionEnd, replacedText);
|
||||
|
||||
try {
|
||||
document.execCommand("ms-endUndoUnit");
|
||||
} catch (error) {}
|
||||
}
|
||||
},
|
||||
setCursorBeforeClosingTag: function(tag, offset) {
|
||||
var position = this.selectionEnd + tag.length + offset;
|
||||
this.textarea.setSelectionRange(position, position);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
Kanboard.Markdown = function(app) {
|
||||
this.app = app;
|
||||
this.editor = null;
|
||||
};
|
||||
|
||||
Kanboard.Markdown.prototype.onPopoverOpened = function() {
|
||||
this.listen();
|
||||
};
|
||||
|
||||
Kanboard.Markdown.prototype.onPopoverClosed = function() {
|
||||
this.listen();
|
||||
};
|
||||
|
||||
Kanboard.Markdown.prototype.listen = function() {
|
||||
var editors = $(".markdown-editor");
|
||||
|
||||
if (this.editor) {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
if (editors.length > 0) {
|
||||
this.show(editors[0]);
|
||||
}
|
||||
};
|
||||
|
||||
Kanboard.Markdown.prototype.destroy = function() {
|
||||
var cm = this.editor.codemirror;
|
||||
var wrapper = cm.getWrapperElement();
|
||||
|
||||
for (var item in ["toolbar", "statusbar", "sideBySide"]) {
|
||||
if (this.editor.gui[item]) {
|
||||
wrapper.parentNode.removeChild(this.editor.gui[item]);
|
||||
}
|
||||
}
|
||||
|
||||
cm.toTextArea();
|
||||
this.editor = null;
|
||||
};
|
||||
|
||||
Kanboard.Markdown.prototype.show = function(textarea) {
|
||||
var toolbar = ["bold", "italic", "strikethrough", "heading", "|", "unordered-list", "ordered-list", "link", "|", "code", "table"];
|
||||
|
||||
this.editor = new SimpleMDE({
|
||||
element: textarea,
|
||||
status: false,
|
||||
toolbarTips: false,
|
||||
autoDownloadFontAwesome: false,
|
||||
spellChecker: false,
|
||||
autosave: {
|
||||
enabled: false
|
||||
},
|
||||
forceSync: true,
|
||||
blockStyles: {
|
||||
italic: "_"
|
||||
},
|
||||
toolbar: textarea.hasAttribute("data-markdown-editor-disable-toolbar") ? false : toolbar,
|
||||
placeholder: textarea.getAttribute("placeholder")
|
||||
});
|
||||
};
|
||||
1172
assets/js/vendor.min.js
vendored
1172
assets/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -49,11 +49,15 @@ textarea:focus
|
||||
box-shadow: 0 0 8px rgba(82, 168, 236, 0.6)
|
||||
|
||||
textarea
|
||||
padding: 3px
|
||||
border: 1px solid #ccc
|
||||
width: 400px
|
||||
max-width: 99%
|
||||
height: 200px
|
||||
font-family: sans-serif
|
||||
font-size: size('normal')
|
||||
@include placeholder
|
||||
color: color('lighter')
|
||||
|
||||
select
|
||||
font-size: 1.0em
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
.markdown-editor-container
|
||||
max-width: 400px
|
||||
@import variables
|
||||
|
||||
div
|
||||
&.CodeMirror, &.CodeMirror-scroll
|
||||
max-height: 250px
|
||||
min-height: 200px
|
||||
|
||||
.markdown-editor-small div
|
||||
&.CodeMirror, &.CodeMirror-scroll
|
||||
min-height: 100px
|
||||
max-height: 180px
|
||||
|
||||
.form-column div.CodeMirror
|
||||
margin-bottom: 10px
|
||||
.text-editor
|
||||
button
|
||||
font-size: size('normal')
|
||||
border: none
|
||||
color: color('light')
|
||||
background: transparent
|
||||
&:hover
|
||||
color: link-color('primary')
|
||||
cursor: pointer
|
||||
.text-editor-preview-area
|
||||
border: 1px solid color('lighter')
|
||||
width: 400px
|
||||
height: 200px
|
||||
overflow: auto
|
||||
.text-editor-toolbar
|
||||
button:first-child
|
||||
padding-left: 0
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
@mixin placeholder
|
||||
&::-webkit-input-placeholder
|
||||
@content
|
||||
&:-moz-placeholder
|
||||
@content
|
||||
&::-moz-placeholder
|
||||
@content
|
||||
&:-ms-input-placeholder
|
||||
|
||||
Reference in New Issue
Block a user