UNPKG

@snippetify/book-reader-component

Version:
784 lines (783 loc) 20.9 kB
import '@fortawesome/fontawesome-free/js/all.min'; import { Component, Host, h, Listen, State, Prop, Method, Event, Fragment } from '@stencil/core'; import { SelectionManager } from '../../managers/selection-manager'; import { Page } from '../../models/page'; import defaultConfig from '../../config/config'; import { DecoratorManager } from '../../managers/decorator-manager'; import { Decorator } from '../../models/decorator'; import { ReadStyle } from '../../models/read-style'; import { BookReaderEvent, EventName, ScrollDirectionAction } from '../../events/events'; export class BookReader { constructor() { this.isMenuOpened = false; this.pages = []; this.decorators = []; this.lastScrollTop = 0; this.config = defaultConfig; this.decoratorManager = DecoratorManager.getInstance(); this.selectionManager = SelectionManager.getInstance(); this.readStyle = new ReadStyle(defaultConfig.style.reading); } async getConfig() { return this.config; } async setConfig(config) { this.config = config; } async setPages(pages) { this.pages = pages.map(v => new Page(v)); this.printed = false; } async setAllPages(pages, pages2) { this.pages = pages.map(v => new Page(v)); this.pages2 = pages2.map(v => new Page(v)); this.printed = false; } async setDecorators(decorators) { this.decorators = decorators.map(v => new Decorator(v)); this.decoratorManager.print(this.decorators, this.container); } async getReadStyle() { return this.readStyle; } async setReadStyle(style) { this.readStyle = style; this.applyStyle(); } async goToPage(no, shift = 0) { const top = this.container.querySelector(`[data-no="${no}"]`).offsetTop + shift; (document.documentElement || document.body).scrollTo({ top: top, behavior: 'smooth' }); } async goToParagraph(page, parag, shift = 0) { const top = this.container.querySelector(`[data-no="${page}"] [data-pos="${parag}"]`).offsetTop + shift; (document.documentElement || document.body).scrollTo({ top: top, behavior: 'smooth' }); } async goToElementById(id, shift = 0) { const top = this.container.querySelector(`[data-id="${id}"]`).offsetTop + shift; (document.documentElement || document.body).scrollTo({ top: top, behavior: 'smooth' }); } async getFirstVisibleElementOnViewport() { let item; this.container .querySelectorAll('[data-id]') .forEach((val) => { if (val.getBoundingClientRect().top >= 0 && !item) { item = val; } }); return item ? item.getAttribute('data-id') : ''; } async highlightKeywords(elementId, keywords) { keywords.forEach(val => { const elem = this.container.querySelector(`[data-id="${elementId}"]`); elem.innerHTML = elem.innerHTML.replace(new RegExp(val, 'g'), `<mark>${val}</mark>`); }); } langComparisonHandler(event) { this.comparison.emit(new BookReaderEvent(EventName.COMPARISON, '', event.detail)); } menuItemClickedHandler(event) { this.isMenuOpened = false; this.contextMenuItem.emit(event.detail); } handleScroll(_) { var _a; if ((_a = this.container) === null || _a === void 0 ? void 0 : _a.closest('.reading')) { this.isMenuOpened = false; this.onStopScrolling(); this.publishScrollDirections(); } } handleDocumentClick(e) { const target = e.target; if (target.closest('.reading')) { this.publishSelectionEvent(); this.publishDecoratorEvent(target); } } handleDocumentMouseDown(e) { if (e.target.closest('.reading')) { this.selectionManager.removePrevious(); } if (!e.target.closest('[data-type="context-menu"]')) { this.isMenuOpened = false; } } handleSelection(_) { if (this.config.contextMenu.enabled) { this.isMenuOpened = true; this.menuAnchor = this.getContextMenuAnchor(); } } componentDidLoad() { if ((this.pages || []).length > 0 && !this.printed) { this.applyStyle(); this.printed = true; this.decoratorManager.print(this.decorators, this.container); this.bookReady.emit(new BookReaderEvent(EventName.BOOK_READY, '', '')); } } componentDidUpdate() { if ((this.pages || []).length > 0 && !this.printed) { this.printed = true; this.bookReady.emit(new BookReaderEvent(EventName.BOOK_READY, '', '')); } this.bookUpdated.emit(new BookReaderEvent(EventName.BOOK_UPDATED, '', '')); } publishSelectionEvent() { const selection = this.selectionManager.create(); if (selection && this.config.selection.dispatch) { this.selection.emit(new BookReaderEvent(EventName.SELECTION, '', selection)); } } publishDecoratorEvent(target) { const decoratorElement = target.closest('[data-uuid]'); if (decoratorElement) { const uui = decoratorElement.getAttribute('data-uuid'); const decorator = this.decorators.find(v => v.uuid === uui); this.decorator.emit(new BookReaderEvent(EventName.DECORATOR, '', decorator)); } } onStopScrolling() { if (this.scrollTimer !== null) { clearTimeout(this.scrollTimer); } this.scrollTimer = setTimeout(async () => { this.stopScrolling.emit(new BookReaderEvent(EventName.STOP_SCROLLING, '', await this.getFirstVisibleElementOnViewport())); }, 150); } publishScrollDirections() { var st = window.pageYOffset || (document.documentElement || document.body).scrollTop; var direction = st > this.lastScrollTop ? ScrollDirectionAction.DOWN : ScrollDirectionAction.UP; this.lastScrollTop = st <= 0 ? 0 : st; this.scrollDirection.emit(new BookReaderEvent(EventName.SCROLL_DIRECTION, direction, `${st}`)); } applyStyle() { Object.keys(this.readStyle).forEach(key => { this.container.style[key] = this.readStyle[key]; }); } getContextMenuAnchor() { const items = document.querySelectorAll('.selection'); return items[items.length - 1]; } getItemToRender() { if ((this.pages2 || []).length > 0) { return (h("div", { ref: v => this.container = v }, this.pages.map((v, i) => h("book-pages-comparison", { page: v, page2: this.pages2[i] })))); } else if ((this.pages || []).length > 0) { return (h(Fragment, null, h("div", { class: "reading", ref: v => this.container = v }, this.pages.map(v => h("book-page", { page: v, config: this.config.page }))), this.config.contextMenu.enabled ? h("context-menu", { class: this.isMenuOpened ? '' : 'hide', anchor: this.menuAnchor }) : '')); } } render() { return (h(Host, null, this.getItemToRender())); } static get is() { return "book-reader"; } static get encapsulation() { return "scoped"; } static get originalStyleUrls() { return { "$": ["book-reader.css"] }; } static get styleUrls() { return { "$": ["book-reader.css"] }; } static get properties() { return { "config": { "type": "any", "mutable": true, "complexType": { "original": "any", "resolved": "any", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "config", "reflect": false }, "pages": { "type": "unknown", "mutable": true, "complexType": { "original": "Page[]", "resolved": "Page[]", "references": { "Page": { "location": "import", "path": "../../models/page" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } }, "pages2": { "type": "unknown", "mutable": true, "complexType": { "original": "Page[]", "resolved": "Page[]", "references": { "Page": { "location": "import", "path": "../../models/page" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } }, "readStyle": { "type": "unknown", "mutable": true, "complexType": { "original": "ReadStyle", "resolved": "ReadStyle", "references": { "ReadStyle": { "location": "import", "path": "../../models/read-style" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } }, "decorators": { "type": "unknown", "mutable": true, "complexType": { "original": "Decorator[]", "resolved": "Decorator[]", "references": { "Decorator": { "location": "import", "path": "../../models/decorator" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } } }; } static get states() { return { "isMenuOpened": {} }; } static get events() { return [{ "method": "bookReady", "name": "bookReady", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<string, string>", "resolved": "BookReaderEvent<string, string>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" } } } }, { "method": "bookUpdated", "name": "bookUpdated", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<string, string>", "resolved": "BookReaderEvent<string, string>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" } } } }, { "method": "selection", "name": "selection", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<string, Selection>", "resolved": "BookReaderEvent<string, Selection>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" }, "Selection": { "location": "import", "path": "../../models/selection" } } } }, { "method": "decorator", "name": "decorator", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<string, Decorator>", "resolved": "BookReaderEvent<string, Decorator>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" }, "Decorator": { "location": "import", "path": "../../models/decorator" } } } }, { "method": "comparison", "name": "comparison", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<string, Paragraph>", "resolved": "BookReaderEvent<string, Paragraph>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" }, "Paragraph": { "location": "import", "path": "../../models/paragraph" } } } }, { "method": "stopScrolling", "name": "stopScrolling", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<string, string>", "resolved": "BookReaderEvent<string, string>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" } } } }, { "method": "contextMenuItem", "name": "contextMenuItem", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<ContextMenuAction, Selection>", "resolved": "BookReaderEvent<ContextMenuAction, Selection>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" }, "ContextMenuAction": { "location": "import", "path": "../../events/events" }, "Selection": { "location": "import", "path": "../../models/selection" } } } }, { "method": "scrollDirection", "name": "scrollDirection", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "BookReaderEvent<ScrollDirectionAction, string>", "resolved": "BookReaderEvent<ScrollDirectionAction, string>", "references": { "BookReaderEvent": { "location": "import", "path": "../../events/events" }, "ScrollDirectionAction": { "location": "import", "path": "../../events/events" } } } }]; } static get methods() { return { "getConfig": { "complexType": { "signature": "() => Promise<any>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<any>" }, "docs": { "text": "", "tags": [] } }, "setConfig": { "complexType": { "signature": "(config: any) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "setPages": { "complexType": { "signature": "(pages: Page[]) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "Page": { "location": "import", "path": "../../models/page" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "setAllPages": { "complexType": { "signature": "(pages: Page[], pages2: Page[]) => Promise<void>", "parameters": [{ "tags": [], "text": "" }, { "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "Page": { "location": "import", "path": "../../models/page" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "setDecorators": { "complexType": { "signature": "(decorators: Decorator[]) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "Decorator": { "location": "import", "path": "../../models/decorator" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "getReadStyle": { "complexType": { "signature": "() => Promise<ReadStyle>", "parameters": [], "references": { "Promise": { "location": "global" }, "ReadStyle": { "location": "import", "path": "../../models/read-style" } }, "return": "Promise<ReadStyle>" }, "docs": { "text": "", "tags": [] } }, "setReadStyle": { "complexType": { "signature": "(style: ReadStyle) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "ReadStyle": { "location": "import", "path": "../../models/read-style" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "goToPage": { "complexType": { "signature": "(no: number, shift?: number) => Promise<void>", "parameters": [{ "tags": [], "text": "" }, { "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "HTMLElement": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "goToParagraph": { "complexType": { "signature": "(page: number, parag: number, shift?: number) => Promise<void>", "parameters": [{ "tags": [], "text": "" }, { "tags": [], "text": "" }, { "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "HTMLElement": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "goToElementById": { "complexType": { "signature": "(id: string, shift?: number) => Promise<void>", "parameters": [{ "tags": [], "text": "" }, { "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "HTMLElement": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "getFirstVisibleElementOnViewport": { "complexType": { "signature": "() => Promise<string>", "parameters": [], "references": { "Promise": { "location": "global" }, "HTMLElement": { "location": "global" } }, "return": "Promise<string>" }, "docs": { "text": "", "tags": [] } }, "highlightKeywords": { "complexType": { "signature": "(elementId: string, keywords: string[]) => Promise<void>", "parameters": [{ "tags": [], "text": "" }, { "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "HTMLElement": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } } }; } static get listeners() { return [{ "name": "langComparison", "method": "langComparisonHandler", "target": undefined, "capture": false, "passive": false }, { "name": "menuItemClicked", "method": "menuItemClickedHandler", "target": undefined, "capture": false, "passive": false }, { "name": "scroll", "method": "handleScroll", "target": "window", "capture": false, "passive": true }, { "name": "click", "method": "handleDocumentClick", "target": "body", "capture": false, "passive": false }, { "name": "mousedown", "method": "handleDocumentMouseDown", "target": "body", "capture": false, "passive": true }, { "name": "selection", "method": "handleSelection", "target": undefined, "capture": false, "passive": false }]; } }