From 857a94daf373a83cf3f20078af4ea5ae973350a4 Mon Sep 17 00:00:00 2001 From: John Factotum <50942278+johnfactotum@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:20:28 +0800 Subject: [PATCH] Add header and footer Also allow getting next location, which is useful for the footer --- paginator.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- progress.js | 6 +++-- view.js | 4 +-- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/paginator.js b/paginator.js index e9c0063..44f2b41 100644 --- a/paginator.js +++ b/paginator.js @@ -128,6 +128,24 @@ const getBackground = doc => { : bodyStyle.background } +const makeMarginals = length => Array.from({ length }, () => { + const div = document.createElement('div') + const child = document.createElement('div') + div.append(child) + Object.assign(div.style, { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + minWidth: '0', + }) + Object.assign(child.style, { + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + }) + return div +}) + class View { #element = document.createElement('div') #iframe = document.createElement('iframe') @@ -320,6 +338,8 @@ class View { export class Paginator { #element = document.createElement('div') #container = document.createElement('div') + #header = document.createElement('div') + #footer = document.createElement('div') #view #vertical = false #rtl = false @@ -348,6 +368,18 @@ export class Paginator { width: '100%', height: '100%', }) + + const marginalStyle = { + position: 'absolute', left: '0', right: '0', + display: 'grid', + fontFamily: 'system-ui', + opacity: '.5', + } + Object.assign(this.#header.style, marginalStyle, { top: '0' }) + Object.assign(this.#footer.style, marginalStyle, { bottom: '0' }) + this.#element.append(this.#header) + this.#element.append(this.#footer) + new ResizeObserver(() => this.render()).observe(this.#element) this.#container.addEventListener('scroll', debounce(() => { if (this.scrolled) this.#afterScroll('scroll') @@ -371,14 +403,22 @@ export class Paginator { this.#element.style.background = background const { flow, margin, gap, maxColumnWidth } = this.layout + if (flow === 'scrolled') { // FIXME: vertical-rl only, not -lr this.#element.setAttribute('dir', vertical ? 'rtl' : 'ltr') this.#element.style.padding = '0' this.#container.style.overflow ='scroll' const columnWidth = this.layout.maxColumnWidth + + this.heads = null + this.feet = null + this.#header.replaceChildren() + this.#footer.replaceChildren() + return { flow, margin, gap, columnWidth } } + const { width, height } = this.#container.getBoundingClientRect() const size = vertical ? height : width const divisor = Math.ceil(size / maxColumnWidth) @@ -388,6 +428,25 @@ export class Paginator { const paddingV = `${vertical ? margin - gap / 2 : margin}px` this.#element.style.padding = `${paddingV} ${paddingH}` this.#container.style.overflow ='hidden' + + const marginalHeight = `${margin * .85}px` + const marginalStyle = { + height: marginalHeight, + gridTemplateColumns: `repeat(${divisor}, 1fr)`, + gap: `${gap}px`, + padding: `0 ${gap}px`, + fontSize: `min(.75em, ${marginalHeight})`, + lineHeight: marginalHeight, + } + Object.assign(this.#header.style, marginalStyle) + Object.assign(this.#footer.style, marginalStyle) + const heads = makeMarginals(divisor) + const feet = makeMarginals(divisor) + this.heads = heads.map(el => el.children[0]) + this.feet = feet.map(el => el.children[0]) + this.#header.replaceChildren(...heads) + this.#footer.replaceChildren(...feet) + return { height, width, margin, gap, columnWidth } } render() { @@ -524,11 +583,15 @@ export class Paginator { const range = this.#getVisibleRange() // don't set new anchor if relocation was to scroll to anchor if (reason !== 'anchor') this.#anchor = range + const index = this.#index if (this.scrolled) - this.onRelocated?.(range, index, this.end / this.viewSize) - else if (this.pages > 0) - this.onRelocated?.(range, index, (this.page + 1) / this.pages) + this.onRelocated?.(range, index, this.start / this.viewSize) + else if (this.pages > 0) { + const { page, pages } = this + this.#header.style.visibility = page > 0 ? 'visible' : 'hidden' + this.onRelocated?.(range, index, page / pages, 1 / pages) + } } async #display(promise) { const { index, src, anchor, onLoad, select } = await promise diff --git a/progress.js b/progress.js index c1e07c7..2982204 100644 --- a/progress.js +++ b/progress.js @@ -62,21 +62,23 @@ export class SectionProgress { this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0) } // get progress given index of and fractions within a section - getProgress(index, fractionInSection) { + getProgress(index, fractionInSection, pageFraction = 0) { const { sizes, sizePerLoc, sizePerTimeUnit, sizeTotal } = this const sizeInSection = sizes[index] ?? 0 const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0) const size = sizeBefore + fractionInSection * sizeInSection + const nextSize = size + pageFraction * sizeInSection const remainingTotal = sizeTotal - size const remainingSection = (1 - fractionInSection) * sizeInSection return { - fraction: size / sizeTotal, + fraction: nextSize / sizeTotal, section: { current: index, total: sizes.length, }, location: { current: Math.floor(size / sizePerLoc), + next: Math.floor(nextSize / sizePerLoc), total: Math.ceil(sizeTotal / sizePerLoc), }, time: { diff --git a/view.js b/view.js index 8748ce4..acefedb 100644 --- a/view.js +++ b/view.js @@ -162,9 +162,9 @@ export class View { await this.annotations.add(annotation, true) } } - #onRelocated(range, index, fraction) { + #onRelocated(range, index, fraction, size) { if (!this.#sectionProgress) return - const progress = this.#sectionProgress.getProgress(index, fraction) + const progress = this.#sectionProgress.getProgress(index, fraction, size) const tocItem = this.#tocProgress.getProgress(index, range) const pageItem = this.#pageProgress.getProgress(index, range) const cfi = this.getCFI(index, range)