Skip to content

Commit 324e403

Browse files
committed
JS Events: Added CM pre/post init events
To allow hacking of all CodeMirror instances. Closes #4639.
1 parent fce7190 commit 324e403

File tree

3 files changed

+88
-13
lines changed

3 files changed

+88
-13
lines changed

dev/docs/javascript-public-events.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,69 @@ window.addEventListener('library-cm6::configure-theme', event => {
253253
});
254254
```
255255
</details>
256+
257+
### `library-cm6::pre-init`
258+
259+
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.
260+
261+
#### Event Data
262+
263+
- `usage` - A string label to identify the usage type of the CodeMirror instance in BookStack.
264+
- `editorViewConfig` - A reference to the [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) that will be used to create the instance.
265+
- `libEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) class object, provided for convenience.
266+
- `libEditorState` - The CodeMirror [EditorState](https://codemirror.net/docs/ref/#state.EditorState) class object, provided for convenience.
267+
- `libCompartment` - The CodeMirror [Compartment](https://codemirror.net/docs/ref/#state.Compartment) class object, provided for convenience.
268+
269+
##### Example
270+
271+
The below shows how you'd enable the built-in line wrapping extension for page content code blocks:
272+
273+
<details>
274+
<summary>Show Example</summary>
275+
276+
```javascript
277+
window.addEventListener('library-cm6::pre-init', event => {
278+
const detail = event.detail;
279+
const config = detail.editorViewConfig;
280+
const EditorView = detail.libEditorView;
281+
282+
if (detail.usage === 'content-code-block') {
283+
config.extensions.push(EditorView.lineWrapping);
284+
}
285+
});
286+
```
287+
</details>
288+
289+
### `library-cm6::post-init`
290+
291+
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.
292+
293+
#### Event Data
294+
295+
- `usage` - A string label to identify the usage type of the CodeMirror instance in BookStack.
296+
- `editorView` - A reference to the [EditorView](https://codemirror.net/docs/ref/#view.EditorView) instance that has been created.
297+
- `editorViewConfig` - A reference to the [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) that was used to create the instance.
298+
- `libEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) class object, provided for convenience.
299+
- `libEditorState` - The CodeMirror [EditorState](https://codemirror.net/docs/ref/#state.EditorState) class object, provided for convenience.
300+
- `libCompartment` - The CodeMirror [Compartment](https://codemirror.net/docs/ref/#state.Compartment) class object, provided for convenience.
301+
302+
##### Example
303+
304+
The below shows how you'd prepend some default text to all content (page) code blocks.
305+
306+
<details>
307+
<summary>Show Example</summary>
308+
309+
```javascript
310+
window.addEventListener('library-cm6::post-init', event => {
311+
const detail = event.detail;
312+
const editorView = detail.editorView;
313+
314+
if (detail.usage === 'content-code-block') {
315+
editorView.dispatch({
316+
changes: {from: 0, to: 0, insert: 'Copyright 2023\n\n'}
317+
});
318+
}
319+
});
320+
```
321+
</details>

resources/js/code/index.mjs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function highlightElem(elem) {
5555
const wrapper = document.createElement('div');
5656
elem.parentNode.insertBefore(wrapper, elem);
5757

58-
const ev = createView({
58+
const ev = createView('content-code-block', {
5959
parent: wrapper,
6060
doc: content,
6161
extensions: viewerExtensions(wrapper),
@@ -99,7 +99,7 @@ export function highlight() {
9999
* @returns {SimpleEditorInterface}
100100
*/
101101
export function wysiwygView(cmContainer, shadowRoot, content, language) {
102-
const ev = createView({
102+
const ev = createView('content-code-block', {
103103
parent: cmContainer,
104104
doc: content,
105105
extensions: viewerExtensions(cmContainer),
@@ -125,16 +125,11 @@ export function popupEditor(elem, modeSuggestion) {
125125
doc: content,
126126
extensions: [
127127
...editorExtensions(elem.parentElement),
128-
EditorView.updateListener.of(v => {
129-
if (v.docChanged) {
130-
// textArea.value = v.state.doc.toString();
131-
}
132-
}),
133128
],
134129
};
135130

136131
// Create editor, hide original input
137-
const editor = new SimpleEditorInterface(createView(config));
132+
const editor = new SimpleEditorInterface(createView('code-editor', config));
138133
editor.setMode(modeSuggestion, content);
139134
elem.style.display = 'none';
140135

@@ -163,7 +158,7 @@ export function inlineEditor(textArea, mode) {
163158
};
164159

165160
// Create editor view, hide original input
166-
const ev = createView(config);
161+
const ev = createView('code-input', config);
167162
const editor = new SimpleEditorInterface(ev);
168163
editor.setMode(mode, content);
169164
textArea.style.display = 'none';
@@ -198,7 +193,7 @@ export function markdownEditor(elem, onChange, domEventHandlers, keyBindings) {
198193
window.$events.emitPublic(elem, 'editor-markdown-cm6::pre-init', {editorViewConfig: config});
199194

200195
// Create editor view, hide original input
201-
const ev = createView(config);
196+
const ev = createView('markdown-editor', config);
202197
(new SimpleEditorInterface(ev)).setMode('markdown', '');
203198
elem.style.display = 'none';
204199

resources/js/code/views.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Compartment} from '@codemirror/state';
1+
import {Compartment, EditorState} from '@codemirror/state';
22
import {EditorView} from '@codemirror/view';
33
import {getLanguageExtension} from './languages';
44

@@ -7,17 +7,31 @@ const viewLangCompartments = new WeakMap();
77
/**
88
* Create a new editor view.
99
*
10+
* @param {String} usageType
1011
* @param {{parent: Element, doc: String, extensions: Array}} config
1112
* @returns {EditorView}
1213
*/
13-
export function createView(config) {
14+
export function createView(usageType, config) {
1415
const langCompartment = new Compartment();
1516
config.extensions.push(langCompartment.of([]));
1617

17-
const ev = new EditorView(config);
18+
const commonEventData = {
19+
usage: usageType,
20+
editorViewConfig: config,
21+
libEditorView: EditorView,
22+
libEditorState: EditorState,
23+
libCompartment: Compartment,
24+
};
25+
26+
// Emit a pre-init public event so the user can tweak the config before instance creation
27+
window.$events.emitPublic(config.parent, 'library-cm6::pre-init', commonEventData);
1828

29+
const ev = new EditorView(config);
1930
viewLangCompartments.set(ev, langCompartment);
2031

32+
// Emit a post-init public event so the user can gain a reference to the EditorView
33+
window.$events.emitPublic(config.parent, 'library-cm6::post-init', {editorView: ev, ...commonEventData});
34+
2135
return ev;
2236
}
2337

0 commit comments

Comments
 (0)