[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:
@@ -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")
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user