diff --git a/resources/assets/js/components/markdown-editor.js b/resources/assets/js/components/markdown-editor.js index ac77cb459..7f3d4ef24 100644 --- a/resources/assets/js/components/markdown-editor.js +++ b/resources/assets/js/components/markdown-editor.js @@ -222,16 +222,30 @@ class MarkdownEditor { } }); - // Handle images on drag-drop + // Handle image & content drag n drop cm.on('drop', (cm, event) => { - event.stopPropagation(); - event.preventDefault(); - let cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY}); - cm.setCursor(cursorPos); - if (!event.dataTransfer || !event.dataTransfer.files) return; - for (let i = 0; i < event.dataTransfer.files.length; i++) { - uploadImage(event.dataTransfer.files[i]); + + const templateId = event.dataTransfer.getData('bookstack/template'); + if (templateId) { + const cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY}); + cm.setCursor(cursorPos); + event.preventDefault(); + window.$http.get(`/templates/${templateId}`).then(resp => { + const content = resp.data.markdown || resp.data.html; + cm.replaceSelection(content); + }); } + + if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) { + const cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY}); + cm.setCursor(cursorPos); + event.stopPropagation(); + event.preventDefault(); + for (let i = 0; i < event.dataTransfer.files.length; i++) { + uploadImage(event.dataTransfer.files[i]); + } + } + }); // Helper to replace editor content diff --git a/resources/assets/js/components/template-manager.js b/resources/assets/js/components/template-manager.js index b966762d2..d004a4307 100644 --- a/resources/assets/js/components/template-manager.js +++ b/resources/assets/js/components/template-manager.js @@ -16,6 +16,9 @@ class TemplateManager { // Template list item content click DOM.onChildEvent(this.elem, '.template-item-content', 'click', this.handleTemplateItemClick.bind(this)); + // Template list item drag start + DOM.onChildEvent(this.elem, '.template-item', 'dragstart', this.handleTemplateItemDragStart.bind(this)); + this.setupSearchBox(); } @@ -24,6 +27,12 @@ class TemplateManager { this.insertTemplate(templateId, 'replace'); } + handleTemplateItemDragStart(event, templateItem) { + const templateId = templateItem.closest('[template-id]').getAttribute('template-id'); + event.dataTransfer.setData('bookstack/template', templateId); + event.dataTransfer.setData('text/plain', templateId); + } + handleTemplateActionClick(event, actionButton) { event.stopPropagation(); diff --git a/resources/assets/js/components/wysiwyg-editor.js b/resources/assets/js/components/wysiwyg-editor.js index be0aaf18a..c03c0d2aa 100644 --- a/resources/assets/js/components/wysiwyg-editor.js +++ b/resources/assets/js/components/wysiwyg-editor.js @@ -608,6 +608,18 @@ class WysiwygEditor { let dom = editor.dom, rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc()); + // Template insertion + const templateId = event.dataTransfer.getData('bookstack/template'); + if (templateId) { + event.preventDefault(); + window.$http.get(`/templates/${templateId}`).then(resp => { + editor.selection.setRng(rng); + editor.undoManager.transact(function () { + editor.execCommand('mceInsertContent', false, resp.data.html); + }); + }); + } + // Don't allow anything to be dropped in a captioned image. if (dom.getParent(rng.startContainer, '.mceTemp')) { event.preventDefault(); diff --git a/resources/assets/sass/_tinymce.scss b/resources/assets/sass/_tinymce.scss index 4c50f14d2..27c3b28d0 100644 --- a/resources/assets/sass/_tinymce.scss +++ b/resources/assets/sass/_tinymce.scss @@ -61,6 +61,7 @@ .page-content.mce-content-body { padding-top: 16px; + outline: none; } // Fix to prevent 'No color' option from not being clickable.