Add header and footer

Also allow getting next location, which is useful for the footer
This commit is contained in:
John Factotum
2023-03-09 16:20:28 +08:00
parent 2c1919c099
commit 857a94daf3
3 changed files with 72 additions and 7 deletions
+66 -3
View File
@@ -128,6 +128,24 @@ const getBackground = doc => {
: bodyStyle.background : 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 { class View {
#element = document.createElement('div') #element = document.createElement('div')
#iframe = document.createElement('iframe') #iframe = document.createElement('iframe')
@@ -320,6 +338,8 @@ class View {
export class Paginator { export class Paginator {
#element = document.createElement('div') #element = document.createElement('div')
#container = document.createElement('div') #container = document.createElement('div')
#header = document.createElement('div')
#footer = document.createElement('div')
#view #view
#vertical = false #vertical = false
#rtl = false #rtl = false
@@ -348,6 +368,18 @@ export class Paginator {
width: '100%', width: '100%',
height: '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) new ResizeObserver(() => this.render()).observe(this.#element)
this.#container.addEventListener('scroll', debounce(() => { this.#container.addEventListener('scroll', debounce(() => {
if (this.scrolled) this.#afterScroll('scroll') if (this.scrolled) this.#afterScroll('scroll')
@@ -371,14 +403,22 @@ export class Paginator {
this.#element.style.background = background this.#element.style.background = background
const { flow, margin, gap, maxColumnWidth } = this.layout const { flow, margin, gap, maxColumnWidth } = this.layout
if (flow === 'scrolled') { if (flow === 'scrolled') {
// FIXME: vertical-rl only, not -lr // FIXME: vertical-rl only, not -lr
this.#element.setAttribute('dir', vertical ? 'rtl' : 'ltr') this.#element.setAttribute('dir', vertical ? 'rtl' : 'ltr')
this.#element.style.padding = '0' this.#element.style.padding = '0'
this.#container.style.overflow ='scroll' this.#container.style.overflow ='scroll'
const columnWidth = this.layout.maxColumnWidth const columnWidth = this.layout.maxColumnWidth
this.heads = null
this.feet = null
this.#header.replaceChildren()
this.#footer.replaceChildren()
return { flow, margin, gap, columnWidth } return { flow, margin, gap, columnWidth }
} }
const { width, height } = this.#container.getBoundingClientRect() const { width, height } = this.#container.getBoundingClientRect()
const size = vertical ? height : width const size = vertical ? height : width
const divisor = Math.ceil(size / maxColumnWidth) const divisor = Math.ceil(size / maxColumnWidth)
@@ -388,6 +428,25 @@ export class Paginator {
const paddingV = `${vertical ? margin - gap / 2 : margin}px` const paddingV = `${vertical ? margin - gap / 2 : margin}px`
this.#element.style.padding = `${paddingV} ${paddingH}` this.#element.style.padding = `${paddingV} ${paddingH}`
this.#container.style.overflow ='hidden' 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 } return { height, width, margin, gap, columnWidth }
} }
render() { render() {
@@ -524,11 +583,15 @@ export class Paginator {
const range = this.#getVisibleRange() const range = this.#getVisibleRange()
// don't set new anchor if relocation was to scroll to anchor // don't set new anchor if relocation was to scroll to anchor
if (reason !== 'anchor') this.#anchor = range if (reason !== 'anchor') this.#anchor = range
const index = this.#index const index = this.#index
if (this.scrolled) if (this.scrolled)
this.onRelocated?.(range, index, this.end / this.viewSize) this.onRelocated?.(range, index, this.start / this.viewSize)
else if (this.pages > 0) else if (this.pages > 0) {
this.onRelocated?.(range, index, (this.page + 1) / this.pages) const { page, pages } = this
this.#header.style.visibility = page > 0 ? 'visible' : 'hidden'
this.onRelocated?.(range, index, page / pages, 1 / pages)
}
} }
async #display(promise) { async #display(promise) {
const { index, src, anchor, onLoad, select } = await promise const { index, src, anchor, onLoad, select } = await promise
+4 -2
View File
@@ -62,21 +62,23 @@ export class SectionProgress {
this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0) this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0)
} }
// get progress given index of and fractions within a section // 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 { sizes, sizePerLoc, sizePerTimeUnit, sizeTotal } = this
const sizeInSection = sizes[index] ?? 0 const sizeInSection = sizes[index] ?? 0
const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0) const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0)
const size = sizeBefore + fractionInSection * sizeInSection const size = sizeBefore + fractionInSection * sizeInSection
const nextSize = size + pageFraction * sizeInSection
const remainingTotal = sizeTotal - size const remainingTotal = sizeTotal - size
const remainingSection = (1 - fractionInSection) * sizeInSection const remainingSection = (1 - fractionInSection) * sizeInSection
return { return {
fraction: size / sizeTotal, fraction: nextSize / sizeTotal,
section: { section: {
current: index, current: index,
total: sizes.length, total: sizes.length,
}, },
location: { location: {
current: Math.floor(size / sizePerLoc), current: Math.floor(size / sizePerLoc),
next: Math.floor(nextSize / sizePerLoc),
total: Math.ceil(sizeTotal / sizePerLoc), total: Math.ceil(sizeTotal / sizePerLoc),
}, },
time: { time: {
+2 -2
View File
@@ -162,9 +162,9 @@ export class View {
await this.annotations.add(annotation, true) await this.annotations.add(annotation, true)
} }
} }
#onRelocated(range, index, fraction) { #onRelocated(range, index, fraction, size) {
if (!this.#sectionProgress) return 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 tocItem = this.#tocProgress.getProgress(index, range)
const pageItem = this.#pageProgress.getProgress(index, range) const pageItem = this.#pageProgress.getProgress(index, range)
const cfi = this.getCFI(index, range) const cfi = this.getCFI(index, range)