Fixed Some Bugs
This commit is contained in:
+110
-88
@@ -197,13 +197,18 @@ const id = `dd-${Math.random().toString(36).slice(2)}`;
|
||||
|
||||
const side = sideOf(pref);
|
||||
const align = alignOf(pref);
|
||||
|
||||
const AR = showArrow && arrowEl ? 8 : 0; // arrow size
|
||||
const SAFE = Math.max(shiftPadding, 8); // viewport padding
|
||||
const GAP = offset + AR; // desired gap + arrow
|
||||
|
||||
let x = 0,
|
||||
y = 0;
|
||||
|
||||
if (side === "bottom") y = t.bottom + offset;
|
||||
else if (side === "top") y = t.top - p.height - offset;
|
||||
else if (side === "right") x = t.right + offset;
|
||||
else if (side === "left") x = t.left - p.width - offset;
|
||||
if (side === "bottom") y = t.bottom + GAP;
|
||||
else if (side === "top") y = t.top - p.height - GAP;
|
||||
else if (side === "right") x = t.right + GAP;
|
||||
else if (side === "left") x = t.left - p.width - GAP;
|
||||
|
||||
if (side === "top" || side === "bottom") {
|
||||
if (align === "start") x = t.left;
|
||||
@@ -215,116 +220,99 @@ const id = `dd-${Math.random().toString(36).slice(2)}`;
|
||||
else y = t.top + (t.height - p.height) / 2;
|
||||
}
|
||||
|
||||
const pad = shiftPadding;
|
||||
x = Math.max(pad, Math.min(x, vw - p.width - pad));
|
||||
y = Math.max(pad, Math.min(y, vh - p.height - pad));
|
||||
return { x, y }; // no Math.round
|
||||
}
|
||||
// Clamp into viewport
|
||||
x = Math.max(SAFE, Math.min(x, vw - p.width - SAFE));
|
||||
y = Math.max(SAFE, Math.min(y, vh - p.height - SAFE));
|
||||
|
||||
function positionPanel() {
|
||||
if (!open) return;
|
||||
if (matchTriggerWidth) {
|
||||
const w = Math.max(192, trigger.getBoundingClientRect().width);
|
||||
panel.style.minWidth = `${w}px`;
|
||||
}
|
||||
const best = smartPosition();
|
||||
panel.style.left = `${best.x}px`;
|
||||
panel.style.top = `${best.y}px`;
|
||||
// Return overflow score (lower is better)
|
||||
const over =
|
||||
Math.max(0, SAFE - x) +
|
||||
Math.max(0, SAFE - y) +
|
||||
Math.max(0, x + p.width - (vw - SAFE)) +
|
||||
Math.max(0, y + p.height - (vh - SAFE));
|
||||
|
||||
const side = sideOf(best.cand),
|
||||
align = alignOf(best.cand);
|
||||
setOriginFor(side, align);
|
||||
panel.dataset.side = side;
|
||||
|
||||
if (showArrow && arrowEl) {
|
||||
Object.assign(arrowEl.style, {
|
||||
left: "",
|
||||
top: "",
|
||||
right: "",
|
||||
bottom: "",
|
||||
});
|
||||
const t = trigger.getBoundingClientRect();
|
||||
const p = panel.getBoundingClientRect();
|
||||
const aw = arrowEl.getBoundingClientRect().width;
|
||||
const ah = arrowEl.getBoundingClientRect().height;
|
||||
const AR = 7;
|
||||
|
||||
if (side === "bottom") {
|
||||
arrowEl.style.top = `${-AR}px`;
|
||||
const cx = t.left + t.width / 2;
|
||||
arrowEl.style.left = `${cx - p.left - aw / 2}px`;
|
||||
} else if (side === "top") {
|
||||
arrowEl.style.bottom = `${-AR}px`;
|
||||
const cx = t.left + t.width / 2;
|
||||
arrowEl.style.left = `${cx - p.left - aw / 2}px`;
|
||||
} else if (side === "right") {
|
||||
arrowEl.style.left = `${-AR}px`;
|
||||
const cy = t.top + t.height / 2;
|
||||
arrowEl.style.top = `${cy - p.top - ah / 2}px`;
|
||||
} else if (side === "left") {
|
||||
arrowEl.style.right = `${-AR}px`;
|
||||
const cy = t.top + t.height / 2;
|
||||
arrowEl.style.top = `${cy - p.top - ah / 2}px`;
|
||||
}
|
||||
}
|
||||
return { x: Math.round(x), y: Math.round(y), over };
|
||||
}
|
||||
|
||||
function smartPosition() {
|
||||
const s = sideOf(placement),
|
||||
a = alignOf(placement);
|
||||
const flips = {
|
||||
const s = sideOf(placement);
|
||||
const a = alignOf(placement);
|
||||
|
||||
const flipSide = {
|
||||
top: "bottom",
|
||||
bottom: "top",
|
||||
left: "right",
|
||||
right: "left",
|
||||
};
|
||||
const candidates = [placement, `${flips[s]}${a ? `-${a}` : ""}`];
|
||||
if (s === "top" || s === "bottom")
|
||||
candidates.push(
|
||||
`left-${a || "start"}`,
|
||||
`right-${a || "start"}`,
|
||||
);
|
||||
else
|
||||
candidates.push(
|
||||
`top-${a || "start"}`,
|
||||
`bottom-${a || "start"}`,
|
||||
);
|
||||
}[s];
|
||||
|
||||
const vw = document.documentElement.clientWidth;
|
||||
const vh = document.documentElement.clientHeight;
|
||||
// Try: requested → side flipped → align flipped → both flipped → orthogonal sides
|
||||
const aligns = a
|
||||
? [a, a === "start" ? "end" : "start", "center"]
|
||||
: ["center", "start", "end"];
|
||||
const candidates = [];
|
||||
|
||||
aligns.forEach((al) =>
|
||||
candidates.push(`${s}${al === "center" ? "" : "-" + al}`),
|
||||
);
|
||||
aligns.forEach((al) =>
|
||||
candidates.push(
|
||||
`${flipSide}${al === "center" ? "" : "-" + al}`,
|
||||
),
|
||||
);
|
||||
|
||||
if (s === "top" || s === "bottom") {
|
||||
aligns.forEach((al) =>
|
||||
candidates.push(`left-${al === "center" ? "start" : al}`),
|
||||
);
|
||||
aligns.forEach((al) =>
|
||||
candidates.push(`right-${al === "center" ? "start" : al}`),
|
||||
);
|
||||
} else {
|
||||
aligns.forEach((al) =>
|
||||
candidates.push(`top-${al === "center" ? "start" : al}`),
|
||||
);
|
||||
aligns.forEach((al) =>
|
||||
candidates.push(`bottom-${al === "center" ? "start" : al}`),
|
||||
);
|
||||
}
|
||||
|
||||
let best = null;
|
||||
for (const cand of candidates) {
|
||||
const pos = placeOnce(cand);
|
||||
const p = panel.getBoundingClientRect();
|
||||
const w = p.width,
|
||||
h = p.height;
|
||||
const over =
|
||||
Math.max(0, -pos.x) +
|
||||
Math.max(0, -pos.y) +
|
||||
Math.max(0, pos.x + w - vw) +
|
||||
Math.max(0, pos.y + h - vh);
|
||||
if (!best || over < best.score)
|
||||
best = { cand, ...pos, score: over };
|
||||
if (!best || pos.over < best.over) best = { cand, ...pos };
|
||||
if (pos.over === 0) break; // perfect fit
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
function positionPanel() {
|
||||
if (!open) return;
|
||||
|
||||
// Constrain size so the panel never forces overflow
|
||||
const vw = document.documentElement.clientWidth;
|
||||
const vh = document.documentElement.clientHeight;
|
||||
const PAD = Math.max(shiftPadding, 8);
|
||||
|
||||
// Optional: match trigger width
|
||||
if (matchTriggerWidth) {
|
||||
const w = Math.max(192, trigger.getBoundingClientRect().width);
|
||||
panel.style.minWidth = `${Math.round(w)}px`;
|
||||
}
|
||||
|
||||
panel.style.maxWidth = `${vw - PAD * 2}px`;
|
||||
panel.style.maxHeight = `${vh - PAD * 2}px`;
|
||||
panel.style.overflow = "auto"; // scroll when needed
|
||||
|
||||
const best = smartPosition();
|
||||
panel.style.left = best.x + "px";
|
||||
panel.style.top = best.y + "px";
|
||||
|
||||
const side = sideOf(best.cand),
|
||||
align = alignOf(best.cand);
|
||||
const side = sideOf(best.cand);
|
||||
const align = alignOf(best.cand);
|
||||
setOriginFor(side, align);
|
||||
panel.dataset.side = side;
|
||||
|
||||
// Arrow
|
||||
if (showArrow && arrowEl) {
|
||||
Object.assign(arrowEl.style, {
|
||||
left: "",
|
||||
@@ -336,26 +324,60 @@ const id = `dd-${Math.random().toString(36).slice(2)}`;
|
||||
const p = panel.getBoundingClientRect();
|
||||
const aw = arrowEl.getBoundingClientRect().width;
|
||||
const ah = arrowEl.getBoundingClientRect().height;
|
||||
const AR = 7;
|
||||
const AR = 8;
|
||||
|
||||
if (side === "bottom") {
|
||||
arrowEl.style.top = -AR + "px";
|
||||
const cx = t.left + t.width / 2;
|
||||
arrowEl.style.left =
|
||||
Math.round(cx - p.left - aw / 2) + "px";
|
||||
Math.round(
|
||||
Math.max(
|
||||
PAD,
|
||||
Math.min(
|
||||
cx - p.left - aw / 2,
|
||||
p.width - PAD - aw,
|
||||
),
|
||||
),
|
||||
) + "px";
|
||||
} else if (side === "top") {
|
||||
arrowEl.style.bottom = -AR + "px";
|
||||
const cx = t.left + t.width / 2;
|
||||
arrowEl.style.left =
|
||||
Math.round(cx - p.left - aw / 2) + "px";
|
||||
Math.round(
|
||||
Math.max(
|
||||
PAD,
|
||||
Math.min(
|
||||
cx - p.left - aw / 2,
|
||||
p.width - PAD - aw,
|
||||
),
|
||||
),
|
||||
) + "px";
|
||||
} else if (side === "right") {
|
||||
arrowEl.style.left = -AR + "px";
|
||||
const cy = t.top + t.height / 2;
|
||||
arrowEl.style.top = Math.round(cy - p.top - ah / 2) + "px";
|
||||
arrowEl.style.top =
|
||||
Math.round(
|
||||
Math.max(
|
||||
PAD,
|
||||
Math.min(
|
||||
cy - p.top - ah / 2,
|
||||
p.height - PAD - ah,
|
||||
),
|
||||
),
|
||||
) + "px";
|
||||
} else if (side === "left") {
|
||||
arrowEl.style.right = -AR + "px";
|
||||
const cy = t.top + t.height / 2;
|
||||
arrowEl.style.top = Math.round(cy - p.top - ah / 2) + "px";
|
||||
arrowEl.style.top =
|
||||
Math.round(
|
||||
Math.max(
|
||||
PAD,
|
||||
Math.min(
|
||||
cy - p.top - ah / 2,
|
||||
p.height - PAD - ah,
|
||||
),
|
||||
),
|
||||
) + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user