mirror of
https://github.com/john-okeefe/foliate-js.git
synced 2026-09-09 11:29:14 -04:00
View: make it possible to do things on load
Also refactor reader, and add left/right shortcuts
This commit is contained in:
@@ -105,22 +105,116 @@ const $ = document.querySelector.bind(document)
|
||||
const locales = 'en'
|
||||
const percentFormat = new Intl.NumberFormat(locales, { style: 'percent' })
|
||||
|
||||
let setCurrentHref
|
||||
class Reader {
|
||||
#tocView
|
||||
style = {
|
||||
spacing: 1.4,
|
||||
justify: true,
|
||||
hyphenate: true,
|
||||
}
|
||||
layout = {
|
||||
margin: 48,
|
||||
gap: 48,
|
||||
maxColumnWidth: 720,
|
||||
}
|
||||
closeSideBar() {
|
||||
$('#dimming-overlay').classList.remove('show')
|
||||
$('#side-bar').classList.remove('show')
|
||||
}
|
||||
constructor() {
|
||||
$('#side-bar-button').addEventListener('click', () => {
|
||||
$('#dimming-overlay').classList.add('show')
|
||||
$('#side-bar').classList.add('show')
|
||||
})
|
||||
$('#dimming-overlay').addEventListener('click', () => this.closeSideBar())
|
||||
|
||||
const emit = obj => {
|
||||
const menu = createMenu([
|
||||
{
|
||||
name: 'layout',
|
||||
label: 'Layout',
|
||||
type: 'radio',
|
||||
items: [
|
||||
['Paginated', 'paginated'],
|
||||
['Scrolled', 'scrolled'],
|
||||
],
|
||||
onclick: value => {
|
||||
this.layout.flow = value
|
||||
this.setAppearance()
|
||||
},
|
||||
}
|
||||
])
|
||||
menu.element.classList.add('menu')
|
||||
|
||||
$('#menu-button').append(menu.element)
|
||||
$('#menu-button > button').addEventListener('click', () =>
|
||||
menu.element.classList.toggle('show'))
|
||||
menu.groups.layout.select('paginated')
|
||||
}
|
||||
async open(file) {
|
||||
this.view = await getView(file, this.#handleEvent.bind(this))
|
||||
this.setAppearance()
|
||||
this.view.renderer.next()
|
||||
|
||||
$('#header-bar').style.visibility = 'visible'
|
||||
$('#nav-bar').style.visibility = 'visible'
|
||||
$('#left-button').addEventListener('click', () => this.view.goLeft())
|
||||
$('#right-button').addEventListener('click', () => this.view.goRight())
|
||||
|
||||
document.addEventListener('keydown', this.#handleKeydown.bind(this))
|
||||
|
||||
const { book } = this.view
|
||||
const title = book.metadata?.title ?? 'Untitled Book'
|
||||
document.title = title
|
||||
$('#side-bar-title').innerText = title
|
||||
const author = book.metadata?.author
|
||||
$('#side-bar-author').innerText = typeof author === 'string' ? author
|
||||
: author
|
||||
?.map(author => typeof author === 'string' ? author : author.name)
|
||||
?.join(', ')
|
||||
?? ''
|
||||
book.getCover?.()?.then(blob =>
|
||||
blob ? $('#side-bar-cover').src = URL.createObjectURL(blob) : null)
|
||||
|
||||
const toc = book.toc
|
||||
if (toc) {
|
||||
this.#tocView = createTOCView(toc, href => {
|
||||
this.view.goTo(href).catch(e => console.error(e))
|
||||
this.closeSideBar()
|
||||
})
|
||||
$('#toc-view').append(this.#tocView.element)
|
||||
}
|
||||
}
|
||||
setAppearance = () => {
|
||||
this.view?.setAppearance({ css: getCSS(this.style), layout: this.layout })
|
||||
const scrolled = this.layout.flow === 'scrolled'
|
||||
document.documentElement.classList.toggle('scrolled', scrolled)
|
||||
}
|
||||
#handleEvent(obj) {
|
||||
console.debug(obj)
|
||||
switch (obj.type) {
|
||||
case 'relocated': {
|
||||
case 'loaded': this.#onLoaded(obj); break
|
||||
case 'relocated': this.#onRelocated(obj); break
|
||||
case 'reference': this.#onReference(obj); break
|
||||
}
|
||||
}
|
||||
#handleKeydown(event) {
|
||||
const k = event.key
|
||||
if (k === 'ArrowLeft' || k === 'h') this.view.goLeft()
|
||||
else if(k === 'ArrowRight' || k === 'l') this.view.goRight()
|
||||
}
|
||||
#onLoaded({ doc }) {
|
||||
doc.addEventListener('keydown', this.#handleKeydown.bind(this))
|
||||
}
|
||||
#onRelocated(obj) {
|
||||
const { fraction, location, tocItem, pageItem } = obj
|
||||
const percent = percentFormat.format(fraction)
|
||||
const loc = pageItem
|
||||
? `Page ${pageItem.label}`
|
||||
: `Loc ${location.current}`
|
||||
$('#progress-label').innerText = `${percent} · ${loc}`
|
||||
if (tocItem?.href) setCurrentHref?.(tocItem.href)
|
||||
break
|
||||
if (tocItem?.href) this.#tocView.setCurrentHref?.(tocItem.href)
|
||||
}
|
||||
case 'reference': {
|
||||
#onReference(obj) {
|
||||
const { content, pos: { point, dir } } = obj
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.sandbox = 'allow-same-origin'
|
||||
@@ -145,90 +239,11 @@ const emit = obj => {
|
||||
document.body.append(arrow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const open = async file => {
|
||||
document.body.removeChild($('#drop-target'))
|
||||
const view = await getView(file, emit)
|
||||
const style = {
|
||||
spacing: 1.4,
|
||||
justify: true,
|
||||
hyphenate: true,
|
||||
}
|
||||
const layout = {
|
||||
margin: 48,
|
||||
gap: 48,
|
||||
maxColumnWidth: 720,
|
||||
}
|
||||
const setAppearance = () => {
|
||||
view.setAppearance({ css: getCSS(style), layout })
|
||||
const scrolled = layout.flow === 'scrolled'
|
||||
document.documentElement.classList.toggle('scrolled', scrolled)
|
||||
}
|
||||
setAppearance()
|
||||
view.renderer.next()
|
||||
|
||||
const { book } = view
|
||||
const title = book.metadata?.title ?? 'Untitled Book'
|
||||
document.title = title
|
||||
$('#side-bar-title').innerText = title
|
||||
const author = book.metadata?.author
|
||||
$('#side-bar-author').innerText = typeof author === 'string' ? author
|
||||
: author
|
||||
?.map(author => typeof author === 'string' ? author : author.name)
|
||||
?.join(', ')
|
||||
?? ''
|
||||
book.getCover?.()?.then(blob =>
|
||||
blob ? $('#side-bar-cover').src = URL.createObjectURL(blob) : null)
|
||||
|
||||
const closeSideBar = () => {
|
||||
$('#dimming-overlay').classList.remove('show')
|
||||
$('#side-bar').classList.remove('show')
|
||||
}
|
||||
|
||||
const toc = book.toc
|
||||
if (toc) {
|
||||
const onclick = href => {
|
||||
view.goTo(href).catch(e => console.error(e))
|
||||
closeSideBar()
|
||||
}
|
||||
const tocView = createTOCView(toc, onclick)
|
||||
setCurrentHref = tocView.setCurrentHref
|
||||
$('#toc-view').append(tocView.element)
|
||||
}
|
||||
|
||||
$('#header-bar').style.visibility = 'visible'
|
||||
$('#side-bar-button').addEventListener('click', () => {
|
||||
$('#dimming-overlay').classList.add('show')
|
||||
$('#side-bar').classList.add('show')
|
||||
})
|
||||
$('#dimming-overlay').addEventListener('click', closeSideBar)
|
||||
|
||||
$('#nav-bar').style.visibility = 'visible'
|
||||
$('#left-button').addEventListener('click', () => view.goLeft())
|
||||
$('#right-button').addEventListener('click', () => view.goRight())
|
||||
|
||||
const menu = createMenu([
|
||||
{
|
||||
name: 'layout',
|
||||
label: 'Layout',
|
||||
type: 'radio',
|
||||
items: [
|
||||
['Paginated', 'paginated'],
|
||||
['Scrolled', 'scrolled'],
|
||||
],
|
||||
onclick: value => {
|
||||
layout.flow = value
|
||||
setAppearance()
|
||||
},
|
||||
}
|
||||
])
|
||||
menu.element.classList.add('menu')
|
||||
|
||||
$('#menu-button').append(menu.element)
|
||||
$('#menu-button > button').addEventListener('click', () =>
|
||||
menu.element.classList.toggle('show'))
|
||||
menu.groups.layout.select('paginated')
|
||||
const reader = new Reader()
|
||||
await reader.open(file)
|
||||
}
|
||||
|
||||
const dragOverHandler = e => e.preventDefault()
|
||||
@@ -236,12 +251,12 @@ const dropHandler = e => {
|
||||
e.preventDefault()
|
||||
const file = Array.from(e.dataTransfer.items)
|
||||
.find(item => item.kind === 'file')?.getAsFile()
|
||||
if (file) open(file).catch(e => emit(e))
|
||||
if (file) open(file).catch(e => console.error(e))
|
||||
}
|
||||
const dropTarget = $('#drop-target')
|
||||
dropTarget.addEventListener('drop', dropHandler)
|
||||
dropTarget.addEventListener('dragover', dragOverHandler)
|
||||
|
||||
$('#file-input').addEventListener('change', e =>
|
||||
open(e.target.files[0]).catch(e => emit(e)))
|
||||
open(e.target.files[0]).catch(e => console.error(e)))
|
||||
$('#file-button').addEventListener('click', () => $('#file-input').click())
|
||||
|
||||
Reference in New Issue
Block a user