[FIX] web_editor: toolbar scroll over navbar
Before this commit the floating toolbar would scroll over the headers above the html field, such as the form view header and the web client navbar. This commit ensures the toolbar is hidden when it overflows the scroll container. task-3263463 X-original-commit: 96d180c7591bcb359c5b74796f39c0e36ea541fa Part-of: odoo/odoo#133459
This commit is contained in:
@@ -216,6 +216,7 @@ export class OdooEditor extends EventTarget {
|
||||
getContextFromParentRect: () => {
|
||||
return { top: 0, left: 0 };
|
||||
},
|
||||
getScrollContainerRect: () => document.body.getBoundingClientRect(),
|
||||
toSanitize: true,
|
||||
isRootEditable: true,
|
||||
placeholder: false,
|
||||
@@ -3070,7 +3071,8 @@ export class OdooEditor extends EventTarget {
|
||||
_positionToolbar() {
|
||||
const OFFSET = 10;
|
||||
let isBottom = false;
|
||||
this.toolbar.classList.toggle('toolbar-bottom', false);
|
||||
// Toolbar display must not be none in order to calculate width and height.
|
||||
this.toolbar.classList.toggle('d-none', false);
|
||||
this.toolbar.style.maxWidth = window.innerWidth - OFFSET * 2 + 'px';
|
||||
const sel = this.document.getSelection();
|
||||
const range = sel.getRangeAt(0);
|
||||
@@ -3095,6 +3097,7 @@ export class OdooEditor extends EventTarget {
|
||||
const toolbarHeight = this.toolbar.offsetHeight;
|
||||
const editorRect = this.editable.getBoundingClientRect();
|
||||
const parentContextRect = this.options.getContextFromParentRect();
|
||||
const scrollContainerRect = this.options.getScrollContainerRect();
|
||||
const editorTopPos = Math.max(0, editorRect.top);
|
||||
const scrollX = document.defaultView.scrollX;
|
||||
const scrollY = document.defaultView.scrollY;
|
||||
@@ -3111,14 +3114,12 @@ export class OdooEditor extends EventTarget {
|
||||
|
||||
// Get top position.
|
||||
let top = correctedSelectionRect.top - toolbarHeight - OFFSET;
|
||||
// Ensure the toolbar doesn't overflow the editor on the top.
|
||||
if (top < editorTopPos) {
|
||||
// Ensure the toolbar doesn't overflow the editor or scroll container on the top.
|
||||
if (top < editorTopPos || top + parentContextRect.top - scrollContainerRect.top < OFFSET / 2) {
|
||||
// Position the toolbar below the selection.
|
||||
top = correctedSelectionRect.bottom + OFFSET;
|
||||
isBottom = true;
|
||||
}
|
||||
// Ensure the toolbar doesn't overflow the editor on the bottom.
|
||||
top = Math.min(window.innerHeight - OFFSET - toolbarHeight, top);
|
||||
// Offset top to compensate for parent context position (eg. Iframe).
|
||||
top += parentContextRect.top;
|
||||
this.toolbar.style.top = scrollY + top + 'px';
|
||||
@@ -3130,12 +3131,18 @@ export class OdooEditor extends EventTarget {
|
||||
// Ensure the arrow doesn't overflow the toolbar on the right.
|
||||
arrowLeftPos = Math.min(toolbarWidth - OFFSET - 20, arrowLeftPos);
|
||||
this.toolbar.style.setProperty('--arrow-left-pos', arrowLeftPos + 'px');
|
||||
if (isBottom) {
|
||||
this.toolbar.classList.toggle('toolbar-bottom', true);
|
||||
this.toolbar.style.setProperty('--arrow-top-pos', -17 + 'px');
|
||||
} else {
|
||||
this.toolbar.style.setProperty('--arrow-top-pos', toolbarHeight - 3 + 'px');
|
||||
}
|
||||
const arrowTopPos = isBottom ? -17 : toolbarHeight - 3;
|
||||
this.toolbar.classList.toggle('toolbar-bottom', isBottom);
|
||||
this.toolbar.style.setProperty('--arrow-top-pos', arrowTopPos + 'px');
|
||||
|
||||
// Calculate toolbar dimensions including the arrow.
|
||||
const toolbarTop = Math.min(top , top + arrowTopPos);
|
||||
const toolbarBottom = Math.max(top + toolbarHeight, top + arrowTopPos + 20);
|
||||
|
||||
// Hide toolbar if it overflows the scroll container.
|
||||
const distToScrollContainer = Math.min(toolbarTop - scrollContainerRect.top,
|
||||
scrollContainerRect.bottom - toolbarBottom);
|
||||
this.toolbar.classList.toggle('d-none', distToScrollContainer < OFFSET / 2);
|
||||
}
|
||||
|
||||
// PASTING / DROPPING
|
||||
|
||||
@@ -368,6 +368,12 @@ export class Wysiwyg extends Component {
|
||||
allowInlineAtRoot: this.options.allowInlineAtRoot,
|
||||
getYoutubeVideoElement: getYoutubeVideoElement,
|
||||
getContextFromParentRect: options.getContextFromParentRect,
|
||||
getScrollContainerRect: () => {
|
||||
if (!this.scrollContainer || !this.scrollContainer.getBoundingClientRect) {
|
||||
this.scrollContainer = document.querySelector('.o_action_manager');
|
||||
}
|
||||
return this.scrollContainer.getBoundingClientRect();
|
||||
},
|
||||
getPowerboxElement: () => {
|
||||
const selection = (this.options.document || document).getSelection();
|
||||
if (selection.isCollapsed && selection.rangeCount) {
|
||||
@@ -2760,6 +2766,7 @@ export class Wysiwyg extends Component {
|
||||
}
|
||||
_onScroll(ev) {
|
||||
if (ev.target.contains(this.$editable[0])) {
|
||||
this.scrollContainer = ev.target;
|
||||
this.odooEditor.updateToolbarPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,12 @@ patch(Wysiwyg.prototype, {
|
||||
if (this.options.inIframe) {
|
||||
const iframeDocument = this.$iframe[0].contentDocument;
|
||||
const scrollInIframe = ev.target === iframeDocument || ev.target.ownerDocument === iframeDocument;
|
||||
if (ev.target.contains(this.$iframe[0]) || scrollInIframe) {
|
||||
if (ev.target.contains(this.$iframe[0])) {
|
||||
this.scrollContainer = ev.target;
|
||||
this.odooEditor.updateToolbarPosition();
|
||||
} else if (scrollInIframe) {
|
||||
// UpdateToolbarPosition needs a scroll container in the top document.
|
||||
this.scrollContainer = this.$iframe[0];
|
||||
this.odooEditor.updateToolbarPosition();
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user