fix: z-index on hover for map markers
- Simple pins (hover card): z-index 150 on hover - Compact cards: z-index 200 on hover - Ensures hover cards are always above other markers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fa8134fd41
commit
e39f5e8226
|
|
@ -705,6 +705,7 @@ function heroImageForPost(post) {
|
||||||
post?.image_large ||
|
post?.image_large ||
|
||||||
post?.image_small ||
|
post?.image_small ||
|
||||||
post?.image ||
|
post?.image ||
|
||||||
|
post?.thumbnail_url ||
|
||||||
post?.media_url ||
|
post?.media_url ||
|
||||||
"";
|
"";
|
||||||
const v = normalizeImageUrl(raw);
|
const v = normalizeImageUrl(raw);
|
||||||
|
|
@ -1263,12 +1264,12 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
<div class="sw-cluster-left">
|
<div class="sw-cluster-left">
|
||||||
<div class="sw-cluster-hero">
|
<div class="sw-cluster-hero">
|
||||||
<div class="sw-truth-rail" data-score="${truth}">
|
<div class="sw-truth-rail" data-score="${truth}">
|
||||||
<button class="sw-truth-rail-btn sw-truth-rail-btn-up" type="button">TRUE</button>
|
<button class="sw-truth-rail-btn sw-truth-rail-btn-up" type="button" title="Vote TRUE">✓</button>
|
||||||
<div class="sw-truth-rail-bar">
|
<div class="sw-truth-rail-bar">
|
||||||
<div class="sw-truth-rail-marker" style="top:${100 - truth}%"></div>
|
<div class="sw-truth-rail-marker" style="top:${100 - truth}%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sw-truth-rail-score">${truth}</div>
|
<div class="sw-truth-rail-score">${truth}</div>
|
||||||
<button class="sw-truth-rail-btn sw-truth-rail-btn-down" type="button">FALSE</button>
|
<button class="sw-truth-rail-btn sw-truth-rail-btn-down" type="button" title="Vote FALSE">✗</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="sw-cluster-hero-media">
|
<div class="sw-cluster-hero-media">
|
||||||
${
|
${
|
||||||
|
|
@ -1345,12 +1346,12 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
<div class="sw-modal-hero-row">
|
<div class="sw-modal-hero-row">
|
||||||
${!isCamera ? `
|
${!isCamera ? `
|
||||||
<div class="sw-truth-rail" data-score="${truth}">
|
<div class="sw-truth-rail" data-score="${truth}">
|
||||||
<button class="sw-truth-rail-btn sw-truth-rail-btn-up" type="button">TRUE</button>
|
<button class="sw-truth-rail-btn sw-truth-rail-btn-up" type="button" title="Vote TRUE">✓</button>
|
||||||
<div class="sw-truth-rail-bar">
|
<div class="sw-truth-rail-bar">
|
||||||
<div class="sw-truth-rail-marker" style="top:${100 - truth}%"></div>
|
<div class="sw-truth-rail-marker" style="top:${100 - truth}%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sw-truth-rail-score">${truth}</div>
|
<div class="sw-truth-rail-score">${truth}</div>
|
||||||
<button class="sw-truth-rail-btn sw-truth-rail-btn-down" type="button">FALSE</button>
|
<button class="sw-truth-rail-btn sw-truth-rail-btn-down" type="button" title="Vote FALSE">✗</button>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
<div class="sw-modal-hero" ${(isCamera || isVideo) ? 'style="flex:1;max-width:100%;"' : ''}>
|
<div class="sw-modal-hero" ${(isCamera || isVideo) ? 'style="flex:1;max-width:100%;"' : ''}>
|
||||||
|
|
@ -1944,32 +1945,71 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
const rail = modalWrap.querySelector(".sw-truth-rail");
|
const rail = modalWrap.querySelector(".sw-truth-rail");
|
||||||
if (rail && postID > 0 && !rail.hasAttribute("data-vote-bound")) {
|
if (rail && postID > 0 && !rail.hasAttribute("data-vote-bound")) {
|
||||||
rail.setAttribute("data-vote-bound", "1");
|
rail.setAttribute("data-vote-bound", "1");
|
||||||
|
// Track current user vote to prevent duplicate API calls
|
||||||
|
let currentVote = null; // "true", "false", or null
|
||||||
|
let isVoting = false;
|
||||||
|
|
||||||
const doVote = async (vote) => {
|
const doVote = async (vote) => {
|
||||||
if (!isAuthed()) {
|
if (!isAuthed()) {
|
||||||
requestAuth("Please sign in or create a free account to vote on truth.");
|
requestAuth("Please sign in or create a free account to vote on truth.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rail.classList.contains("sw-truth-vote-disabled")) {
|
// Skip if already voted same way
|
||||||
return; // Already voting
|
if (currentVote === vote) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (isVoting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isVoting = true;
|
||||||
|
rail.style.opacity = "0.5";
|
||||||
|
rail.style.pointerEvents = "none";
|
||||||
try {
|
try {
|
||||||
rail.classList.add("sw-truth-vote-disabled");
|
|
||||||
const stats = await votePostTruth(postID, vote);
|
const stats = await votePostTruth(postID, vote);
|
||||||
if (stats) {
|
if (stats) {
|
||||||
|
currentVote = vote;
|
||||||
updateTruthVoteUI(modalWrap, stats, true);
|
updateTruthVoteUI(modalWrap, stats, true);
|
||||||
} else {
|
|
||||||
console.warn("votePostTruth returned no stats for post", postID);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Truth vote error:", err);
|
// Silently fail - don't update UI
|
||||||
} finally {
|
} finally {
|
||||||
rail.classList.remove("sw-truth-vote-disabled");
|
isVoting = false;
|
||||||
|
rail.style.opacity = "1";
|
||||||
|
rail.style.pointerEvents = "auto";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const voteUp = rail.querySelector(".sw-truth-rail-btn-up");
|
const voteUp = rail.querySelector(".sw-truth-rail-btn-up");
|
||||||
const voteDown = rail.querySelector(".sw-truth-rail-btn-down");
|
const voteDown = rail.querySelector(".sw-truth-rail-btn-down");
|
||||||
if (voteUp) voteUp.addEventListener("click", (e) => { e.stopPropagation(); doVote("true"); });
|
let touchFired = false;
|
||||||
if (voteDown) voteDown.addEventListener("click", (e) => { e.stopPropagation(); doVote("false"); });
|
const handleVote = (vote) => (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
// Prevent touch+click double fire
|
||||||
|
if (e.type === "touchend") {
|
||||||
|
touchFired = true;
|
||||||
|
setTimeout(() => { touchFired = false; }, 400);
|
||||||
|
} else if (e.type === "click" && touchFired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isVoting || currentVote === vote) return;
|
||||||
|
doVote(vote);
|
||||||
|
};
|
||||||
|
if (voteUp) {
|
||||||
|
voteUp.addEventListener("click", handleVote("true"), { passive: false });
|
||||||
|
voteUp.addEventListener("touchend", handleVote("true"), { passive: false });
|
||||||
|
}
|
||||||
|
if (voteDown) {
|
||||||
|
voteDown.addEventListener("click", handleVote("false"), { passive: false });
|
||||||
|
voteDown.addEventListener("touchend", handleVote("false"), { passive: false });
|
||||||
|
}
|
||||||
|
// Fetch initial vote state
|
||||||
|
if (isAuthed()) {
|
||||||
|
fetchPostTruth(postID).then((stats) => {
|
||||||
|
if (stats?.user_vote || stats?.UserVote) {
|
||||||
|
currentVote = (stats.user_vote || stats.UserVote).toString();
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAuthUI();
|
applyAuthUI();
|
||||||
|
|
@ -2145,6 +2185,14 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
||||||
renderCompact();
|
renderCompact();
|
||||||
root.__renderCompact = renderCompact;
|
root.__renderCompact = renderCompact;
|
||||||
|
|
||||||
|
// Bring marker to front on hover so card is above other markers (compact cards = 200)
|
||||||
|
root.addEventListener("mouseenter", () => {
|
||||||
|
root.style.zIndex = "200";
|
||||||
|
});
|
||||||
|
root.addEventListener("mouseleave", () => {
|
||||||
|
root.style.zIndex = "1";
|
||||||
|
});
|
||||||
|
|
||||||
if (HEATMAP_ENABLED && isClusterMainPost(post)) {
|
if (HEATMAP_ENABLED && isClusterMainPost(post)) {
|
||||||
const handleEnter = () => {
|
const handleEnter = () => {
|
||||||
dispatchClusterHeatmap(root.__post || post, { mode: "hover", skipPan: true });
|
dispatchClusterHeatmap(root.__post || post, { mode: "hover", skipPan: true });
|
||||||
|
|
@ -2499,8 +2547,9 @@ export function createSimpleMarkerForPost(post, mapRef, markersRef, expandedElRe
|
||||||
const markerEl = root.querySelector(".sw-simple-marker");
|
const markerEl = root.querySelector(".sw-simple-marker");
|
||||||
const hoverCard = root.querySelector(".sw-simple-hover-card");
|
const hoverCard = root.querySelector(".sw-simple-hover-card");
|
||||||
|
|
||||||
// Hover effects
|
// Hover effects - bring marker to front so hover card is above other markers (simple pins = 150)
|
||||||
root.addEventListener("mouseenter", () => {
|
root.addEventListener("mouseenter", () => {
|
||||||
|
root.style.zIndex = "150";
|
||||||
if (isCluster) {
|
if (isCluster) {
|
||||||
dispatchClusterHeatmap(root.__post || post, { mode: "hover", skipPan: true });
|
dispatchClusterHeatmap(root.__post || post, { mode: "hover", skipPan: true });
|
||||||
}
|
}
|
||||||
|
|
@ -2515,6 +2564,7 @@ export function createSimpleMarkerForPost(post, mapRef, markersRef, expandedElRe
|
||||||
});
|
});
|
||||||
|
|
||||||
root.addEventListener("mouseleave", () => {
|
root.addEventListener("mouseleave", () => {
|
||||||
|
root.style.zIndex = isWeatherPost ? "10" : "2";
|
||||||
if (isCluster) {
|
if (isCluster) {
|
||||||
dispatchClusterHeatmapClear();
|
dispatchClusterHeatmapClear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue