1
0
mirror of https://github.com/osmarks/website synced 2025-04-29 14:13:21 +00:00

inline footnotes prototype

This commit is contained in:
osmarks 2025-03-30 23:02:21 +01:00
parent a9c4072283
commit ebcee20c5b
2 changed files with 76 additions and 31 deletions

View File

@ -413,6 +413,37 @@ const inCollapsedDetails = el => {
const footnotes = document.querySelector(".footnotes") const footnotes = document.querySelector(".footnotes")
const sidenotes = document.querySelector(".sidenotes") const sidenotes = document.querySelector(".sidenotes")
var sidenotesAtSide = () => false
let previousHighlight
const fixDetailsSummary = elt => {
if (!(elt instanceof HTMLElement) && !window.location.hash.slice(1)) return
const el = elt instanceof HTMLElement ? elt : document.getElementById(window.location.hash.slice(1))
var parent = el
if (!el) return
if (el.classList.contains("footnote-item")) {
if (previousHighlight) {
previousHighlight.classList.remove("hl1")
}
el.classList.add("hl1")
previousHighlight = el
}
if (el.classList.contains("footnote-backref")) {
// close footnote
if (previousHighlight) {
previousHighlight.classList.remove("hl1")
previousHighlight.classList.remove("hl3")
}
}
while (parent.parentElement) {
if (parent.nodeName === "DETAILS") {
parent.setAttribute("open", true)
}
parent = parent.parentElement
}
if (sidenotesAtSide()) el.scrollIntoView()
}
if (sidenotes && footnotes) { if (sidenotes && footnotes) {
const codeblocks = Array.from(document.querySelectorAll(".wider")).concat(Array.from(document.querySelectorAll(".content pre"))) const codeblocks = Array.from(document.querySelectorAll(".wider")).concat(Array.from(document.querySelectorAll(".content pre")))
const article = document.querySelector(".content") const article = document.querySelector(".content")
@ -424,7 +455,7 @@ if (sidenotes && footnotes) {
const sum = xs => xs.reduce((a, b) => a + b, 0) const sum = xs => xs.reduce((a, b) => a + b, 0)
const arrayOf = (n, x) => new Array(n).fill(x) const arrayOf = (n, x) => new Array(n).fill(x)
const BORDER = 16 const BORDER = 16
const sidenotesAtSide = () => getComputedStyle(sidenotes).paddingLeft !== "0px" sidenotesAtSide = () => getComputedStyle(sidenotes).paddingLeft !== "0px"
let rendered = false let rendered = false
const relayout = forceRedraw => { const relayout = forceRedraw => {
// sidenote column width is static: no need to redo positioning on resize unless no positions applied // sidenote column width is static: no need to redo positioning on resize unless no positions applied
@ -447,13 +478,14 @@ if (sidenotes && footnotes) {
inclusions.push({ start: end - snRect.top, contents: [] }) inclusions.push({ start: end - snRect.top, contents: [] })
} }
inclusions[inclusions.length - 1].end = Infinity inclusions[inclusions.length - 1].end = Infinity
console.log(inclusions, exclusions)
const notes = [] const notes = []
// read off sidenotes to place // read off sidenotes to place
for (const item of footnoteItems) { for (const item of footnoteItems) {
const link = article.querySelector(`#${item.id.replace(/^fn/, "fnref")}`) const link = article.querySelector(`#${item.id.replace(/^fn/, "fnref")}`)
const linkRect = link.getBoundingClientRect() const linkRect = link.getBoundingClientRect()
item.style.position = "absolute" item.style.position = "absolute"
item.style.display = "list-item"
item.style.width = null
item.style.left = getComputedStyle(sidenotes).paddingLeft item.style.left = getComputedStyle(sidenotes).paddingLeft
item.style.paddingBottom = item.style.paddingTop = item.style.paddingRight = `${BORDER / 2}px` item.style.paddingBottom = item.style.paddingTop = item.style.paddingRight = `${BORDER / 2}px`
const itemRect = item.getBoundingClientRect() const itemRect = item.getBoundingClientRect()
@ -470,7 +502,6 @@ if (sidenotes && footnotes) {
.findIndex(inc => (sum(inc.contents.map(x => x.height)) + note.height) < (inc.end - inc.start)) .findIndex(inc => (sum(inc.contents.map(x => x.height)) + note.height) < (inc.end - inc.start))
inclusions[index + next].contents.push(note) inclusions[index + next].contents.push(note)
} }
console.log(inclusions)
// TODO: try simple moves between regions? might be useful sometimes // TODO: try simple moves between regions? might be useful sometimes
// place within region and apply styles // place within region and apply styles
for (const inc of inclusions) { for (const inc of inclusions) {
@ -525,7 +556,7 @@ if (sidenotes && footnotes) {
} }
const solutionVars = new Map(solution.variables) const solutionVars = new Map(solution.variables)
let position = 0 let position = 0
console.log(solutionVars, model) console.log("Layout solution found")
regionNotes.forEach((note, i) => { regionNotes.forEach((note, i) => {
position += solutionVars.get(`gap_${i}`) || 0 position += solutionVars.get(`gap_${i}`) || 0
note.item.style.top = position + "px" note.item.style.top = position + "px"
@ -535,8 +566,17 @@ if (sidenotes && footnotes) {
} }
rendered = true rendered = true
} else { } else {
const articleRect = article.getBoundingClientRect()
const pad = parseFloat(getComputedStyle(sidenotes).paddingLeft)
for (const item of sidenotes.querySelectorAll(".footnote-item")) { for (const item of sidenotes.querySelectorAll(".footnote-item")) {
item.style.position = "static" //item.style.position = "static"
const link = article.querySelector(`#${item.id.replace(/^fn/, "fnref")}`)
const linkRect = link.getBoundingClientRect()
item.style.position = "absolute"
item.style.left = (articleRect.left + pad) + "px"
item.style.width = (articleRect.right - articleRect.left - pad * 2) + "px"
item.style.top = linkRect.bottom + "px"
item.style.display = "none"
} }
rendered = false rendered = false
} }
@ -549,6 +589,12 @@ if (sidenotes && footnotes) {
link.addEventListener("mouseleave", () => { link.addEventListener("mouseleave", () => {
item.classList.remove("hl2") item.classList.remove("hl2")
}) })
item.addEventListener("mouseover", () => {
if (!sidenotesAtSide()) item.classList.add("hl3")
})
item.addEventListener("mouseleave", () => {
item.classList.remove("hl3")
})
} }
} }
@ -565,28 +611,18 @@ if (sidenotes && footnotes) {
setTimeout(() => relayout(true), 0) setTimeout(() => relayout(true), 0)
}) })
}) })
} document.querySelectorAll("a").forEach(x => {
const isBackref = x.classList.contains("footnote-backref")
let previousHighlight const isRef = x.parentElement.classList.contains("footnote-ref")
const fixDetailsSummary = () => { if (!isBackref && !isRef) return
if (!window.location.hash.slice(1)) return x.addEventListener("click", ev => {
const el = document.getElementById(window.location.hash.slice(1)) if (!sidenotesAtSide()) {
var parent = el ev.preventDefault()
if (!el) return if (isRef) fixDetailsSummary(document.getElementById(x.getAttribute("href").slice(1)))
if (el.classList.contains("footnote-item")) { else fixDetailsSummary(x)
if (previousHighlight) {
previousHighlight.classList.remove("hl1")
} }
el.classList.add("hl1") })
previousHighlight = el })
}
while (parent.parentElement) {
if (parent.nodeName === "DETAILS") {
parent.setAttribute("open", true)
}
parent = parent.parentElement
}
el.scrollIntoView()
} }
window.addEventListener("hashchange", fixDetailsSummary) window.addEventListener("hashchange", fixDetailsSummary)

