[IMP] web: position_hook supports fit variant

This commits adds another variant to the position hook. This one allows
a popper to be positionned next to a side, having the same dimension from
this side. A classname is then added to the popper, allowing the element
to be stylized depending of the side it's fitting.

Possible values:
(bottom/top)-fit would give the same width as the reference
(left/right)-fit would give the same height as the reference

Tests have been added to make sure those dimensions are correctly
applied when using thr fit variant.

Part-of: odoo/odoo#106265
This commit is contained in:
luvi
2022-12-23 17:03:36 +01:00
parent 97cc94269d
commit 6a7b3dedaf
2 changed files with 49 additions and 4 deletions
+16 -4
View File
@@ -36,7 +36,7 @@ import { onWillUnmount, useEffect, useExternalListener, useRef } from "@odoo/owl
* }} VariantsData
*
* @typedef {"top" | "left" | "bottom" | "right"} Direction
* @typedef {"start" | "middle" | "end"} Variant
* @typedef {"start" | "middle" | "end" | "fit"} Variant
*
* @typedef {{[direction in Direction]: string}} DirectionFlipOrder
* values are successive DirectionsDataKey represented as a single string
@@ -57,11 +57,13 @@ import { onWillUnmount, useEffect, useExternalListener, useRef } from "@odoo/owl
/** @type {{[d: string]: Direction}} */
const DIRECTIONS = { t: "top", r: "right", b: "bottom", l: "left" };
/** @type {{[v: string]: Variant}} */
const VARIANTS = { s: "start", m: "middle", e: "end" };
const VARIANTS = { s: "start", m: "middle", e: "end", f: "fit" };
/** @type DirectionFlipOrder */
const DIRECTION_FLIP_ORDER = { top: "tbrl", right: "rltb", bottom: "btrl", left: "lrbt" };
/** @type VariantFlipOrder */
const VARIANT_FLIP_ORDER = { start: "sme", middle: "mse", end: "ems" };
const VARIANT_FLIP_ORDER = { start: "sme", middle: "mse", end: "ems", fit: "f" };
/** @type DirectionFlipOrder */
const FIT_FLIP_ORDER = { top: "tb", right: "rl", bottom: "bt", left: "lr" };
/** @type {Options} */
const DEFAULTS = {
@@ -86,7 +88,8 @@ const DEFAULTS = {
function getBestPosition(reference, popper, { container, margin, position }) {
// Retrieve directions and variants
const [directionKey, variantKey = "middle"] = position.split("-");
const directions = DIRECTION_FLIP_ORDER[directionKey];
const directions =
variantKey === "fit" ? FIT_FLIP_ORDER[directionKey] : DIRECTION_FLIP_ORDER[directionKey];
const variants = VARIANT_FLIP_ORDER[variantKey];
// Boxes
@@ -106,9 +109,11 @@ function getBestPosition(reference, popper, { container, margin, position }) {
};
/** @type {VariantsData} */
const variantsData = {
vf: refBox.left,
vs: refBox.left,
vm: refBox.left + refBox.width / 2 + -popBox.width / 2,
ve: refBox.right - popBox.width,
hf: refBox.top,
hs: refBox.top,
hm: refBox.top + refBox.height / 2 + -popBox.height / 2,
he: refBox.bottom - popBox.height,
@@ -198,6 +203,7 @@ function getBestPosition(reference, popper, { container, margin, position }) {
* @param {Options} options
*/
export function reposition(reference, popper, options) {
const [directionKey, variantKey = "middle"] = options.position.split("-");
options = {
container: document.documentElement,
...options,
@@ -213,6 +219,12 @@ export function reposition(reference, popper, options) {
const { top, left } = position;
popper.style.top = `${top}px`;
popper.style.left = `${left}px`;
if (variantKey === "fit") {
const styleProperty = ["top", "bottom"].includes(directionKey) ? "width" : "height";
popper.style[styleProperty] = reference.getBoundingClientRect()[styleProperty] + "px";
}
if (options.onPositioned) {
options.onPositioned(popper, position);
}
@@ -812,3 +812,36 @@ QUnit.test(
"reposition from right-end to top-end",
getRepositionTest("right-end", "top-end", "w125 right")
);
function getFittingTest(position, styleAttribute) {
return async (assert) => {
const TestComp = getTestComponent({ position });
await mount(TestComp, container);
assert.strictEqual(container.querySelector("#popper").style[styleAttribute], "50px");
}
}
QUnit.test(
"reposition from bottom-fit to top-fit", getRepositionTest("bottom-fit", "top-fit", "bottom")
);
QUnit.test(
"reposition from top-fit to bottom-fit", getRepositionTest("top-fit", "bottom-fit", "top")
);
QUnit.test(
"reposition from right-fit to left-fit", getRepositionTest("right-fit", "left-fit", "right")
);
QUnit.test(
"reposition from left-fit to right-fit", getRepositionTest("left-fit", "right-fit", "left")
);
QUnit.test(
"bottom-fit has the same width as the reference", getFittingTest("bottom-fit", "width")
);
QUnit.test(
"top-fit has the same width as the reference", getFittingTest("top-fit", "width")
);
QUnit.test(
"left-fit has the same height as the reference", getFittingTest("left-fit", "height")
);
QUnit.test(
"right-fit has the same height as the reference", getFittingTest("right-fit", "height")
);