summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-09-23 10:36:19 +0000
committerSébastien Theys <seb@odoo.com>2020-09-23 17:08:28 +0000
commitdc951b9c2e7948492137c313a80b0070c044b02f (patch)
tree48a58d8dc89693403ff6bdf9cfef61306ecb2146 /addons/web/static/src
parentabab265cc252128a9a1beab67707bd96ba363d4c (diff)
[FIX] web: properly compute popover position
- avoid updating default position list in place - ensure computed position takes into account enough margin with border - fallback to requested position instead of bottom Part of task-2270731 closes odoo/odoo#58368 X-original-commit: 3fad17421518732db2085c3eec596600cfe34f03 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com> Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/core/popover.js59
-rw-r--r--addons/web/static/src/scss/popover.scss4
2 files changed, 41 insertions, 22 deletions
diff --git a/addons/web/static/src/js/core/popover.js b/addons/web/static/src/js/core/popover.js
index 28843d55d47..6210ce940b5 100644
--- a/addons/web/static/src/js/core/popover.js
+++ b/addons/web/static/src/js/core/popover.js
@@ -16,13 +16,13 @@ odoo.define('web.Popover', function () {
class Popover extends Component {
/**
* @param {Object} props
- * @param {String} props.position='bottom' 'top', 'bottom', 'left' or 'right'
+ * @param {String} [props.position='bottom']
* @param {String} [props.title]
*/
constructor() {
super(...arguments);
this.popoverRef = useRef('popover');
- this.orderedPositions = ['top', 'right', 'bottom', 'left'];
+ this.orderedPositions = ['top', 'bottom', 'left', 'right'];
this.state = useState({
displayed: false,
});
@@ -99,7 +99,9 @@ odoo.define('web.Popover', function () {
return;
}
- const positionIndex = this.orderedPositions.indexOf(
+ // copy the default ordered position to avoid updating them in place
+ const possiblePositions = [...this.orderedPositions];
+ const positionIndex = possiblePositions.indexOf(
this.props.position
);
@@ -110,9 +112,9 @@ odoo.define('web.Popover', function () {
// check if the requested position fits the viewport; if not,
// try all other positions and find one that does
- const position = this.orderedPositions
+ const position = possiblePositions
.slice(positionIndex)
- .concat(this.orderedPositions.slice(0, positionIndex))
+ .concat(possiblePositions.slice(0, positionIndex))
.map((pos) => positioningData[pos])
.find((pos) => {
this.popoverRef.el.style.top = `${pos.top}px`;
@@ -127,20 +129,19 @@ odoo.define('web.Popover', function () {
);
});
- // remove all positioning classes
- this.orderedPositions.forEach((pos) => {
+ // remove all existing positioning classes
+ possiblePositions.forEach((pos) => {
this.popoverRef.el.classList.remove(`o_popover--${pos}`);
});
- // apply the found position ('position' props or another position
- // that fits the viewport) if it fits the viewport, otherwise
- // fallback to 'bottom' if no position fits the viewport
if (position) {
+ // apply the preferred found position that fits the viewport
this.popoverRef.el.classList.add(`o_popover--${position.name}`);
} else {
- this.popoverRef.el.style.top = `${positioningData.bottom.top}px`;
- this.popoverRef.el.style.left = `${positioningData.bottom.left}px`;
- this.popoverRef.el.classList.add(`o_popover--bottom`);
+ // use the given `position` props because no position fits
+ this.popoverRef.el.style.top = `${positioningData[this.props.position].top}px`;
+ this.popoverRef.el.style.left = `${positioningData[this.props.position].left}px`;
+ this.popoverRef.el.classList.add(`o_popover--${this.props.position}`);
}
}
@@ -233,14 +234,20 @@ odoo.define('web.Popover', function () {
/**
* Compute the expected positioning coordinates for each possible
* positioning based on the target and popover sizes.
+ * In particular the popover must not overflow the viewport in any
+ * direction, it should actually stay at `margin` distance from the
+ * border to look good.
*
* @static
* @param {HTMLElement} popoverElement The popover element
* @param {HTMLElement} targetElement The target element, to which
* the popover will be visually 'bound'
+ * @param {integer} [margin=16] Minimal accepted margin from the border
+ * of the viewport.
* @returns {Object}
*/
- static computePositioningData(popoverElement, targetElement) {
+ static computePositioningData(popoverElement, targetElement, margin = 16) {
+ // set target position, possible position
const boundingRectangle = targetElement.getBoundingClientRect();
const targetTop = boundingRectangle.top;
const targetLeft = boundingRectangle.left;
@@ -248,25 +255,41 @@ odoo.define('web.Popover', function () {
const targetWidth = targetElement.offsetWidth;
const popoverHeight = popoverElement.offsetHeight;
const popoverWidth = popoverElement.offsetWidth;
+ const windowWidth = window.innerWidth || document.documentElement.clientWidth;
+ const windowHeight = window.innerHeight || document.documentElement.clientHeight;
+ const leftOffsetForVertical = Math.max(
+ margin,
+ Math.min(
+ Math.round(targetLeft - (popoverWidth - targetWidth) / 2),
+ windowWidth - popoverWidth - margin,
+ ),
+ );
+ const topOffsetForHorizontal = Math.max(
+ margin,
+ Math.min(
+ Math.round(targetTop - (popoverHeight - targetHeight) / 2),
+ windowHeight - popoverHeight - margin,
+ ),
+ );
return {
top: {
name: 'top',
top: Math.round(targetTop - popoverHeight),
- left: Math.round(targetLeft - (popoverWidth - targetWidth) / 2),
+ left: leftOffsetForVertical,
},
right: {
name: 'right',
- top: Math.round(targetTop - (popoverHeight - targetHeight) / 2),
+ top: topOffsetForHorizontal,
left: Math.round(targetLeft + targetWidth),
},
bottom: {
name: 'bottom',
top: Math.round(targetTop + targetHeight),
- left: Math.round(targetLeft - (popoverWidth - targetWidth) / 2),
+ left: leftOffsetForVertical,
},
left: {
name: 'left',
- top: Math.round(targetTop - (popoverHeight - targetHeight) / 2),
+ top: topOffsetForHorizontal,
left: Math.round(targetLeft - popoverWidth),
},
};
diff --git a/addons/web/static/src/scss/popover.scss b/addons/web/static/src/scss/popover.scss
index aea72dcb446..500d097709e 100644
--- a/addons/web/static/src/scss/popover.scss
+++ b/addons/web/static/src/scss/popover.scss
@@ -73,7 +73,6 @@
}
&--top {
- margin-top: -$arrow-size - $arrow-pad;
animation: $animation-time slide-top;
&::before,
@@ -94,7 +93,6 @@
}
&--right {
- margin-left: $arrow-size + $arrow-pad;
animation: $animation-time slide-right;
&::before,
@@ -115,7 +113,6 @@
}
&--bottom {
- margin-top: $arrow-size + $arrow-pad;
animation: $animation-time slide-bottom;
&::before,
@@ -136,7 +133,6 @@
}
&--left {
- margin-left: -$arrow-size - $arrow-pad;
animation: $animation-time slide-left;
&::before,