JS Events: Added CM pre/post init events
To allow hacking of all CodeMirror instances. Closes #4639.
This commit is contained in:
parent
fce7190257
commit
324e403ae5
3 changed files with 88 additions and 13 deletions
|
@ -253,3 +253,69 @@ window.addEventListener('library-cm6::configure-theme', event => {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
</details>
|
</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>
|
|
@ -55,7 +55,7 @@ function highlightElem(elem) {
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
elem.parentNode.insertBefore(wrapper, elem);
|
elem.parentNode.insertBefore(wrapper, elem);
|
||||||
|
|
||||||
const ev = createView({
|
const ev = createView('content-code-block', {
|
||||||
parent: wrapper,
|
parent: wrapper,
|
||||||
doc: content,
|
doc: content,
|
||||||
extensions: viewerExtensions(wrapper),
|
extensions: viewerExtensions(wrapper),
|
||||||
|
@ -99,7 +99,7 @@ export function highlight() {
|
||||||
* @returns {SimpleEditorInterface}
|
* @returns {SimpleEditorInterface}
|
||||||
*/
|
*/
|
||||||
export function wysiwygView(cmContainer, shadowRoot, content, language) {
|
export function wysiwygView(cmContainer, shadowRoot, content, language) {
|
||||||
const ev = createView({
|
const ev = createView('content-code-block', {
|
||||||
parent: cmContainer,
|
parent: cmContainer,
|
||||||
doc: content,
|
doc: content,
|
||||||
extensions: viewerExtensions(cmContainer),
|
extensions: viewerExtensions(cmContainer),
|
||||||
|
@ -125,16 +125,11 @@ export function popupEditor(elem, modeSuggestion) {
|
||||||
doc: content,
|
doc: content,
|
||||||
extensions: [
|
extensions: [
|
||||||
...editorExtensions(elem.parentElement),
|
...editorExtensions(elem.parentElement),
|
||||||
EditorView.updateListener.of(v => {
|
|
||||||
if (v.docChanged) {
|
|
||||||
// textArea.value = v.state.doc.toString();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create editor, hide original input
|
// Create editor, hide original input
|
||||||
const editor = new SimpleEditorInterface(createView(config));
|
const editor = new SimpleEditorInterface(createView('code-editor', config));
|
||||||
editor.setMode(modeSuggestion, content);
|
editor.setMode(modeSuggestion, content);
|
||||||
elem.style.display = 'none';
|
elem.style.display = 'none';
|
||||||
|
|
||||||
|
@ -163,7 +158,7 @@ export function inlineEditor(textArea, mode) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create editor view, hide original input
|
// Create editor view, hide original input
|
||||||
const ev = createView(config);
|
const ev = createView('code-input', config);
|
||||||
const editor = new SimpleEditorInterface(ev);
|
const editor = new SimpleEditorInterface(ev);
|
||||||
editor.setMode(mode, content);
|
editor.setMode(mode, content);
|
||||||
textArea.style.display = 'none';
|
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});
|
window.$events.emitPublic(elem, 'editor-markdown-cm6::pre-init', {editorViewConfig: config});
|
||||||
|
|
||||||
// Create editor view, hide original input
|
// Create editor view, hide original input
|
||||||
const ev = createView(config);
|
const ev = createView('markdown-editor', config);
|
||||||
(new SimpleEditorInterface(ev)).setMode('markdown', '');
|
(new SimpleEditorInterface(ev)).setMode('markdown', '');
|
||||||
elem.style.display = 'none';
|
elem.style.display = 'none';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Compartment} from '@codemirror/state';
|
import {Compartment, EditorState} from '@codemirror/state';
|
||||||
import {EditorView} from '@codemirror/view';
|
import {EditorView} from '@codemirror/view';
|
||||||
import {getLanguageExtension} from './languages';
|
import {getLanguageExtension} from './languages';
|
||||||
|
|
||||||
|
@ -7,17 +7,31 @@ const viewLangCompartments = new WeakMap();
|
||||||
/**
|
/**
|
||||||
* Create a new editor view.
|
* Create a new editor view.
|
||||||
*
|
*
|
||||||
|
* @param {String} usageType
|
||||||
* @param {{parent: Element, doc: String, extensions: Array}} config
|
* @param {{parent: Element, doc: String, extensions: Array}} config
|
||||||
* @returns {EditorView}
|
* @returns {EditorView}
|
||||||
*/
|
*/
|
||||||
export function createView(config) {
|
export function createView(usageType, config) {
|
||||||
const langCompartment = new Compartment();
|
const langCompartment = new Compartment();
|
||||||
config.extensions.push(langCompartment.of([]));
|
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);
|
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;
|
return ev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue