From 23232e7ea154365c316cb714db3ba215409882a2 Mon Sep 17 00:00:00 2001 From: Tanya Fomina Date: Sat, 16 Apr 2022 12:50:14 +0800 Subject: [PATCH 1/3] Change popover opening direction based on available space below it --- src/components/ui/toolbox.ts | 37 +++++++++++++++++++++++++++++++-- src/components/utils/popover.ts | 19 +++++++++++++++++ src/styles/toolbox.css | 8 ++++++- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/components/ui/toolbox.ts b/src/components/ui/toolbox.ts index 47d14d564..693d7fdea 100644 --- a/src/components/ui/toolbox.ts +++ b/src/components/ui/toolbox.ts @@ -100,6 +100,7 @@ export default class Toolbox extends EventsDispatcher { private static get CSS(): { [name: string]: string } { return { toolbox: 'ce-toolbox', + toolboxOpenedTop: 'ce-toolbox--opened-top', }; } @@ -108,6 +109,12 @@ export default class Toolbox extends EventsDispatcher { */ private clickListenerId: string = null; + /** + * Stores popover height. + * Allows not to recalculate height on each popover opening + */ + private popoverHeightCached: number | null = null; + /** * Toolbox constructor * @@ -199,8 +206,22 @@ export default class Toolbox extends EventsDispatcher { return; } - this.popover.show(); + /** + * Calculate popover height on first open + */ + if (this.popoverHeightCached === null) { + this.popoverHeightCached = this.popover.calculateHeight(); + } + /** + * Open popover top if there is not enought available space below it + */ + if (!this.canPopoverOpenBottom) { + this.nodes.toolbox.style.setProperty('--popover-height', this.popoverHeightCached + 'px'); + this.nodes.toolbox.classList.add(Toolbox.CSS.toolboxOpenedTop); + } + + this.popover.show(); this.opened = true; this.emit(ToolboxEvent.Opened); } @@ -210,8 +231,8 @@ export default class Toolbox extends EventsDispatcher { */ public close(): void { this.popover.hide(); - this.opened = false; + this.nodes.toolbox.classList.remove(Toolbox.CSS.toolboxOpenedTop); this.emit(ToolboxEvent.Closed); } @@ -226,6 +247,18 @@ export default class Toolbox extends EventsDispatcher { } } + /** + * Checks if there is enough available space to open popover downwards. + */ + private get canPopoverOpenBottom(): boolean { + const toolboxRect = this.nodes.toolbox.getBoundingClientRect(); + const popoverBottomEdge = toolboxRect.top + this.popoverHeightCached; + + debugger; + + return popoverBottomEdge <= window.innerHeight; + } + /** * Returns list of tools that enables the Toolbox (by specifying the 'toolbox' getter) */ diff --git a/src/components/utils/popover.ts b/src/components/utils/popover.ts index aa3168470..4beef2fff 100644 --- a/src/components/utils/popover.ts +++ b/src/components/utils/popover.ts @@ -216,6 +216,25 @@ export default class Popover extends EventsDispatcher { return this.flipper.hasFocus(); } + /** + * Helps to calculate height of popover while it is not displayed on screen. + * Renders invisible clone of popover to get actual height. + */ + public calculateHeight(): number { + let height = 0; + const popoverClone = this.nodes.popover.cloneNode(true) as HTMLElement; + + popoverClone.style.visibility = 'hidden'; + popoverClone.style.position = 'absolute'; + popoverClone.style.top = '-1000px'; + popoverClone.classList.add(Popover.CSS.popoverOpened); + document.body.appendChild(popoverClone); + height = popoverClone.offsetHeight; + popoverClone.remove(); + + return height; + } + /** * Makes the UI */ diff --git a/src/styles/toolbox.css b/src/styles/toolbox.css index d933d8c46..d8b602100 100644 --- a/src/styles/toolbox.css +++ b/src/styles/toolbox.css @@ -1,8 +1,14 @@ .ce-toolbox { + --gap: 8px; + @media (--not-mobile){ position: absolute; - top: var(--toolbar-buttons-size); + top: calc(var(--toolbox-buttons-size) + var(--gap)); left: 0; + + &--opened-top { + top: calc(-1 * (var(--gap) + var(--popover-height))); + } } } From b79b44fd690ed877cc79375907df86217f7cf428 Mon Sep 17 00:00:00 2001 From: Tanya Fomina Date: Sat, 16 Apr 2022 13:32:11 +0800 Subject: [PATCH 2/3] Update check --- src/components/ui/toolbox.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/ui/toolbox.ts b/src/components/ui/toolbox.ts index 693d7fdea..d87566e03 100644 --- a/src/components/ui/toolbox.ts +++ b/src/components/ui/toolbox.ts @@ -216,7 +216,7 @@ export default class Toolbox extends EventsDispatcher { /** * Open popover top if there is not enought available space below it */ - if (!this.canPopoverOpenBottom) { + if (!this.shouldOpenPopoverBottom) { this.nodes.toolbox.style.setProperty('--popover-height', this.popoverHeightCached + 'px'); this.nodes.toolbox.classList.add(Toolbox.CSS.toolboxOpenedTop); } @@ -248,15 +248,17 @@ export default class Toolbox extends EventsDispatcher { } /** - * Checks if there is enough available space to open popover downwards. + * Checks if there popover should be opened downwards. + * It happens in case there is enough space below or not enough space above */ - private get canPopoverOpenBottom(): boolean { + private get shouldOpenPopoverBottom(): boolean { const toolboxRect = this.nodes.toolbox.getBoundingClientRect(); - const popoverBottomEdge = toolboxRect.top + this.popoverHeightCached; + const editorElementRect = this.api.ui.nodes.redactor.getBoundingClientRect(); + const popoverPotentialBottomEdge = toolboxRect.top + this.popoverHeightCached; + const popoverPotentialTopEdge = toolboxRect.top - this.popoverHeightCached; + const bottomEdgeForComparison = Math.min(window.innerHeight, editorElementRect.bottom); - debugger; - - return popoverBottomEdge <= window.innerHeight; + return popoverPotentialTopEdge < editorElementRect.top || popoverPotentialBottomEdge <= bottomEdgeForComparison; } /** From 7b4cba5027552ca711da19beadfa9d9c68b69e17 Mon Sep 17 00:00:00 2001 From: Tanya Fomina Date: Sun, 17 Apr 2022 20:23:22 +0800 Subject: [PATCH 3/3] Use cacheable decorator --- src/components/ui/toolbox.ts | 20 ++++---------------- src/components/utils/popover.ts | 3 ++- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/components/ui/toolbox.ts b/src/components/ui/toolbox.ts index d87566e03..284f4db7a 100644 --- a/src/components/ui/toolbox.ts +++ b/src/components/ui/toolbox.ts @@ -109,12 +109,6 @@ export default class Toolbox extends EventsDispatcher { */ private clickListenerId: string = null; - /** - * Stores popover height. - * Allows not to recalculate height on each popover opening - */ - private popoverHeightCached: number | null = null; - /** * Toolbox constructor * @@ -206,18 +200,11 @@ export default class Toolbox extends EventsDispatcher { return; } - /** - * Calculate popover height on first open - */ - if (this.popoverHeightCached === null) { - this.popoverHeightCached = this.popover.calculateHeight(); - } - /** * Open popover top if there is not enought available space below it */ if (!this.shouldOpenPopoverBottom) { - this.nodes.toolbox.style.setProperty('--popover-height', this.popoverHeightCached + 'px'); + this.nodes.toolbox.style.setProperty('--popover-height', this.popover.calculateHeight() + 'px'); this.nodes.toolbox.classList.add(Toolbox.CSS.toolboxOpenedTop); } @@ -254,8 +241,9 @@ export default class Toolbox extends EventsDispatcher { private get shouldOpenPopoverBottom(): boolean { const toolboxRect = this.nodes.toolbox.getBoundingClientRect(); const editorElementRect = this.api.ui.nodes.redactor.getBoundingClientRect(); - const popoverPotentialBottomEdge = toolboxRect.top + this.popoverHeightCached; - const popoverPotentialTopEdge = toolboxRect.top - this.popoverHeightCached; + const popoverHeight = this.popover.calculateHeight(); + const popoverPotentialBottomEdge = toolboxRect.top + popoverHeight; + const popoverPotentialTopEdge = toolboxRect.top - popoverHeight; const bottomEdgeForComparison = Math.min(window.innerHeight, editorElementRect.bottom); return popoverPotentialTopEdge < editorElementRect.top || popoverPotentialBottomEdge <= bottomEdgeForComparison; diff --git a/src/components/utils/popover.ts b/src/components/utils/popover.ts index 4beef2fff..e19ad83ee 100644 --- a/src/components/utils/popover.ts +++ b/src/components/utils/popover.ts @@ -3,7 +3,7 @@ import Listeners from './listeners'; import Flipper from '../flipper'; import SearchInput from './search-input'; import EventsDispatcher from './events'; -import { isMobile, keyCodes } from '../utils'; +import { isMobile, keyCodes, cacheable } from '../utils'; /** * Describe parameters for rendering the single item of Popover @@ -220,6 +220,7 @@ export default class Popover extends EventsDispatcher { * Helps to calculate height of popover while it is not displayed on screen. * Renders invisible clone of popover to get actual height. */ + @cacheable public calculateHeight(): number { let height = 0; const popoverClone = this.nodes.popover.cloneNode(true) as HTMLElement;