Fixed Some Bugs

This commit is contained in:
2025-08-17 15:05:44 +05:30
parent f8a0e82076
commit b362d10519
16 changed files with 549 additions and 306 deletions
+110 -88
View File
@@ -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";
}
}
}