View File

@ -102,7 +102,7 @@ h1, h2, h3, h4, h5, h6
font-family: $headers font-family: $headers
text-align: left text-align: left
a a
color: inherit color: inherit !important
text-decoration: none !important text-decoration: none !important
summary h1, summary h2 summary h1, summary h2
@ -222,9 +222,12 @@ blockquote
min-width: $sidenotes-width min-width: $sidenotes-width
padding-left: 1.5rem padding-left: 1.5rem
position: relative position: relative
box-sizing: border-box
p p
margin: 0 margin: 0
.footnotes-list
margin: 0
.footnotes-sep .footnotes-sep
display: none display: none
.footnotes-list .footnotes-list
@ -236,16 +239,22 @@ blockquote
width: auto width: auto
max-width: $content-width max-width: $content-width
padding: 0 padding: 0
position: static
margin-left: $content-margin margin-left: $content-margin
margin-right: $content-margin margin-right: $content-margin
.footnotes-sep
display: block
.sidenotes-container .sidenotes-container
display: block display: block
table table
display: block display: block
overflow-x: scroll overflow-x: scroll
.footnote-item.hl1, .footnote-item.hl2, .footnote-item.hl3
background: hsl(0, 0%, var(--autocol-lightness))
padding: 0.5em
box-sizing: border-box
list-style-type: none
display: block !important
@media (min-width: calc(4 * $content-margin + $content-width + $sidenotes-width + $navbar-width)) @media (min-width: calc(4 * $content-margin + $content-width + $sidenotes-width + $navbar-width))
// fullwidth 3-pane layout // fullwidth 3-pane layout
body body
@ -278,7 +287,7 @@ blockquote
min-width: calc(40rem - 2 * $content-margin - $navbar-width) min-width: calc(40rem - 2 * $content-margin - $navbar-width)
$hl-border: 3px $hl-border: 3px
.footnote-item.hl1, .footnote-item.hl2 .footnote-item.hl1, .footnote-item.hl2, .footnote-item.hl3
border-top: solid $hl-border orange border-top: solid $hl-border orange
border-bottom: solid $hl-border orange border-bottom: solid $hl-border orange
margin-top: -$hl-border margin-top: -$hl-border