diff --git a/resources/js/wysiwyg/ui/framework/blocks/dropdown-button.ts b/resources/js/wysiwyg/ui/framework/blocks/dropdown-button.ts index 70e1a9ffc..a75cf64fe 100644 --- a/resources/js/wysiwyg/ui/framework/blocks/dropdown-button.ts +++ b/resources/js/wysiwyg/ui/framework/blocks/dropdown-button.ts @@ -7,10 +7,12 @@ export class EditorDropdownButton extends EditorContainerUiElement { protected button: EditorButton; protected childItems: EditorUiElement[]; protected open: boolean = false; + protected showOnHover: boolean = false; - constructor(button: EditorBasicButtonDefinition|EditorButton, children: EditorUiElement[]) { + constructor(button: EditorBasicButtonDefinition|EditorButton, showOnHover: boolean, children: EditorUiElement[]) { super(children); - this.childItems = children + this.childItems = children; + this.showOnHover = showOnHover; if (button instanceof EditorButton) { this.button = button; @@ -47,13 +49,15 @@ export class EditorDropdownButton extends EditorContainerUiElement { class: 'editor-dropdown-menu-container', }, [button, menu]); - handleDropdown(button, menu, () => { + handleDropdown({toggle : button, menu : menu, + showOnHover: this.showOnHover, + onOpen : () => { this.open = true; this.getContext().manager.triggerStateUpdateForElement(this.button); - }, () => { + }, onClose : () => { this.open = false; this.getContext().manager.triggerStateUpdateForElement(this.button); - }); + }}); return wrapper; } diff --git a/resources/js/wysiwyg/ui/framework/blocks/format-menu.ts b/resources/js/wysiwyg/ui/framework/blocks/format-menu.ts index bcd61e45c..52a9c3809 100644 --- a/resources/js/wysiwyg/ui/framework/blocks/format-menu.ts +++ b/resources/js/wysiwyg/ui/framework/blocks/format-menu.ts @@ -20,7 +20,7 @@ export class EditorFormatMenu extends EditorContainerUiElement { class: 'editor-format-menu editor-dropdown-menu-container', }, [toggle, menu]); - handleDropdown(toggle, menu); + handleDropdown({toggle : toggle, menu : menu}); return wrapper; } @@ -33,6 +33,15 @@ export class EditorFormatMenu extends EditorContainerUiElement { this.updateToggleLabel(child.getLabel()); return; } + + if (child instanceof EditorContainerUiElement) { + for (const grandchild of child.getChildren()) { + if (grandchild instanceof EditorButton && grandchild.isActive()) { + this.updateToggleLabel(grandchild.getLabel()); + return; + } + } + } } this.updateToggleLabel(this.trans('Formats')); diff --git a/resources/js/wysiwyg/ui/framework/blocks/overflow-container.ts b/resources/js/wysiwyg/ui/framework/blocks/overflow-container.ts index 2c188471e..83f394d9d 100644 --- a/resources/js/wysiwyg/ui/framework/blocks/overflow-container.ts +++ b/resources/js/wysiwyg/ui/framework/blocks/overflow-container.ts @@ -17,13 +17,14 @@ export class EditorOverflowContainer extends EditorContainerUiElement { this.overflowButton = new EditorDropdownButton({ label: 'More', icon: moreHorizontal, - }, []); + }, false, []); this.addChildren(this.overflowButton); } protected buildDOM(): HTMLElement { - const visibleChildren = this.content.slice(0, this.size); - const invisibleChildren = this.content.slice(this.size); + const slicePosition = this.content.length > this.size ? this.size - 1 : this.size; + const visibleChildren = this.content.slice(0, slicePosition); + const invisibleChildren = this.content.slice(slicePosition); const visibleElements = visibleChildren.map(child => child.getDOMElement()); if (invisibleChildren.length > 0) { diff --git a/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts b/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts index 35886d2f9..45c3f39d1 100644 --- a/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts +++ b/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts @@ -1,7 +1,16 @@ -export function handleDropdown(toggle: HTMLElement, menu: HTMLElement, onOpen: Function|undefined = undefined, onClose: Function|undefined = undefined) { +interface HandleDropdownParams { + toggle: HTMLElement; + menu: HTMLElement; + showOnHover?: boolean, + onOpen?: Function | undefined; + onClose?: Function | undefined; +} + +export function handleDropdown(options: HandleDropdownParams) { + const {menu, toggle, onClose, onOpen, showOnHover} = options; let clickListener: Function|null = null; const hide = () => { @@ -27,8 +36,13 @@ export function handleDropdown(toggle: HTMLElement, menu: HTMLElement, onOpen: F } }; - toggle.addEventListener('click', event => { + const toggleShowing = (event: MouseEvent) => { menu.hasAttribute('hidden') ? show() : hide(); - }); + }; + toggle.addEventListener('click', toggleShowing); + if (showOnHover) { + toggle.addEventListener('mouseenter', toggleShowing); + } + menu.addEventListener('mouseleave', hide); } \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts index 18b811380..df514e504 100644 --- a/resources/js/wysiwyg/ui/toolbars.ts +++ b/resources/js/wysiwyg/ui/toolbars.ts @@ -21,9 +21,12 @@ import {EditorOverflowContainer} from "./framework/blocks/overflow-container"; export function getMainEditorFullToolbar(): EditorContainerUiElement { return new EditorSimpleClassContainer('editor-toolbar-main', [ + // History state - new EditorButton(undo), - new EditorButton(redo), + new EditorOverflowContainer(2, [ + new EditorButton(undo), + new EditorButton(redo), + ]), // Block formats new EditorFormatMenu([ @@ -33,37 +36,43 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement { 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 EditorDropdownButton({label: 'Callouts'}, true, [ + 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), + ]), ]), // Inline formats - new EditorButton(bold), - new EditorButton(italic), - new EditorButton(underline), - new EditorDropdownButton(new EditorColorButton(textColor, 'color'), [ - new EditorColorPicker('color'), + new EditorOverflowContainer(6, [ + new EditorButton(bold), + new EditorButton(italic), + new EditorButton(underline), + new EditorDropdownButton(new EditorColorButton(textColor, 'color'), false, [ + new EditorColorPicker('color'), + ]), + new EditorDropdownButton(new EditorColorButton(highlightColor, 'background-color'), false, [ + new EditorColorPicker('background-color'), + ]), + new EditorButton(strikethrough), + new EditorButton(superscript), + new EditorButton(subscript), + new EditorButton(code), + new EditorButton(clearFormating), ]), - new EditorDropdownButton(new EditorColorButton(highlightColor, 'background-color'), [ - new EditorColorPicker('background-color'), - ]), - new EditorButton(strikethrough), - new EditorButton(superscript), - new EditorButton(subscript), - new EditorButton(code), - new EditorButton(clearFormating), // Lists - new EditorButton(bulletList), - new EditorButton(numberList), - new EditorButton(taskList), + new EditorOverflowContainer(3, [ + new EditorButton(bulletList), + new EditorButton(numberList), + new EditorButton(taskList), + ]), // Insert types new EditorOverflowContainer(6, [ new EditorButton(link), - new EditorDropdownButton(table, [ + new EditorDropdownButton(table, false, [ new EditorTableCreator(), ]), new EditorButton(image), @@ -73,21 +82,23 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement { ]), // Meta elements - new EditorButton(source), - new EditorButton(fullscreen), + new EditorOverflowContainer(3, [ + new EditorButton(source), + new EditorButton(fullscreen), - // Test - new EditorButton({ - label: 'Test button', - action(context: EditorUiContext) { - context.editor.update(() => { - // Do stuff - }); - }, - isActive() { - return false; - } - }) + // Test + new EditorButton({ + label: 'Test button', + action(context: EditorUiContext) { + context.editor.update(() => { + // Do stuff + }); + }, + isActive() { + return false; + } + }) + ]), ]); } diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss index 5305ada82..f5e166cc3 100644 --- a/resources/sass/_editor.scss +++ b/resources/sass/_editor.scss @@ -37,12 +37,13 @@ body.editor-is-fullscreen { // Buttons .editor-button { font-size: 12px; - padding: 4px 6px; + padding: 4px; color: #444; border-radius: 4px; display: flex; align-items: center; justify-content: center; + margin: 2px; } .editor-button:hover { background-color: #EEE; @@ -67,6 +68,7 @@ body.editor-is-fullscreen { height: 24px; color: inherit; fill: currentColor; + display: block; } // Containers @@ -79,22 +81,60 @@ body.editor-is-fullscreen { box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.15); z-index: 99; min-width: 120px; + display: flex; + flex-direction: row; } .editor-menu-list { display: flex; flex-direction: column; + align-items: stretch; } -.editor-menu-list > .editor-button { +.editor-menu-list .editor-button { border-bottom: 0; text-align: start; + display: block; + width: 100%; +} +.editor-menu-list > .editor-dropdown-menu-container .editor-dropdown-menu { + inset-inline-start: 100%; + top: 0; + flex-direction: column; } +.editor-format-menu-toggle { + width: 130px; + height: 32px; + overflow: hidden; + padding-inline: 12px; + justify-content: start; + background-image: url('data:image/svg+xml;utf8,'); + background-repeat: no-repeat; + background-position: 98% 50%; + background-size: 28px; +} .editor-format-menu .editor-dropdown-menu { - min-width: 320px; + min-width: 300px; + .editor-dropdown-menu { + min-width: 220px; + } +} +.editor-format-menu .editor-dropdown-menu .editor-dropdown-menu-container > .editor-button { + padding: 8px 10px; } .editor-overflow-container { display: flex; + border-inline: 1px solid #DDD; + padding-inline: 4px; + &:first-child { + border-inline-start: none; + } + &:last-child { + border-inline-end: none; + } + + .editor-overflow-container { + border-inline-start: none; + } } .editor-context-toolbar { @@ -104,6 +144,8 @@ body.editor-is-fullscreen { padding: .2rem; border-radius: 4px; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12); + display: flex; + flex-direction: row; &:before { content: ''; z-index: -1;