Skip to content

Instantly share code, notes, and snippets.

@fsubal
Last active April 21, 2024 12:14
Show Gist options
  • Save fsubal/9188e111d0a557c8230f2401223677eb to your computer and use it in GitHub Desktop.
Save fsubal/9188e111d0a557c8230f2401223677eb to your computer and use it in GitHub Desktop.
<tora-viewer> Web Components
import toraViewer, { type Viewer } from '@toralab/tora-viewer';
function finiteInteger<R extends number | null | undefined>(value: string | null, defaultValue: R = 0): R {
if (!value) {
return defaultValue
}
const parsed = parseInt(value)
if (!Number.isFinite(parsed)) {
throw new RangeError(`${value} cannot be parsed to finite number`)
}
return parsed
}
/**
* @example
* ```html
* <tora-viewer title="漫画のタイトル" page-style="auto">
* <tora-viewer-page url="..." />
* <tora-viewer-page url="..." />
* <tora-viewer-page url="..." />
* <tora-viewer-page url="..." />
* <tora-viewer-lastpage>
* <button>高評価 +1</button>
* </tora-viewer-lastpage>
* </tora-viewer>
* ```
*/
export class ToraViewer extends HTMLElement {
static { customElements.define('tora-viewer', this) }
readonly pages = this.querySelectorAll<ToraViewerPage>('tora-viewer-page')
#viewer?: Viewer
#lastPageElement = this.querySelector('tora-viewer-lastpage')
get currentIndex() {
return this.#viewer?.currentIndex
}
get isLastPage() {
return this.#viewer?.isLastPage
}
get workTitle() {
return this.getAttribute('title') ?? undefined
}
get direction() {
return this.getAttribute('direction') ?? 'horizontal-rtl'
}
get pageStyle() {
return this.getAttribute('page-style')
}
get controlShowTime() {
return finiteInteger(this.getAttribute('control-show-time'))
}
get width() {
return finiteInteger(this.getAttribute('width'), undefined)
}
get height() {
return finiteInteger(this.getAttribute('height'), undefined)
}
get images() {
return Array.from(this.pages).map(page => page.toImage())
}
get embed() {
return this.hasAttribute('embed')
}
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
connectedCallback() {
this.#viewer = toraViewer(this.images, {
title: this.title,
pageSize: {
width: this.width,
height: this.height,
},
pageStyle: this.pageStyle,
direction: this.direction,
controlShowTime: this.controlShowTime,
lastPageElement: this.#lastPageElement,
insertTarget: this.embed ? this.shadowRoot! : undefined
})
}
disconnectedCallback() {
this.#viewer?.dispose()
}
goNext() {
this.#viewer?.goNext()
}
goPrev() {
this.#viewer?.goPrev()
}
goLeft() {
this.#viewer?.goLeft()
}
goRight() {
this.#viewer?.goRight()
}
goTo(next: number) {
this.#viewer?.goTo(next)
}
openSettings() {
this.#viewer?.openSettings()
}
closeSettings() {
this.#viewer?.closeSettings()
}
onCurrentIndexChanged(callback: (index: number) => void) {
this.#viewer?.onCurrentIndexChanged(callback)
}
}
export class ToraViewerPage extends HTMLElement {
static { customElements.define('tora-viewer-page', this) }
get url() {
const url = this.getAttribute('url')
if (!url) {
throw new Error('<tora-viewer-page url="..."> is required')
}
return url
}
get thumbnailUrl() {
return this.getAttribute('thumbnail-url')
}
toImage() {
const { url, thumbnailUrl } = this
if (thumbnailUrl) {
return { url, thumbnailUrl }
} else {
return url
}
}
}
export class ToraViewerLastPage extends HTMLElement {
static { customElements.define('tora-viewer-lastpage', this) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment