protected buildDOM(): HTMLButtonElement {
const button = el('button', {
type: 'button',
- class: 'editor-toolbar-button',
+ class: 'editor-button',
}, [this.definition.label]) as HTMLButtonElement;
- button.addEventListener('click', event => {
- this.definition.action(this.getContext().editor);
- });
+ button.addEventListener('click', this.onClick.bind(this));
return button;
}
+ protected onClick() {
+ this.definition.action(this.getContext().editor);
+ }
+
updateActiveState(selection: BaseSelection|null) {
const isActive = this.definition.isActive(selection);
- this.dom?.classList.toggle('editor-toolbar-button-active', isActive);
+ this.dom?.classList.toggle('editor-button-active', isActive);
}
updateState(state: EditorUiStateUpdate): void {
this.updateActiveState(state.selection);
}
+}
+
+export class FormatPreviewButton extends EditorButton {
+ protected previewSampleElement: HTMLElement;
+
+ constructor(previewSampleElement: HTMLElement,definition: EditorButtonDefinition) {
+ super(definition);
+ this.previewSampleElement = previewSampleElement;
+ }
+
+ protected buildDOM(): HTMLButtonElement {
+ const button = super.buildDOM();
+ button.innerHTML = '';
+
+ const preview = el('span', {
+ class: 'editor-button-format-preview'
+ }, [this.definition.label]);
+
+ const stylesToApply = this.getStylesFromPreview();
+ console.log(stylesToApply);
+ for (const style of Object.keys(stylesToApply)) {
+ preview.style.setProperty(style, stylesToApply[style]);
+ }
+
+ button.append(preview);
+ return button;
+ }
+
+ protected getStylesFromPreview(): Record<string, string> {
+ const wrap = el('div', {style: 'display: none', hidden: 'true', class: 'page-content'});
+ const sampleClone = this.previewSampleElement.cloneNode() as HTMLElement;
+ sampleClone.textContent = this.definition.label;
+ wrap.append(sampleClone);
+ document.body.append(wrap);
+
+ const propertiesToFetch = ['color', 'font-size', 'background-color', 'border-inline-start'];
+ const propertiesToReturn: Record<string, string> = {};
+
+ const computed = window.getComputedStyle(sampleClone);
+ for (const property of propertiesToFetch) {
+ propertiesToReturn[property] = computed.getPropertyValue(property);
+ }
+ wrap.remove();
+
+ return propertiesToReturn;
+ }
}
\ No newline at end of file
}
}
-export class EditorFormatMenu extends EditorContainerUiElement {
- buildDOM(): HTMLElement {
+export class EditorSimpleClassContainer extends EditorContainerUiElement {
+ protected className;
+
+ constructor(className: string, children: EditorUiElement[]) {
+ super(children);
+ this.className = className;
+ }
+
+ protected buildDOM(): HTMLElement {
return el('div', {
- class: 'editor-format-menu'
+ class: this.className,
}, this.getChildren().map(child => child.getDOMElement()));
}
+}
+export class EditorFormatMenu extends EditorContainerUiElement {
+ buildDOM(): HTMLElement {
+ const childElements: HTMLElement[] = this.getChildren().map(child => child.getDOMElement());
+ const menu = el('div', {
+ class: 'editor-format-menu-dropdown editor-dropdown-menu editor-menu-list',
+ hidden: 'true',
+ }, childElements);
+
+ const toggle = el('button', {
+ class: 'editor-format-menu-toggle',
+ type: 'button',
+ }, ['Formats']);
+
+ const wrapper = el('div', {
+ class: 'editor-format-menu editor-dropdown-menu-container',
+ }, [toggle, menu]);
+
+ let clickListener: Function|null = null;
+
+ const hide = () => {
+ menu.hidden = true;
+ if (clickListener) {
+ window.removeEventListener('click', clickListener as EventListener);
+ }
+ };
+
+ const show = () => {
+ menu.hidden = false
+ clickListener = (event: MouseEvent) => {
+ if (!wrapper.contains(event.target as HTMLElement)) {
+ hide();
+ }
+ }
+ window.addEventListener('click', clickListener as EventListener);
+ };
+
+ toggle.addEventListener('click', event => {
+ menu.hasAttribute('hidden') ? show() : hide();
+ });
+ menu.addEventListener('mouseleave', hide);
+
+ return wrapper;
+ }
}
\ No newline at end of file
-import {EditorButton} from "./framework/buttons";
+import {EditorButton, FormatPreviewButton} from "./framework/buttons";
import {
blockquote, bold, code,
dangerCallout,
undo,
warningCallout
} from "./defaults/button-definitions";
-import {EditorContainerUiElement, EditorFormatMenu} from "./framework/containers";
+import {EditorContainerUiElement, EditorFormatMenu, EditorSimpleClassContainer} from "./framework/containers";
+import {el} from "../helpers";
export function getMainEditorFullToolbar(): EditorContainerUiElement {
- return new EditorContainerUiElement([
+ return new EditorSimpleClassContainer('editor-toolbar-main', [
new EditorButton(undo),
new EditorButton(redo),
new EditorFormatMenu([
- new EditorButton(h2),
- new EditorButton(h3),
- new EditorButton(h4),
- new EditorButton(h5),
- new EditorButton(blockquote),
- new EditorButton(paragraph),
- new EditorButton(infoCallout),
- new EditorButton(successCallout),
- new EditorButton(warningCallout),
- new EditorButton(dangerCallout),
+ new FormatPreviewButton(el('h2'), h2),
+ new FormatPreviewButton(el('h3'), h3),
+ new FormatPreviewButton(el('h4'), h4),
+ new FormatPreviewButton(el('h5'), h5),
+ new FormatPreviewButton(el('blockquote'), blockquote),
+ new FormatPreviewButton(el('p'), paragraph),
+ new FormatPreviewButton(el('p', {class: 'callout info'}), infoCallout),
+ new FormatPreviewButton(el('p', {class: 'callout success'}), successCallout),
+ new FormatPreviewButton(el('p', {class: 'callout warning'}), warningCallout),
+ new FormatPreviewButton(el('p', {class: 'callout danger'}), dangerCallout),
]),
new EditorButton(bold),
--- /dev/null
+// Main UI elements
+.editor-toolbar-main {
+ display: flex;
+}
+
+// Buttons
+.editor-button {
+ border: 1px solid #DDD;
+ font-size: 12px;
+ padding: 4px 6px;
+ color: #444;
+}
+.editor-button:hover {
+ background-color: #EEE;
+ cursor: pointer;
+ color: #000;
+}
+.editor-button-active, .editor-button-active:hover {
+ background-color: #ceebff;
+ color: #000;
+}
+.editor-button-format-preview {
+ padding: 4px 6px;
+ display: block;
+}
+
+// Containers
+.editor-dropdown-menu-container {
+ position: relative;
+}
+.editor-dropdown-menu {
+ position: absolute;
+ background-color: #FFF;
+ box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.15);
+ z-index: 99;
+ min-width: 120px;
+}
+.editor-menu-list {
+ display: flex;
+ flex-direction: column;
+}
+.editor-menu-list > .editor-button {
+ border-bottom: 0;
+ text-align: start;
+}
+
+.editor-format-menu .editor-dropdown-menu {
+ min-width: 320px;
+}
\ No newline at end of file
@import "forms";
@import "animations";
@import "tinymce";
+@import "editor";
@import "codemirror";
@import "components";
@import "header";
option:wysiwyg-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
class="">
- <style>
- .editor-toolbar-button-active {
- background-color: tomato;
- }
- </style>
+ <div class="editor-container">
+ <div refs="wysiwyg-editor@edit-area" contenteditable="true">
+ <p id="Content!">Some <strong>content</strong> here</p>
+ <p>This has a <a href="https://example.com" target="_blank" title="Link to example">link</a> in it</p>
+ <h2>List below this h2 header</h2>
+ <ul>
+ <li>Hello</li>
+ </ul>
- <div refs="wysiwyg-editor@edit-area" contenteditable="true">
- <p id="Content!">Some <strong>content</strong> here</p>
- <p>This has a <a href="https://example.com" target="_blank" title="Link to example">link</a> in it</p>
- <h2>List below this h2 header</h2>
- <ul>
- <li>Hello</li>
- </ul>
-
- <p class="callout danger">
- Hello there, this is an info callout
- </p>
+ <p class="callout danger">
+ Hello there, this is an info callout
+ </p>
+ </div>
</div>
<div id="lexical-debug" style="white-space: pre-wrap; font-size: 12px; height: 200px; overflow-y: scroll; background-color: #000; padding: 1rem; border-radius: 4px; color: #FFF;"></div>