mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-23 23:29:59 +08:00
Added drawio abilities to markdown editor
This commit is contained in:
@ -2,6 +2,8 @@ const MarkdownIt = require("markdown-it");
|
|||||||
const mdTasksLists = require('markdown-it-task-lists');
|
const mdTasksLists = require('markdown-it-task-lists');
|
||||||
const code = require('../libs/code');
|
const code = require('../libs/code');
|
||||||
|
|
||||||
|
const DrawIO = require('../libs/drawio');
|
||||||
|
|
||||||
class MarkdownEditor {
|
class MarkdownEditor {
|
||||||
|
|
||||||
constructor(elem) {
|
constructor(elem) {
|
||||||
@ -20,13 +22,26 @@ class MarkdownEditor {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
||||||
|
let lastClick = 0;
|
||||||
|
|
||||||
// Prevent markdown display link click redirect
|
// Prevent markdown display link click redirect
|
||||||
this.display.addEventListener('click', event => {
|
this.display.addEventListener('click', event => {
|
||||||
let link = event.target.closest('a');
|
let isDblClick = Date.now() - lastClick < 300;
|
||||||
if (link === null) return;
|
|
||||||
|
|
||||||
event.preventDefault();
|
let link = event.target.closest('a');
|
||||||
window.open(link.getAttribute('href'));
|
if (link !== null) {
|
||||||
|
event.preventDefault();
|
||||||
|
window.open(link.getAttribute('href'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let drawing = event.target.closest('[drawio-diagram]');
|
||||||
|
if (drawing !== null && isDblClick) {
|
||||||
|
this.actionEditDrawing(drawing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastClick = Date.now();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Button actions
|
// Button actions
|
||||||
@ -37,6 +52,7 @@ class MarkdownEditor {
|
|||||||
let action = button.getAttribute('data-action');
|
let action = button.getAttribute('data-action');
|
||||||
if (action === 'insertImage') this.actionInsertImage();
|
if (action === 'insertImage') this.actionInsertImage();
|
||||||
if (action === 'insertLink') this.actionShowLinkSelector();
|
if (action === 'insertLink') this.actionShowLinkSelector();
|
||||||
|
if (action === 'insertDrawing') this.actionStartDrawing();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.$events.listen('editor-markdown-update', value => {
|
window.$events.listen('editor-markdown-update', value => {
|
||||||
@ -290,6 +306,70 @@ class MarkdownEditor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show draw.io if enabled and handle save.
|
||||||
|
actionStartDrawing() {
|
||||||
|
if (document.querySelector('[drawio-enabled]').getAttribute('drawio-enabled') !== 'true') return;
|
||||||
|
let cursorPos = this.cm.getCursor('from');
|
||||||
|
|
||||||
|
DrawIO.show(() => {
|
||||||
|
return Promise.resolve('');
|
||||||
|
}, (pngData) => {
|
||||||
|
// let id = "image-" + Math.random().toString(16).slice(2);
|
||||||
|
// let loadingImage = window.baseUrl('/loading.gif');
|
||||||
|
let data = {
|
||||||
|
image: pngData,
|
||||||
|
uploaded_to: Number(document.getElementById('page-editor').getAttribute('page-id'))
|
||||||
|
};
|
||||||
|
|
||||||
|
window.$http.post(window.baseUrl('/images/drawing/upload'), data).then(resp => {
|
||||||
|
let newText = `<div drawio-diagram="${resp.data.id}"><img src="${resp.data.url}"></div>`;
|
||||||
|
this.cm.focus();
|
||||||
|
this.cm.replaceSelection(newText);
|
||||||
|
this.cm.setCursor(cursorPos.line, cursorPos.ch + newText.length);
|
||||||
|
DrawIO.close();
|
||||||
|
}).catch(err => {
|
||||||
|
window.$events.emit('error', trans('errors.image_upload_error'));
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show draw.io if enabled and handle save.
|
||||||
|
actionEditDrawing(imgContainer) {
|
||||||
|
if (document.querySelector('[drawio-enabled]').getAttribute('drawio-enabled') !== 'true') return;
|
||||||
|
let cursorPos = this.cm.getCursor('from');
|
||||||
|
let drawingId = imgContainer.getAttribute('drawio-diagram');
|
||||||
|
|
||||||
|
DrawIO.show(() => {
|
||||||
|
return window.$http.get(window.baseUrl(`/images/base64/${drawingId}`)).then(resp => {
|
||||||
|
return `data:image/png;base64,${resp.data.content}`;
|
||||||
|
});
|
||||||
|
}, (pngData) => {
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
image: pngData,
|
||||||
|
uploaded_to: Number(document.getElementById('page-editor').getAttribute('page-id'))
|
||||||
|
};
|
||||||
|
|
||||||
|
window.$http.put(window.baseUrl(`/images/drawing/upload/${drawingId}`), data).then(resp => {
|
||||||
|
let newText = `<div drawio-diagram="${resp.data.id}"><img src="${resp.data.url + `?updated=${Date.now()}`}"></div>`;
|
||||||
|
let newContent = this.cm.getValue().split('\n').map(line => {
|
||||||
|
if (line.indexOf(`drawio-diagram="${drawingId}"`) !== -1) {
|
||||||
|
return newText;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}).join('\n');
|
||||||
|
this.cm.setValue(newContent);
|
||||||
|
this.cm.setCursor(cursorPos);
|
||||||
|
this.cm.focus();
|
||||||
|
DrawIO.close();
|
||||||
|
}).catch(err => {
|
||||||
|
window.$events.emit('error', trans('errors.image_upload_error'));
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MarkdownEditor ;
|
module.exports = MarkdownEditor ;
|
@ -9,7 +9,7 @@ let onInit, onSave;
|
|||||||
* @param onInitCallback - Must return a promise with the xml to load for the editor.
|
* @param onInitCallback - Must return a promise with the xml to load for the editor.
|
||||||
* @param onSaveCallback - Is called with the drawing data on save.
|
* @param onSaveCallback - Is called with the drawing data on save.
|
||||||
*/
|
*/
|
||||||
export function show(onInitCallback, onSaveCallback) {
|
function show(onInitCallback, onSaveCallback) {
|
||||||
onInit = onInitCallback;
|
onInit = onInitCallback;
|
||||||
onSave = onSaveCallback;
|
onSave = onSaveCallback;
|
||||||
|
|
||||||
@ -19,9 +19,10 @@ export function show(onInitCallback, onSaveCallback) {
|
|||||||
iFrame.setAttribute('src', drawIoUrl);
|
iFrame.setAttribute('src', drawIoUrl);
|
||||||
iFrame.setAttribute('class', 'fullscreen');
|
iFrame.setAttribute('class', 'fullscreen');
|
||||||
iFrame.style.backgroundColor = '#FFFFFF';
|
iFrame.style.backgroundColor = '#FFFFFF';
|
||||||
|
document.body.appendChild(iFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function close() {
|
function close() {
|
||||||
drawEventClose();
|
drawEventClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ function drawEventSave(message) {
|
|||||||
function drawEventInit() {
|
function drawEventInit() {
|
||||||
if (!onInit) return;
|
if (!onInit) return;
|
||||||
onInit().then(xml => {
|
onInit().then(xml => {
|
||||||
drawPostMessage({action: 'load', autosave: 1, xml: ''});
|
drawPostMessage({action: 'load', autosave: 1, xml: xml});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,3 +65,5 @@ function drawEventClose() {
|
|||||||
function drawPostMessage(data) {
|
function drawPostMessage(data) {
|
||||||
iFrame.contentWindow.postMessage(JSON.stringify(data), '*');
|
iFrame.contentWindow.postMessage(JSON.stringify(data), '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = {show, close};
|
@ -59,16 +59,21 @@
|
|||||||
border: 1px solid #DDD;
|
border: 1px solid #DDD;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
.markdown-display {
|
}
|
||||||
padding: 0 $-m 0;
|
|
||||||
margin-left: -1px;
|
.markdown-display {
|
||||||
overflow-y: scroll;
|
padding: 0 $-m 0;
|
||||||
}
|
margin-left: -1px;
|
||||||
.markdown-display.page-content {
|
overflow-y: scroll;
|
||||||
|
&.page-content {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
[drawio-diagram]:hover {
|
||||||
|
outline: 2px solid $primary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-toolbar {
|
.editor-toolbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: $-xs $-m;
|
padding: $-xs $-m;
|
||||||
|
@ -162,6 +162,7 @@ return [
|
|||||||
'pages_md_preview' => 'Preview',
|
'pages_md_preview' => 'Preview',
|
||||||
'pages_md_insert_image' => 'Insert Image',
|
'pages_md_insert_image' => 'Insert Image',
|
||||||
'pages_md_insert_link' => 'Insert Entity Link',
|
'pages_md_insert_link' => 'Insert Entity Link',
|
||||||
|
'pages_md_insert_drawing' => 'Insert Drawing',
|
||||||
'pages_not_in_chapter' => 'Page is not in a chapter',
|
'pages_not_in_chapter' => 'Page is not in a chapter',
|
||||||
'pages_move' => 'Move Page',
|
'pages_move' => 'Move Page',
|
||||||
'pages_move_success' => 'Page moved to ":parentName"',
|
'pages_move_success' => 'Page moved to ":parentName"',
|
||||||
|
@ -86,6 +86,10 @@
|
|||||||
<div class="editor-toolbar">
|
<div class="editor-toolbar">
|
||||||
<span class="float left">{{ trans('entities.pages_md_editor') }}</span>
|
<span class="float left">{{ trans('entities.pages_md_editor') }}</span>
|
||||||
<div class="float right buttons">
|
<div class="float right buttons">
|
||||||
|
@if(config('services.drawio'))
|
||||||
|
<button class="text-button" type="button" data-action="insertDrawing"><i class="zmdi zmdi-widgets"></i>{{ trans('entities.pages_md_insert_drawing') }}</button>
|
||||||
|
| 
|
||||||
|
@endif
|
||||||
<button class="text-button" type="button" data-action="insertImage"><i class="zmdi zmdi-image"></i>{{ trans('entities.pages_md_insert_image') }}</button>
|
<button class="text-button" type="button" data-action="insertImage"><i class="zmdi zmdi-image"></i>{{ trans('entities.pages_md_insert_image') }}</button>
|
||||||
|
|
|
|
||||||
<button class="text-button" type="button" data-action="insertLink"><i class="zmdi zmdi-link"></i>{{ trans('entities.pages_md_insert_link') }}</button>
|
<button class="text-button" type="button" data-action="insertLink"><i class="zmdi zmdi-link"></i>{{ trans('entities.pages_md_insert_link') }}</button>
|
||||||
|
Reference in New Issue
Block a user