MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
No edit summary |
No edit summary |
||
| Line 347: | Line 347: | ||
* SV Definitions v1 — tooltips for .sv-def | * SV Definitions v1 — tooltips for .sv-def | ||
* - Reads: data-sv-def-tip, data-sv-def-link | * - Reads: data-sv-def-tip, data-sv-def-link | ||
* - | * - Only activates when data-sv-def-tip is non-empty | ||
* ========================================================================= */ | * ========================================================================== */ | ||
(function (mw) { | (function (mw) { | ||
"use strict"; | "use strict"; | ||
| Line 358: | Line 358: | ||
var activeEl = null; | var activeEl = null; | ||
var pinned = false; | var pinned = false; | ||
var lastPoint = null; // {x,y} | var lastPoint = null; // {x,y} | ||
var lastTouchTime = 0; | |||
function qTipText(el) { | function qTipText(el) { | ||
| Line 390: | Line 391: | ||
tipEl.style.lineHeight = "1.25"; | tipEl.style.lineHeight = "1.25"; | ||
tipEl.style.boxShadow = "0 8px 24px rgba(0,0,0,0.35)"; | tipEl.style.boxShadow = "0 8px 24px rgba(0,0,0,0.35)"; | ||
tipEl.style.pointerEvents = "none"; | tipEl.style.pointerEvents = "none"; | ||
tipInner = document.createElement("div"); | tipInner = document.createElement("div"); | ||
| Line 421: | Line 422: | ||
} | } | ||
// Cursor/finger anchored positioning; prefers ABOVE the point. | |||
function positionTipPoint(clientX, clientY) { | function positionTipPoint(clientX, clientY) { | ||
if (!tipEl) return; | if (!tipEl) return; | ||
| Line 431: | Line 433: | ||
var tr = tipEl.getBoundingClientRect(); | var tr = tipEl.getBoundingClientRect(); | ||
var pad = 8; | var pad = 8; | ||
var gap = | var gap = pinned ? 14 : 18; // larger gap on hover so cursor doesn't cover text | ||
var x = clientX - | var x = clientX - tr.width / 2; | ||
x = clamp(x, pad, window.innerWidth - tr.width - pad); | x = clamp(x, pad, window.innerWidth - tr.width - pad); | ||
var y = clientY - tr.height - gap; | var y = clientY - tr.height - gap; // ABOVE by default | ||
if (y < pad) y = clientY + gap; | if (y < pad) y = clientY + gap; // flip below if needed | ||
y = clamp(y, pad, window.innerHeight - tr.height - pad); | y = clamp(y, pad, window.innerHeight - tr.height - pad); | ||
| Line 450: | Line 452: | ||
var txt = qTipText(el); | var txt = qTipText(el); | ||
if (!txt) return; | if (!txt) return false; | ||
activeEl = el; | activeEl = el; | ||
| Line 458: | Line 460: | ||
suppressTitle(el); | suppressTitle(el); | ||
return true; | |||
} | } | ||
| Line 482: | Line 485: | ||
} | } | ||
// | // Desktop hover | ||
document.addEventListener( | document.addEventListener("mouseover", function (e) { | ||
if (pinned) return; | |||
var el = closestDef(e.target); | |||
if (!el) { | |||
if (activeEl) hide(); | |||
return; | |||
} | |||
if (isEntering(el, e.relatedTarget)) return; | |||
if (activeEl === el) return; | |||
// Blank definition disables rollover AND clears any previous tooltip | |||
if (activeEl | if (!qTipText(el)) { | ||
if (activeEl) hide(); | |||
return; | |||
} | |||
if (show(el)) positionTipPoint(e.clientX, e.clientY); | |||
}, true); | |||
document.addEventListener("mousemove", function (e) { | |||
if (pinned) return; | |||
var el = closestDef(e.target); | |||
if (!el) { | |||
if (activeEl) hide(); | |||
if ( | return; | ||
} | |||
if (activeEl && el !== activeEl) return; | |||
if (activeEl | if (!qTipText(el)) { | ||
if (activeEl) hide(); | |||
return; | |||
} | |||
if (activeEl) positionTipPoint(e.clientX, e.clientY); | |||
}, true); | |||
document.addEventListener("mouseout", function (e) { | |||
if (pinned) return; | |||
}, | var el = closestDef(e.target); | ||
if (!el) return; | |||
if (isEntering(el, e.relatedTarget)) return; | |||
if (activeEl === el) hide(); | |||
}, true); | |||
document.addEventListener( | // Touch pin (positions above finger) | ||
document.addEventListener("pointerdown", function (e) { | |||
var el = closestDef(e.target); | |||
if (!el) return; | |||
if (e.pointerType !== "touch") return; | |||
// Link = allow navigation | |||
if (qLink(el)) return; | |||
// Blank tip = do nothing (and close any pinned tooltip) | |||
if (!qTipText(el)) { | |||
if (pinned) hide(); | |||
return; | |||
} | |||
lastTouchTime = Date.now(); | |||
pinned = true; | |||
if (show(el)) positionTipPoint(e.clientX, e.clientY); | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
}, true); | |||
// Click behavior: | // Click behavior: | ||
// - | // - link -> navigate | ||
// - | // - tip -> toggle pinned | ||
// - | // - blank tip -> close pinned | ||
document.addEventListener( | document.addEventListener("click", function (e) { | ||
// Ignore the synthetic click right after a touch pin | |||
if (Date.now() - lastTouchTime < 500) return; | |||
var el = closestDef(e.target); | |||
if (!el) { | |||
if ( | if (pinned) hide(); | ||
return; | |||
} | |||
var link = qLink(el); | |||
if (link) { | |||
window.location.href = mw.util.getUrl(link); | |||
return; | |||
} | |||
if (!qTipText(el)) { | |||
if (pinned) hide(); | |||
return; | |||
} | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
if (pinned && activeEl === el) { | |||
hide(); | |||
return; | |||
} | } | ||
pinned = true; | |||
if (show(el)) positionTipPoint(e.clientX, e.clientY); | |||
}, true); | |||
document.addEventListener("keydown", function (e) { | |||
if (e.key === "Escape" && (pinned || activeEl)) hide(); | |||
}, true); | |||
window.addEventListener( | window.addEventListener("resize", function () { | ||
" | if (tipEl && tipEl.style.display === "block" && lastPoint) { | ||
function () { | positionTipPoint(lastPoint.x, lastPoint.y); | ||
} | |||
}, true); | |||
window.addEventListener("scroll", function () { | |||
if (tipEl && tipEl.style.display === "block" && lastPoint) { | |||
positionTipPoint(lastPoint.x, lastPoint.y); | |||
} | |||
}, true); | |||
})(window.mw); | })(window.mw); | ||