Skip to content

Commit ab4b1c8

Browse files
committed
Lexical API: Reviewed docs, Made toolbar its own UI class
1 parent 8890746 commit ab4b1c8

File tree

4 files changed

+102
-69
lines changed

4 files changed

+102
-69
lines changed

dev/docs/javascript-public-events.md

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,47 @@ window.addEventListener('editor-tinymce::setup', event => {
134134
});
135135
```
136136

137+
### `editor-wysiwyg::post-init`
138+
139+
This is called after the (new custom-built Lexical-based) WYSIWYG editor has been initialised.
140+
141+
#### Event Data
142+
143+
- `usage` - A string label to identify the usage type of the WYSIWYG editor in BookStack.
144+
- `api` - An instance to the WYSIWYG editor API, as documented in the [WYSIWYG JavaScript API file](./wysiwyg-js-api.md).
145+
146+
##### Example
147+
148+
The below example shows how you'd use this API to create a button, with that button added to the main toolbar of the page editor, which inserts bold "Hello!" text on press:
149+
150+
<details>
151+
<summary>Show Example</summary>
152+
153+
```javascript
154+
window.addEventListener('editor-wysiwyg::post-init', event => {
155+
const {usage, api} = event.detail;
156+
// Check that it's the page editor which is being loaded
157+
if (usage !== 'page-editor') {
158+
return;
159+
}
160+
161+
// Create a custom button which inserts bold hello text on press
162+
const button = api.ui.createButton({
163+
label: 'Greet',
164+
action: () => {
165+
api.content.insertHtml(`<strong>Hello!</strong>`);
166+
}
167+
});
168+
169+
// Add the button to the start of the first section within the main toolbar
170+
const toolbar = api.ui.getMainToolbar();
171+
if (toolbar) {
172+
toolbar.getSections()[0]?.addButton(button, 0);
173+
}
174+
});
175+
```
176+
</details>
177+
137178
### `library-cm6::configure-theme`
138179
139180
This event is called whenever a CodeMirror instance is loaded, as a method to configure the theme used by CodeMirror. This applies to all CodeMirror instances including in-page code blocks, editors using in BookStack settings, and the Page markdown editor.
@@ -319,41 +360,3 @@ window.addEventListener('library-cm6::post-init', event => {
319360
});
320361
```
321362
</details>
322-
323-
### `editor-wysiwyg::post-init`
324-
325-
This is called after the (new custom-built Lexical-based) WYSIWYG editor has been initialised.
326-
327-
#### Event Data
328-
329-
- `usage` - A string label to identify the usage type of the WYSIWYG editor in BookStack.
330-
- `api` - An instance to the WYSIWYG editor API, as documented in the [WYSIWYG JavaScript API file](./wysiwyg-js-api.md).
331-
332-
##### Example
333-
334-
The below shows how you'd use this API to create a button, with that button added to the toolbar of the page editor, which inserts bold hello text on press:
335-
336-
<details>
337-
<summary>Show Example</summary>
338-
339-
```javascript
340-
window.addEventListener('editor-wysiwyg::post-init', event => {
341-
const {usage, api} = event.detail;
342-
// Check that it's the page editor being loaded.
343-
if (usage !== 'page-editor') {
344-
return;
345-
}
346-
347-
// Create a custom button which inserts bold hello text on press.
348-
const button = api.ui.createButton({
349-
label: 'Greet',
350-
action: () => {
351-
api.content.insertHtml(`<strong>Hello!</strong>`);
352-
}
353-
});
354-
355-
// Add the button to the start of the first section within the main toolbar.
356-
api.ui.getMainToolbarSections()[0]?.addButton(button, 0);
357-
});
358-
```
359-
</details>

dev/docs/wysiwyg-js-api.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# WYSIWYG JavaScript API
22

3-
TODO - Create JS events and add to the js public events doc.
4-
53
**Warning: This API is currently in development and may change without notice.**
64

