mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-08-26 20:22:18 +00:00
Updated the markdown editor to use codemirror as editor
Improved scroll sync system to be smarter
This commit is contained in:
parent
e5fc6bf5fa
commit
88f93f76dd
@ -37,3 +37,20 @@ module.exports.highlight = function() {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.markdownEditor = function(elem) {
|
||||||
|
let content = elem.textContent;
|
||||||
|
|
||||||
|
let cm = CodeMirror(function(elt) {
|
||||||
|
elem.parentNode.insertBefore(elt, elem);
|
||||||
|
elem.style.display = 'none';
|
||||||
|
}, {
|
||||||
|
value: content,
|
||||||
|
mode: "markdown",
|
||||||
|
lineNumbers: true,
|
||||||
|
theme: 'base16-light',
|
||||||
|
lineWrapping: true
|
||||||
|
});
|
||||||
|
return cm;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
const DropZone = require("dropzone");
|
const DropZone = require("dropzone");
|
||||||
const MarkdownIt = require("markdown-it");
|
const MarkdownIt = require("markdown-it");
|
||||||
const mdTasksLists = require('markdown-it-task-lists');
|
const mdTasksLists = require('markdown-it-task-lists');
|
||||||
|
const code = require('./code');
|
||||||
|
|
||||||
module.exports = function (ngApp, events) {
|
module.exports = function (ngApp, events) {
|
||||||
|
|
||||||
@ -233,19 +234,41 @@ module.exports = function (ngApp, events) {
|
|||||||
|
|
||||||
// Set initial model content
|
// Set initial model content
|
||||||
element = element.find('textarea').first();
|
element = element.find('textarea').first();
|
||||||
let content = element.val();
|
|
||||||
scope.mdModel = content;
|
|
||||||
scope.mdChange(md.render(content));
|
|
||||||
|
|
||||||
element.on('change input', (event) => {
|
// Codemirror Setup
|
||||||
content = element.val();
|
let cm = code.markdownEditor(element[0]);
|
||||||
|
cm.on('change', (instance, changeObj) => {
|
||||||
|
update(instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
cm.on('scroll', instance => {
|
||||||
|
// Thanks to http://liuhao.im/english/2015/11/10/the-sync-scroll-of-markdown-editor-in-javascript.html
|
||||||
|
let scroll = instance.getScrollInfo();
|
||||||
|
let atEnd = scroll.top + scroll.clientHeight === scroll.height;
|
||||||
|
if (atEnd) {
|
||||||
|
scope.$emit('markdown-scroll', -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let lineNum = instance.lineAtHeight(scroll.top, 'local');
|
||||||
|
let range = instance.getRange({line: 0, ch: null}, {line: lineNum, ch: null});
|
||||||
|
let parser = new DOMParser();
|
||||||
|
let doc = parser.parseFromString(md.render(range), 'text/html');
|
||||||
|
let totalLines = doc.documentElement.querySelectorAll('body > *');
|
||||||
|
scope.$emit('markdown-scroll', totalLines.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
function update(instance) {
|
||||||
|
let content = instance.getValue();
|
||||||
|
element.val(content);
|
||||||
$timeout(() => {
|
$timeout(() => {
|
||||||
scope.mdModel = content;
|
scope.mdModel = content;
|
||||||
scope.mdChange(md.render(content));
|
scope.mdChange(md.render(content));
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
update(cm);
|
||||||
|
|
||||||
scope.$on('markdown-update', (event, value) => {
|
scope.$on('markdown-update', (event, value) => {
|
||||||
|
cm.setValue(value);
|
||||||
element.val(value);
|
element.val(value);
|
||||||
scope.mdModel = value;
|
scope.mdModel = value;
|
||||||
scope.mdChange(md.render(value));
|
scope.mdChange(md.render(value));
|
||||||
@ -259,7 +282,7 @@ module.exports = function (ngApp, events) {
|
|||||||
* Markdown Editor
|
* Markdown Editor
|
||||||
* Handles all functionality of the markdown editor.
|
* Handles all functionality of the markdown editor.
|
||||||
*/
|
*/
|
||||||
ngApp.directive('markdownEditor', ['$timeout', function ($timeout) {
|
ngApp.directive('markdownEditor', ['$timeout', '$rootScope', function ($timeout, $rootScope) {
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function (scope, element, attrs) {
|
link: function (scope, element, attrs) {
|
||||||
@ -282,34 +305,15 @@ module.exports = function (ngApp, events) {
|
|||||||
currentCaretPos = $input[0].selectionStart;
|
currentCaretPos = $input[0].selectionStart;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Scroll sync
|
// Handle scroll sync event from editor scroll
|
||||||
let inputScrollHeight,
|
$rootScope.$on('markdown-scroll', (event, lineCount) => {
|
||||||
inputHeight,
|
let elems = $display[0].children[0].children;
|
||||||
displayScrollHeight,
|
if (elems.length > lineCount) {
|
||||||
displayHeight;
|
let topElem = (lineCount === -1) ? elems[elems.length-1] : elems[lineCount];
|
||||||
|
$display.animate({
|
||||||
function setScrollHeights() {
|
scrollTop: topElem.offsetTop
|
||||||
inputScrollHeight = $input[0].scrollHeight;
|
}, {queue: false, duration: 200, easing: 'linear'});
|
||||||
inputHeight = $input.height();
|
|
||||||
displayScrollHeight = $display[0].scrollHeight;
|
|
||||||
displayHeight = $display.height();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setScrollHeights();
|
|
||||||
}, 200);
|
|
||||||
window.addEventListener('resize', setScrollHeights);
|
|
||||||
let scrollDebounceTime = 800;
|
|
||||||
let lastScroll = 0;
|
|
||||||
$input.on('scroll', event => {
|
|
||||||
let now = Date.now();
|
|
||||||
if (now - lastScroll > scrollDebounceTime) {
|
|
||||||
setScrollHeights()
|
|
||||||
}
|
}
|
||||||
let scrollPercent = ($input.scrollTop() / (inputScrollHeight - inputHeight));
|
|
||||||
let displayScrollY = (displayScrollHeight - displayHeight) * scrollPercent;
|
|
||||||
$display.scrollTop(displayScrollY);
|
|
||||||
lastScroll = now;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Editor key-presses
|
// Editor key-presses
|
||||||
|
@ -350,7 +350,7 @@ span.CodeMirror-selectedtext { background: none; }
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.cm-s-base16-light.CodeMirror { background: #f5f5f5; color: #202020; }
|
.cm-s-base16-light.CodeMirror { background: #f8f8f8; color: #444444; }
|
||||||
.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; }
|
.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; }
|
||||||
.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; }
|
.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; }
|
||||||
.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; }
|
.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; }
|
||||||
@ -388,4 +388,13 @@ span.CodeMirror-selectedtext { background: none; }
|
|||||||
margin-bottom: $-l;
|
margin-bottom: $-l;
|
||||||
border: 1px solid #DDD;;
|
border: 1px solid #DDD;;
|
||||||
}
|
}
|
||||||
.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 1px solid #DDD; }
|
.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 1px solid #DDD; }
|
||||||
|
|
||||||
|
.flex-fill .CodeMirror {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
@ -11,16 +11,17 @@ body.flexbox {
|
|||||||
#content {
|
#content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 0px;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-fill {
|
.flex-fill {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
min-height: 0px;
|
min-height: 0;
|
||||||
|
position: relative;
|
||||||
.flex, &.flex {
|
.flex, &.flex {
|
||||||
min-height: 0px;
|
min-height: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user