]> BookStack Code Mirror - bookstack/commitdiff
Added callout cycling in markdown editor via shortcut
authorDan Brown <redacted>
Sat, 26 Nov 2022 23:18:51 +0000 (23:18 +0000)
committerDan Brown <redacted>
Sat, 26 Nov 2022 23:18:51 +0000 (23:18 +0000)
resources/js/markdown/actions.js
resources/js/markdown/shortcuts.js

index 96f9b263c9a7db4be02bc08273c32043e9696b0a..c2c3409a590cdc02ee68d11d326e7a246fe5ffca 100644 (file)
@@ -326,6 +326,44 @@ export class Actions {
         return this.replaceLineStart(prefix);
     }
 
+    /**
+     * Cycles through the type of callout block within the selection.
+     * Creates a callout block if none existing, and removes it if cycling past the danger type.
+     */
+    cycleCalloutTypeAtSelection() {
+        const selectionRange = this.editor.cm.listSelections()[0];
+        const lineContent = this.editor.cm.getLine(selectionRange.anchor.line);
+        const lineLength = lineContent.length;
+        const contentRange = {
+            anchor: {line: selectionRange.anchor.line, ch: 0},
+            head: {line: selectionRange.anchor.line, ch: lineLength},
+        };
+
+        const formats = ['info', 'success', 'warning', 'danger'];
+        const joint = formats.join('|');
+        const regex = new RegExp(`class="((${joint})\\s+callout|callout\\s+(${joint}))"`, 'i');
+        const matches = regex.exec(lineContent);
+        const format = (matches ? (matches[2] || matches[3]) : '').toLowerCase();
+
+        if (format === formats[formats.length - 1]) {
+            this.wrapLine(`<p class="callout ${formats[formats.length - 1]}">`, '</p>');
+        } else if (format === '') {
+            this.wrapLine('<p class="callout info">', '</p>');
+        } else {
+            const newFormatIndex = formats.indexOf(format) + 1;
+            const newFormat = formats[newFormatIndex];
+            const newContent = lineContent.replace(matches[0], matches[0].replace(format, newFormat));
+            this.editor.cm.replaceRange(newContent, contentRange.anchor, contentRange.head);
+
+            const chDiff = newContent.length - lineContent.length;
+            selectionRange.anchor.ch += chDiff;
+            if (selectionRange.anchor !== selectionRange.head) {
+                selectionRange.head.ch += chDiff;
+            }
+            this.editor.cm.setSelection(selectionRange.anchor, selectionRange.head);
+        }
+    }
+
     /**
      * Handle image upload and add image into markdown content
      * @param {File} file
@@ -404,7 +442,7 @@ export class Actions {
         const cursorPos = this.editor.cm.coordsChar({left: event.pageX, top: event.pageY});
         this.editor.cm.setCursor(cursorPos);
         for (const image of images) {
-            this.editor.actions.uploadImage(image);
+            this.uploadImage(image);
         }
     }
 }
\ No newline at end of file
index 1249f7d609839142e8b0b8b9230961cb72ef1b04..17ffe2fb3a6fc69d94654fdcc63cc631d0af21f6 100644 (file)
@@ -40,7 +40,7 @@ export function provide(editor, metaKey) {
     shortcuts[`${metaKey}-7`] = cm => editor.actions.wrapSelection('\n```\n', '\n```');
     shortcuts[`${metaKey}-8`] = cm => editor.actions.wrapSelection('`', '`');
     shortcuts[`Shift-${metaKey}-E`] = cm => editor.actions.wrapSelection('`', '`');
-    shortcuts[`${metaKey}-9`] = cm => editor.actions.wrapSelection('<p class="callout info">', '</p>');
+    shortcuts[`${metaKey}-9`] = cm => editor.actions.cycleCalloutTypeAtSelection();
     shortcuts[`${metaKey}-P`] = cm => editor.actions.replaceLineStart('-')
     shortcuts[`${metaKey}-O`] = cm => editor.actions.replaceLineStartForOrderedList()