5+
Feedback is very much welcomed via this issue: https://github.com/BookStackApp/BookStack/issues/5937
6+
77
This document covers the JavaScript API for the (newer Lexical-based) WYSIWYG editor.
88
This API is built and designed to abstract the internals of the editor away
99
to provide a stable interface for performing common customizations.
@@ -19,16 +19,16 @@ Stable parts of the API may still change where needed, but such changes would be
1919
The API is provided as an object, which itself provides a number of modules
2020
via its properties:
2121

22-
- `ui` - Provides all actions related to the UI of the editor, like buttons and toolbars.
23-
- `content` - Provides all actions related to the live user content being edited upon.
22+
- `ui` - Provides methods related to the UI of the editor, like buttons and toolbars.
23+
- `content` - Provides methods related to the live user content being edited upon.
2424

2525
Each of these modules, and the relevant types used within, are documented in detail below.
2626

2727
---
2828

2929
## UI Module
3030

31-
This module provides all actions related to the UI of the editor, like buttons and toolbars.
31+
This module provides methods related to the UI of the editor, like buttons and toolbars.
3232

3333
### Methods
3434

@@ -55,17 +55,16 @@ const button = api.ui.createButton({
5555
});
5656
```
5757

58-
### getMainToolbarSections()
58+
### getMainToolbar()
5959

60-
Get the sections of the main editor toolbar. These are those which contain groups of buttons
61-
with overflow control.
62-
63-
The function returns an array of [EditorToolbarSection](#editortoolbarsection) objects.
60+
Get the main editor toolbar. This is typically the toolbar at the top of the editor.
61+
The function returns an [EditorApiToolbar](#editorapitoolbar) object, or null if no toolbar is found.
6462

6563
**Example**
6664

6765
```javascript
68-
const sections = api.ui.getMainToolbarSections();
66+
const toolbar = api.ui.getMainToolbar();
67+
const sections = toolbar?.getSections() || [];
6968
if (sections.length > 0) {
7069
sections[0].addButton(button);
7170
}
@@ -83,20 +82,27 @@ This has the following methods:
8382
8483
- `setActive(isActive: boolean): void` - Sets whether the button should be in an active state or not (typically active buttons appear as pressed).
8584
86-
#### EditorToolbarSection
85+
#### EditorApiToolbar
86+
87+
Represents a toolbar within the editor. This is a bar typically containing sets of buttons.
88+
This has the following methods:
89+
90+
- `getSections(): EditorApiToolbarSection[]` - Provides the main [EditorApiToolbarSections](#editorapitoolbarsection) contained within this toolbar.
91+
92+
#### EditorApiToolbarSection
8793
8894
Represents a section of the main editor toolbar, which contains a set of buttons.
8995
This has the following methods:
9096
9197
- `getLabel(): string` - Provides the string label of the section.
9298
- `addButton(button: EditorApiButton, targetIndex: number = -1): void` - Adds a button to the section.
93-
- By default, this will append the button, although a target index can be provided to insert the button at a specific position.
99+
- By default, this will append the button, although a target index can be provided to insert at a specific position.
94100
95101
---
96102
97103
## Content Module
98104
99-
This module provides all actions related to the live user content being edited within the editor.
105+
This module provides methods related to the live user content being edited within the editor.
100106
101107
### Methods
102108

resources/js/wysiwyg/api/__tests__/ui.test.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {createEditorApiInstance} from "./api-test-utils";
2-
import {EditorApiButton, EditorApiToolbarSection} from "../ui";
2+
import {EditorApiButton, EditorApiToolbar, EditorApiToolbarSection} from "../ui";
33
import {getMainEditorFullToolbar} from "../../ui/defaults/toolbars";
44
import {EditorContainerUiElement} from "../../ui/framework/core";
55
import {EditorOverflowContainer} from "../../ui/framework/blocks/overflow-container";
@@ -59,25 +59,39 @@ describe('Editor API: UI Module', () => {
5959

6060
});
6161

62-
describe('getMainToolbarSections()', () => {
63-
it('should return an array of toolbar sections', () => {
62+
describe('getMainToolbar()', () => {
63+
it('should return the main editor toolbar', () => {
6464
const {api, context} = createEditorApiInstance();
6565
context.manager.setToolbar(getMainEditorFullToolbar(context));
6666

67-
const sections = api.ui.getMainToolbarSections();
68-
expect(Array.isArray(sections)).toBe(true);
67+
const toolbar = api.ui.getMainToolbar();
6968

70-
expect(sections[0]).toBeInstanceOf(EditorApiToolbarSection);
69+
expect(toolbar).toBeInstanceOf(EditorApiToolbar);
7170
});
7271
});
7372

73+
describe('EditorApiToolbar', () => {
74+
describe('getSections()', () => {
75+
it('should return the sections of the toolbar', () => {
76+
const {api, context} = createEditorApiInstance();
77+
context.manager.setToolbar(testToolbar());
78+
const toolbar = api.ui.getMainToolbar();
79+
80+
const sections = toolbar?.getSections() || [];
81+
82+
expect(sections.length).toBe(2);
83+
expect(sections[0]).toBeInstanceOf(EditorApiToolbarSection);
84+
})
85+
})
86+
})
87+
7488
describe('EditorApiToolbarSection', () => {
7589

7690
describe('getLabel()', () => {
7791
it('should return the label of the section', () => {
7892
const {api, context} = createEditorApiInstance();
7993
context.manager.setToolbar(testToolbar());
80-
const section = api.ui.getMainToolbarSections()[0];
94+
const section = api.ui.getMainToolbar()?.getSections()[0] as EditorApiToolbarSection;
8195
expect(section.getLabel()).toBe('section-a');
8296
})
8397
});
@@ -87,7 +101,7 @@ describe('Editor API: UI Module', () => {
87101
const {api, context} = createEditorApiInstance();
88102
const toolbar = testToolbar();
89103
context.manager.setToolbar(toolbar);
90-
const section = api.ui.getMainToolbarSections()[0];
104+
const section = api.ui.getMainToolbar()?.getSections()[0] as EditorApiToolbarSection;
91105

92106
const button = api.ui.createButton({label: 'TestButtonText!', action: () => ''});
93107
section.addButton(button);

resources/js/wysiwyg/api/ui.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {EditorButton} from "../ui/framework/buttons";
2-
import {EditorUiContext} from "../ui/framework/core";
2+
import {EditorContainerUiElement, EditorUiContext} from "../ui/framework/core";
33
import {EditorOverflowContainer} from "../ui/framework/blocks/overflow-container";
44

55
type EditorApiButtonOptions = {
@@ -34,13 +34,26 @@ export class EditorApiButton {
3434
}
3535
}
3636

37+
export class EditorApiToolbar {
38+
readonly #toolbar: EditorContainerUiElement;
39+
40+
constructor(toolbar: EditorContainerUiElement) {
41+
this.#toolbar = toolbar;
42+
}
43+
44+
getSections(): EditorApiToolbarSection[] {
45+
const sections = this.#toolbar.getChildren();
46+
return sections.filter(section => {
47+
return section instanceof EditorOverflowContainer;
48+
}).map(section => new EditorApiToolbarSection(section));
49+
}
50+
}
51+
3752
export class EditorApiToolbarSection {
3853
readonly #section: EditorOverflowContainer;
39-
label: string;
4054

4155
constructor(section: EditorOverflowContainer) {
4256
this.#section = section;
43-
this.label = section.getLabel();
4457
}
4558

4659
getLabel(): string {
@@ -65,15 +78,12 @@ export class EditorApiUiModule {
6578
return new EditorApiButton(options, this.#context);
6679
}
6780

68-
getMainToolbarSections(): EditorApiToolbarSection[] {
81+
getMainToolbar(): EditorApiToolbar|null {
6982
const toolbar = this.#context.manager.getToolbar();
7083
if (!toolbar) {
71-
return [];
84+
return null;
7285
}
7386

74-
const sections = toolbar.getChildren();
75-
return sections.filter(section => {
76-
return section instanceof EditorOverflowContainer;
77-
}).map(section => new EditorApiToolbarSection(section));
87+
return new EditorApiToolbar(toolbar);
7888
}
7989
}

0 commit comments

Comments
 (0)