]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/plugins-details.js
Added 'Sort Book' action to chapters
[bookstack] / resources / js / wysiwyg / plugins-details.js
index 0f089fc8ed55077aff4f92d9d89dfd4457c9af79..44a0a35ab1228a4ffbe0ee49a263e8595684764a 100644 (file)
@@ -2,6 +2,7 @@
  * @param {Editor} editor
  * @param {String} url
  */
+import {blockElementTypes} from "./util";
 
 function register(editor, url) {
 
@@ -29,12 +30,15 @@ function register(editor, url) {
         icon: 'togglelabel',
         tooltip: 'Edit label',
         onAction() {
-            const details = getSelectedDetailsBlock(editor);
-            const dialog = editor.windowManager.open(detailsDialog(editor));
-            dialog.setData({summary: getSummaryTextFromDetails(details)});
+            showDetailLabelEditWindow(editor);
         }
     });
 
+    editor.on('dblclick', event => {
+        if (!getSelectedDetailsBlock(editor) || event.target.closest('doc-root')) return;
+        showDetailLabelEditWindow(editor);
+    });
+
     editor.ui.registry.addButton('toggledetails', {
         icon: 'togglefold',
         tooltip: 'Toggle open/closed',
@@ -46,13 +50,29 @@ function register(editor, url) {
     });
 
     editor.addCommand('InsertDetailsBlock', function () {
-        const content = editor.selection.getContent({format: 'html'});
+        let content = editor.selection.getContent({format: 'html'});
         const details = document.createElement('details');
         const summary = document.createElement('summary');
+        const id = 'details-' + Date.now();
+        details.setAttribute('data-id', id)
         details.appendChild(summary);
-        details.innerHTML += content;
 
+        if (!content) {
+            content = '<p><br></p>';
+        }
+
+        details.innerHTML += content;
         editor.insertContent(details.outerHTML);
+        editor.focus();
+
+        const domDetails = editor.dom.select(`[data-id="${id}"]`)[0] || null;
+        if (domDetails) {
+            const firstChild = domDetails.querySelector('doc-root > *');
+            if (firstChild) {
+                firstChild.focus();
+            }
+            domDetails.removeAttribute('data-id');
+        }
     });
 
     editor.ui.registry.addContextToolbar('details', {
@@ -69,6 +89,15 @@ function register(editor, url) {
     });
 }
 
+/**
+ * @param {Editor} editor
+ */
+function showDetailLabelEditWindow(editor) {
+    const details = getSelectedDetailsBlock(editor);
+    const dialog = editor.windowManager.open(detailsDialog(editor));
+    dialog.setData({summary: getSummaryTextFromDetails(details)});
+}
+
 /**
  * @param {Editor} editor
  */
@@ -99,7 +128,7 @@ function detailsDialog(editor) {
                 {
                     type: 'input',
                     name: 'summary',
-                    label: 'Toggle label text',
+                    label: 'Toggle label',
                 },
             ],
         },
@@ -141,19 +170,21 @@ function setSummary(editor, summaryContent) {
  */
 function unwrapDetailsInSelection(editor) {
     const details = editor.selection.getNode().closest('details');
+    const selectionBm = editor.selection.getBookmark();
+
     if (details) {
-        const summary = details.querySelector('summary');
+        const elements = details.querySelectorAll('details > *:not(summary, doc-root), doc-root > *');
+
         editor.undoManager.transact(() => {
-            if (summary) {
-                summary.remove();
-            }
-            while (details.firstChild) {
-                details.parentNode.insertBefore(details.firstChild, details);
+            for (const element of elements) {
+                details.parentNode.insertBefore(element, details);
             }
             details.remove();
         });
     }
+
     editor.focus();
+    editor.selection.moveToBookmark(selectionBm);
 }
 
 /**
@@ -172,6 +203,12 @@ function setupElementFilters(editor) {
             el.attr('open', null);
         }
     });
+
+    editor.serializer.addNodeFilter('doc-root', function(elms) {
+        for (const el of elms) {
+            el.unwrap();
+        }
+    });
 }
 
 /**
@@ -181,14 +218,26 @@ function ensureDetailsWrappedInEditable(detailsEl) {
     unwrapDetailsEditable(detailsEl);
 
     detailsEl.attr('contenteditable', 'false');
-    const wrap = tinymce.html.Node.create('doc-root', {contenteditable: 'true'});
+    const rootWrap = tinymce.html.Node.create('doc-root', {contenteditable: 'true'});
+    let previousBlockWrap = null;
+
     for (const child of detailsEl.children()) {
-        if (child.name !== 'summary') {
-            wrap.append(child);
+        if (child.name === 'summary') continue;
+        const isBlock = blockElementTypes.includes(child.name);
+
+        if (!isBlock) {
+            if (!previousBlockWrap) {
+                previousBlockWrap = tinymce.html.Node.create('p');
+                rootWrap.append(previousBlockWrap);
+            }
+            previousBlockWrap.append(child);
+        } else {
+            rootWrap.append(child);
+            previousBlockWrap = null;
         }
     }
 
-    detailsEl.append(wrap);
+    detailsEl.append(rootWrap);
 }
 
 /**