Skip to content

Commit

Permalink
Initial working version of ref hints
Browse files Browse the repository at this point in the history
  • Loading branch information
dlaliberte committed Nov 8, 2023
1 parent f418d95 commit d8fbaa7
Show file tree
Hide file tree
Showing 5 changed files with 2,660 additions and 32 deletions.
3 changes: 0 additions & 3 deletions bikeshed/dfnpanels/dfnpanels.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"use strict";
{
const dfnsJson = window.dfnsJson || {};

// Functions, divided by link type, that wrap an autolink's
// contents with the appropriate outer syntax.
// Alternately, a string naming another type they format
Expand Down Expand Up @@ -249,7 +247,6 @@
for (const el of tabbable) {
el.tabIndex = tabIndex++;
}

}

function positionDfnPanel(dfnPanel) {
Expand Down
24 changes: 24 additions & 0 deletions bikeshed/refs/refhints.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.ref-hint {
display: none;
position: absolute;
z-index: 35;
width: 20em;
width: 300px;
height: auto;
max-height: 500px;
overflow: auto;
padding: 0.5em 0.75em;
font: small Helvetica Neue, sans-serif, Droid Sans Fallback;
background: var(--dfnpanel-bg);
color: var(--dfnpanel-text);
border: outset 0.2em;
white-space: normal; /* in case it's moved into a pre */
}

.ref-hint.on {
display: inline-block;
}

.ref-hint * { margin: 0; padding: 0; text-indent: 0; }

.ref-hint ul { padding: 0 0 0 1em; list-style: none; }
145 changes: 145 additions & 0 deletions bikeshed/refs/refhints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use strict";
{
function genRefHint(link, ref) {
const forList = ref.for_;
const forListElements = forList.length === 0 ? '' : mk.li({},
mk.b({}, "For: "),
mk.ul({},
...forList.map(forItem =>
mk.li({},
mk.span({}, `${forItem}`)
),
),
),
);
const url = link.href;
const safeUrl = encodeURIComponent(url);
const hintPanel = mk.aside({
class: "ref-hint",
id: `ref-hint-for-${safeUrl}`,
"data-for": url,
"aria-labelled-by": `ref-hint-for-${safeUrl}`,
},
mk.ul({},
mk.li({},
mk.b({}, "URL: "),
mk.a({ href: url, class: "ref" }, url),
),
mk.li({},
mk.b({}, "Type: "),
mk.span({}, `${ref.type}`),
),
mk.li({},
mk.b({}, "Spec: "),
mk.span({}, `${ref.spec ? ref.spec : ''}`),
),
forListElements
),
);
hintPanel.forLink = link;
return hintPanel;
}
function genAllRefHints() {
for(const refData of Object.values(window.refsData)) {
const refUrl = refData.url;
const link = document.querySelector(`a[href="${refUrl}"]`);
if(!link) {
console.log(`Can't find link href="${refUrl}".`, refData);
continue;
}
const hint = genRefHint(link, refData);
append(document.body, hint);
insertLinkPopupAction(hint)
}
}

function hideAllRefHints() {
queryAll(".ref-hint.on").forEach(el=>hideRefHint(el));
}

function hideRefHint(refHint) {
const link = refHint.forLink;
link.setAttribute("aria-expanded", "false");
refHint.style.position = "absolute"; // unfix it
refHint.classList.remove("on");
}

document.addEventListener("DOMContentLoaded", ()=>{
genAllRefHints();

document.body.addEventListener("click", (e) => {
// If not handled already, just hide all link panels.
hideAllRefHints();
});
})

function showRefHint(refHint) {
hideAllRefHints(); // Only display one at this time.

const link = refHint.forLink;
link.setAttribute("aria-expanded", "true");
refHint.classList.add("on");
positionRefHint(refHint);
}

function positionRefHint(refHint) {
const link = refHint.forLink;
const linkPos = getRootLevelAbsolutePosition(link);
refHint.style.top = linkPos.bottom + "px";
refHint.style.left = linkPos.left + "px";

const panelPos = refHint.getBoundingClientRect();
const panelMargin = 8;
const maxRight = document.body.parentNode.clientWidth - panelMargin;
if (panelPos.right > maxRight) {
const overflowAmount = panelPos.right - maxRight;
const newLeft = Math.max(panelMargin, linkPos.left - overflowAmount);
refHint.style.left = newLeft + "px";
}
}

// TODO: shared util
// Returns the root-level absolute position {left and top} of element.
function getRootLevelAbsolutePosition(el) {
const boundsRect = el.getBoundingClientRect();
let xPos = 0;
let yPos = 0;

while (el) {
let xScroll = el.scrollLeft;
let yScroll = el.scrollTop;

// Ignore scrolling of body.
if (el.tagName === "BODY") {
xScroll = 0;
yScroll = 0;
}
xPos += (el.offsetLeft - xScroll + el.clientLeft);
yPos += (el.offsetTop - yScroll + el.clientTop);

el = el.offsetParent;
}
return {
left: xPos,
top: yPos,
right: xPos + boundsRect.width,
bottom: yPos + boundsRect.height,
};
}

function insertLinkPopupAction(refHint) {
const link = refHint.forLink;
// link.setAttribute('role', 'button');
link.setAttribute('aria-expanded', 'false')
link.classList.add('has-ref-hint');
link.addEventListener('mouseover', (event) => {
showRefHint(refHint);
event.stopPropagation();
});
}

window.addEventListener("resize", () => {
// Hide any open ref hint.
hideAllRefHints();
})
}
17 changes: 17 additions & 0 deletions bikeshed/unsortedJunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,8 @@ def verifyUsageOfAllLocalBiblios(doc: t.SpecT) -> None:


def processAutolinks(doc: t.SpecT) -> None:
scriptLines = []
atLeastOneRef = False
# An <a> without an href is an autolink.
# <i> is a legacy syntax for term autolinks. If it links up, we change it into an <a>.
# We exclude bibliographical links, as those are processed in `processBiblioLinks`.
Expand Down Expand Up @@ -721,6 +723,7 @@ def processAutolinks(doc: t.SpecT) -> None:
okayToFail = el.get("data-okay-to-fail") is not None
ignorable = linkText.lower() in doc.md.ignoredTerms

atLeastOneRef = True
ref = doc.refs.getRef(
linkType,
linkText,
Expand Down Expand Up @@ -762,13 +765,27 @@ def processAutolinks(doc: t.SpecT) -> None:
h.clearContents(el)
el.text = replacementText
decorateAutolink(doc, el, linkType=linkType, linkText=linkText, ref=ref)

refJson = ref.__json__()
scriptLines.append(
f"window.refsData['{ref.url}'] = {json.dumps(refJson)};\n")
else:
if linkType == "maybe":
el.tag = "css"
if el.get("data-link-type"):
del el.attrib["data-link-type"]
if el.get("data-lt"):
del el.attrib["data-lt"]

if len(scriptLines) > 0:
jsonBlock = doc.extraScripts.setDefault(
"ref-hints-json", "window.refsData = {};\n")
jsonBlock.text += "\n".join(scriptLines)
if atLeastOneRef:
doc.extraScripts.setFile("ref-hints", "refs/refhints.js")
doc.extraStyles.setFile("ref-hints", "refs/refhints.css")
h.addDOMHelperScript(doc)

h.dedupIDs(doc)


Expand Down
Loading

0 comments on commit d8fbaa7

Please sign in to comment.