]> BookStack Code Mirror - bookstack/commitdiff
JS Events: Added CM pre/post init events
authorDan Brown <redacted>
Wed, 1 Nov 2023 17:56:52 +0000 (17:56 +0000)
committerDan Brown <redacted>
Wed, 1 Nov 2023 17:56:52 +0000 (17:56 +0000)
To allow hacking of all CodeMirror instances.
Closes #4639.

dev/docs/javascript-public-events.md
resources/js/code/index.mjs
resources/js/code/views.js

index 95300ddd360bc678dd7a434fdf3fc267b9991ab3..4f68daaebf0a52d35b90333ad23a7bece0d6b00b 100644 (file)
@@ -253,3 +253,69 @@ window.addEventListener('library-cm6::configure-theme', event => {
 });
 ```
 </details>
+
+### `library-cm6::pre-init`
+
+This event is called just before any CodeMirror instances are initialised so that the instance configuration can be viewed and altered before the instance is created.
+
+#### Event Data
+
+- `usage` - A string label to identify the usage type of the CodeMirror instance in BookStack.
+- `editorViewConfig` - A reference to the [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) that will be used to create the instance.
+- `libEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) class object, provided for convenience.
+- `libEditorState` - The CodeMirror [EditorState](https://codemirror.net/docs/ref/#state.EditorState) class object, provided for convenience.
+- `libCompartment` - The CodeMirror [Compartment](https://codemirror.net/docs/ref/#state.Compartment) class object, provided for convenience.
+
+##### Example
+
+The below shows how you'd enable the built-in line wrapping extension for page content code blocks: 
+
+<details>
+<summary>Show Example</summary>
+
+```javascript
+window.addEventListener('library-cm6::pre-init', event => {
+    const detail = event.detail;
+    const config = detail.editorViewConfig;
+    const EditorView = detail.libEditorView;
+    
+    if (detail.usage === 'content-code-block') {
+        config.extensions.push(EditorView.lineWrapping);
+    }
+});
+```
+</details>
+
+### `library-cm6::post-init`
+
+This event is called just after any CodeMirror instances are initialised so that you're able to gain a reference to the CodeMirror EditorView instance.
+
+#### Event Data
+
+- `usage` - A string label to identify the usage type of the CodeMirror instance in BookStack.
+- `editorView` - A reference to the [EditorView](https://codemirror.net/docs/ref/#view.EditorView) instance that has been created.
+- `editorViewConfig` - A reference to the [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) that was used to create the instance.
+- `libEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) class object, provided for convenience.
+- `libEditorState` - The CodeMirror [EditorState](https://codemirror.net/docs/ref/#state.EditorState) class object, provided for convenience.
+- `libCompartment` - The CodeMirror [Compartment](https://codemirror.net/docs/ref/#state.Compartment) class object, provided for convenience.
+
+##### Example
+
+The below shows how you'd prepend some default text to all content (page) code blocks.
+
+<details>
+<summary>Show Example</summary>
+
+```javascript
+window.addEventListener('library-cm6::post-init', event => {
+    const detail = event.detail;
+    const editorView = detail.editorView;
+    
+    if (detail.usage === 'content-code-block') {
+        editorView.dispatch({
+          changes: {from: 0, to: 0, insert: 'Copyright 2023\n\n'}
+        });
+    }
+});
+```
+</details>
\ No newline at end of file
index e51472dc4ce740f0bad2304db6d4e1fd0d5604d8..ada4529db5454fa448e4263a9665a470272a4c03 100644 (file)
@@ -55,7 +55,7 @@ function highlightElem(elem) {
     const wrapper = document.createElement('div');
     elem.parentNode.insertBefore(wrapper, elem);
 
-    const ev = createView({
+    const ev = createView('content-code-block', {
         parent: wrapper,
         doc: content,
         extensions: viewerExtensions(wrapper),
@@ -99,7 +99,7 @@ export function highlight() {
  * @returns {SimpleEditorInterface}
  */
 export function wysiwygView(cmContainer, shadowRoot, content, language) {
-    const ev = createView({
+    const ev = createView('content-code-block', {
         parent: cmContainer,
         doc: content,
         extensions: viewerExtensions(cmContainer),
@@ -125,16 +125,11 @@ export function popupEditor(elem, modeSuggestion) {
         doc: content,
         extensions: [
             ...editorExtensions(elem.parentElement),
-            EditorView.updateListener.of(v => {
-                if (v.docChanged) {
-                    // textArea.value = v.state.doc.toString();
-                }
-            }),
         ],
     };
 
     // Create editor, hide original input
-    const editor = new SimpleEditorInterface(createView(config));
+    const editor = new SimpleEditorInterface(createView('code-editor', config));
     editor.setMode(modeSuggestion, content);
     elem.style.display = 'none';
 
@@ -163,7 +158,7 @@ export function inlineEditor(textArea, mode) {
     };
 
     // Create editor view, hide original input
-    const ev = createView(config);
+    const ev = createView('code-input', config);
     const editor = new SimpleEditorInterface(ev);
     editor.setMode(mode, content);
     textArea.style.display = 'none';
@@ -198,7 +193,7 @@ export function markdownEditor(elem, onChange, domEventHandlers, keyBindings) {
     window.$events.emitPublic(elem, 'editor-markdown-cm6::pre-init', {editorViewConfig: config});
 
     // Create editor view, hide original input
-    const ev = createView(config);
+    const ev = createView('markdown-editor', config);
     (new SimpleEditorInterface(ev)).setMode('markdown', '');
     elem.style.display = 'none';
 
index 12148ca0936f9331f8fbebddd5314ff58a4c5a25..5599c35ddbe6751b9c9d70181a042bfe95c630fb 100644 (file)
@@ -1,4 +1,4 @@
-import {Compartment} from '@codemirror/state';
+import {Compartment, EditorState} from '@codemirror/state';
 import {EditorView} from '@codemirror/view';
 import {getLanguageExtension} from './languages';
 
@@ -7,17 +7,31 @@ const viewLangCompartments = new WeakMap();
 /**
  * Create a new editor view.
  *
+ * @param {String} usageType
  * @param {{parent: Element, doc: String, extensions: Array}} config
  * @returns {EditorView}
  */
-export function createView(config) {
+export function createView(usageType, config) {
     const langCompartment = new Compartment();
     config.extensions.push(langCompartment.of([]));
 
-    const ev = new EditorView(config);
+    const commonEventData = {
+        usage: usageType,
+        editorViewConfig: config,
+        libEditorView: EditorView,
+        libEditorState: EditorState,
+        libCompartment: Compartment,
+    };
+
+    // Emit a pre-init public event so the user can tweak the config before instance creation
+    window.$events.emitPublic(config.parent, 'library-cm6::pre-init', commonEventData);
 
+    const ev = new EditorView(config);
     viewLangCompartments.set(ev, langCompartment);
 
+    // Emit a post-init public event so the user can gain a reference to the EditorView
+    window.$events.emitPublic(config.parent, 'library-cm6::post-init', {editorView: ev, ...commonEventData});
+
     return ev;
 }
 
Morty Proxy This is a proxified and sanitized view of the page, visit original site.