UNPKG

@bookbox/view-html

Version:

Bookbox view for html

442 lines (441 loc) 21.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createHtmlBook = exports.htmlSynteticElements = exports.useHtmlElements = void 0; const core_1 = require("@bookbox/core"); const htmlBookSettings_1 = require("./htmlBookSettings"); const layout_1 = require("./layout"); const model_1 = require("./model"); const math_1 = require("./math"); const code_1 = require("./code"); const useHtmlElements = params => ({ title: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<h1 class="book-box-title" data-key="${key}" data-name="title" data-layout="top">${childrenHtml}</h1>`; }, authors: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<div class="book-box-authors" data-key="${key}" data-name="authors" data-layout="top">${childrenHtml}</div>`; }, draft: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<div class="book-box-draft" data-key="${key}" data-name="draft" data-layout="top">${childrenHtml}</div>`; }, header: ({ level, key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const elem = `h${level}`; return `<div class="book-box-header-container" data-key="${key}" data-name="header" data-layout="top"> <${elem} class="book-box-header"> ${childrenHtml} <div class="book-box-header-mark book-box_clickable" onclick="this.scrollIntoView();">§</div> </${elem}> </div>`; }, strong: ({ key }) => ({ children }) => `<strong data-key="${key}" data-name="strong" data-layout="top">${(0, model_1.listToHtml)(children)}</strong>`, em: ({ key }) => ({ children }) => `<em data-key="${key}" data-name="em" data-layout="top">${(0, model_1.listToHtml)(children)}</em>`, format: { b: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<b data-key="${key}" data-name="format.b" data-layout="top">${childrenHtml}</b>`; }, i: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<i data-key="${key}" data-name="format.i" data-layout="top">${childrenHtml}</i>`; }, u: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<u data-key="${key}" data-name="format.u" data-layout="top">${childrenHtml}</u>`; }, s: ({ key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); return `<s data-key="${key}" data-name="format.s" data-layout="top">${childrenHtml}</s>`; }, sub: ({ key }) => ({ children }) => `<sub data-key="${key}" data-name="format.sub" data-layout="top">${(0, model_1.listToHtml)(children)}</sub>`, sup: ({ key }) => ({ children }) => `<sup data-key="${key}" data-name="format.sup" data-layout="top">${(0, model_1.listToHtml)(children)}</sup>`, pre: ({ key }) => ({ children }) => `<span><pre data-key="${key}" data-name="format.pre" style="display: inline-block;margin: 0; padding: 0 4px; line-height: 1em; overflow: unset;">${(0, model_1.listToHtml)(children)}</pre></span>`, small: ({ inline, key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const elem = inline ? 'span' : 'div'; return `<${elem} data-key="${key}" class="book-box-format-small" data-name="format.small" data-layout="top">${childrenHtml}</${elem}>`; }, }, web: { video: props => ({ children }) => { const { type, src = '', alt, width, height = '50%', inline, block, position, key } = props; const loadableId = `${key}-loadable`; const video = getIframe({ id: loadableId, width, height, src, alt, type: 'video', }); const content = getFigure(video, (0, model_1.listToHtml)(children)); const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); return `<div class="book-box-web-video book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-name="web.video" data-layout="top" > ${content} </div>`; }, audio: ({ src = '', alt, key = '', inline, block, position, width, height = '50%' }) => ({ children }) => { const audio = getIframe({ id: key, width, height, src, alt, type: 'audio', }); const content = getFigure(audio, (0, model_1.listToHtml)(children)); const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); return `<div class="book-box-web-audio book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-name="web.audio" data-layout="top"> ${content} </div>`; }, message: ({ src = '', type, inline, block, position, width, height = '50%', alt, key = '' }) => ({ children }) => { const message = getIframe({ id: key, width, height, src, alt, type: 'message', }); const content = getFigure(message, (0, model_1.listToHtml)(children)); const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); return `<div class="book-box-web-message book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-name="web.message" data-layout="top"> ${content} </div>`; }, }, code: ({ lang, key, inline, block, position = 'full' }) => ({ children, store }) => { const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); const langAttribute = lang ? `data-code-language="${lang}"` : ''; const langMark = lang ? `<div class="book-box-code-lang-mark">${lang}</div>` : ''; const rawChildren = store.elementsByKeys[key !== null && key !== void 0 ? key : ''].children; const colorCodeHtml = params.renderColorCode({ text: rawChildren.join(''), lang }); const codeHtml = `<div style="width: 100%">${langMark}<pre><code ${langAttribute}>${colorCodeHtml}</code></pre></div>`; const content = position === 'full' ? codeHtml : getFigure(codeHtml, ''); return `<div class="book-box-code ${lang ? 'book-box-code-with-lang' : ''} book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-name="code" data-layout="top">${content}</div>`; }, label: ({ key, ref = '' }) => ({ children, store }) => { var _a; const childrenHtml = (0, model_1.listToHtml)(children); const labelId = `label-${key}`; const data = (_a = store.dataByKeys[ref]) !== null && _a !== void 0 ? _a : []; const panel = (0, htmlBookSettings_1.getPanel)({ prefix: labelId, tumbler: { content: childrenHtml, classes: ['book-box-label-mark'], inputClasses: ['book-box-label-input'], }, panel: { content: `<div class="book-box-label-panel-content"> ${(0, model_1.listToHtml)(data)} <div class="book-box-label-panel-goto" onclick="bbx.gotoKey('${ref}')">→</div> </div>`, classes: ['book-box-label-data'], name: `<div class="book-box-label-panel-header">label: <div class="book-box-label-panel-mark">${childrenHtml}</div> <div class="book-box-label-panel-goto" onclick="bbx.gotoKey('${key}')">→</div></div>`, }, }); return `<div class="book-box-label" data-key="${key}" data-name="label" data-ref="${ref}" data-layout="top">${panel}</div>`; }, tooltip: ({ content, key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const labelId = `label-${key}`; const data = [`${content}`]; const panel = (0, htmlBookSettings_1.getPanel)({ prefix: labelId, tumbler: { content: childrenHtml, classes: ['book-box-label-mark'], inputClasses: ['book-box-label-input'], }, panel: { content: `<div class="book-box-label-panel-content">${(0, model_1.listToHtml)(data)}</div>`, classes: ['book-box-label-data'], name: `tooltip: <div class="book-box-label-panel-mark">${childrenHtml}</div>`, }, }); return ` <div class="book-box-label" data-key="${key}" data-name="tooltip" data-layout="top"> ${panel} </div> `; }, link: ({ ref, href, key }) => ({ children }) => { var _a; const content = children.length > 0 ? children : [(_a = href !== null && href !== void 0 ? href : ref) !== null && _a !== void 0 ? _a : '']; const childrenHtml = (0, model_1.listToHtml)(content); return href ? `<a class="book-box-link book-box_clickable" href="${href}" data-key="${key}" data-name="link">${childrenHtml}</a>` : `<span class="book-box-link book-box_clickable" data-key="${key}" data-name="link" onclick="bbx.gotoKey('${ref}')" data-layout="top">${childrenHtml}</span>`; }, image: ({ src = '/~~non-exist.png', alt, position = 'center', height = 1, width = 1, block, inline, key }) => ({ children }) => { const isSvg = src.endsWith('.svg'); const heightSize = (0, layout_1.parseSize)(height !== null && height !== void 0 ? height : 1); const widthSize = (0, layout_1.parseSize)(width !== null && width !== void 0 ? width : 1); const sizeBlockStyle = (0, layout_1.getCssSizeStyle)({ width: widthSize, height: heightSize, }); const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); const svgSource = isSvg ? `<source type="image/svg+xml" srcSet="${src}" />` : ''; const image = `<picture style="${sizeBlockStyle}; width: 100%"> ${svgSource} <img style="${sizeBlockStyle}" src="${src}" alt="${alt}" loading="lazy"/> </picture>`; const content = getFigure(image, (0, model_1.listToHtml)(children)); return `<div class="book-box-image book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-layout="top" > ${content} </div>`; }, video: ({ src, alt = '', position = 'center', height = 1, width = 1, block, inline, key }) => ({ children }) => { const heightSize = (0, layout_1.parseSize)(height); const widthSize = (0, layout_1.parseSize)(width); const sizeBlockStyle = (0, layout_1.getCssSizeStyle)({ width: widthSize, height: heightSize, }); const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); const video = `<video src="${src}" data-key="${key}" controls style="${sizeBlockStyle}">${alt}</video>`; const content = getFigure(video, (0, model_1.listToHtml)(children)); return `<div class="book-box-video book-box_media-${view} book-box_media-${layoutPosition}" data-name="video" data-key="${key}" data-layout="top" > ${content} </div>`; }, audio: ({ src, alt = '', key, inline, block, position = 'center' }) => ({ children }) => { const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); const audio = `<audio controls src="${src}">${alt}</audio>`; const content = getFigure(audio, (0, model_1.listToHtml)(children)); return `<div class="book-box-audio book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-name="audio" data-layout="top" > ${content} </div>`; }, math: ({ key = '', position = 'inline', inline, block }) => ({ children, store }) => { const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); const rawChildren = store.elementsByKeys[key].children; const content = (0, math_1.renderFormula)(rawChildren.map(String).join(''), view === 'block'); return `<div class="book-box-math book-box_media-${view} book-box_media-${layoutPosition}" data-key="${key}" data-name="math" data-layout="top" > ${content} </div>`; }, area: ({ key, inline = false, meta }) => ({ children }) => { const content = (0, model_1.listToHtml)(children); const startSpace = content.startsWith(' ') ? ' ' : ''; const endSpace = content.endsWith(' ') ? ' ' : ''; return `${startSpace}<div class="book-box-area ${inline ? 'book-box-area-inline' : ''}" data-name="area" data-key="${key}" data-layout="top">${content}</div>${endSpace}`; }, item: ({ key }) => ({ children }) => `<li data-key="${key}" data-name="item" data-layout="top">${(0, model_1.listToHtml)(children)}</li>`, list: ({ order, key }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const elem = order ? 'ol' : 'ul'; return `<${elem} data-key="${key}" data-name="list" class="book-box-list" data-layout="top" > ${childrenHtml} </${elem}>`; }, separator: ({ key }) => () => `<hr data-key="${key}" data-name="separator" class="book-box-separator" data-layout="top"/>`, external: ({ scope = 'custom', name = 'local', params }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const paramsStr = params ? JSON.stringify(params) : ''; return `<div data-name="external" class="book-box-external" style="" data-layout="top"> <p style="color: gray"> External: ${scope}.${name} </p> ${paramsStr ? `params: <pre>${paramsStr}</pre>` : ''} ${childrenHtml} </div>`; }, counter: () => ({ children }) => `<span>${(0, model_1.listToHtml)(children)}</span>`, use: ({ ref = '', path }) => ({ children, store }) => { var _a; const targetElement = store.elementsByKeys[ref]; let content = (0, model_1.listToHtml)(children); if (targetElement !== undefined) { if (path !== undefined) { const metaValue = ((_a = targetElement.props.meta) !== null && _a !== void 0 ? _a : {})[path]; if (metaValue !== undefined) { content = `${metaValue}`; } } else { const targetToken = store.dataByKeys[ref]; if (targetToken !== undefined) { content = (0, model_1.listToHtml)(targetToken); } } } return content; }, table: ({ key = '', position, inline, block = true, align = 'center' }) => ({ store, build }) => { const { view, position: layoutPosition } = (0, layout_1.getLayoutParams)({ inline, block, position, }); const tableChildren = store.elementsByKeys[key].children; const isRow = (elem) => typeof elem === 'object' && elem.name === 'row'; const tableContent = tableChildren.filter(elem => isRow(elem)); const captionContent = tableChildren.filter(elem => !isRow(elem)); const table = `<div><table>${(0, model_1.listToHtml)(build(tableContent))}</table></div>`; const caption = (0, model_1.listToHtml)(build(captionContent)); const content = getFigure(table, caption); return `<div class="book-box-table book-box_media-${view} book-box_media-${layoutPosition} book-box_align-${align}" data-key="${key}" data-name="table" data-layout="top" > ${content} </div>`; }, row: ({ key, head = false, align }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const elem = head ? 'thead' : 'tr'; return `<${elem} data-key="${key}" data-name="row" data-layout="top" class="book-box_align-${align}">${childrenHtml}</${elem}>`; }, cell: ({ key, align }) => ({ children, store, parents }) => { var _a, _b; const childrenHtml = (0, model_1.listToHtml)(children); const intoHead = (_b = (_a = store.elementsByKeys[parents[0]]) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.head; const elem = intoHead ? 'th' : 'td'; return `<${elem} data-key="${key}" data-name="cell" data-layout="top" class="book-box_align-${align}">${childrenHtml}</${elem}>`; }, resource: () => () => '', }); exports.useHtmlElements = useHtmlElements; exports.htmlSynteticElements = { text: ({ raw = '' }) => () => { if (raw === '\n') return raw; const text = `${(0, core_1.parseNewLines)('<br/>')(raw.replace(/</g, '&lt;').replace(/>/g, '&gt;')).join('')}`; return text; }, page: ({ count, key }) => ({ children }) => `<span id="page-${count}" class="book-box-page book-box_clickable" data-layout="top" data-page="${count}" data-key="${key}" data-name=".page" href="#page-${count}" onclick="this.scrollIntoView();" >${(0, model_1.listToHtml)(children)}</span>`, error: ({ props, name, error }) => ({ children }) => { const childrenHtml = (0, model_1.listToHtml)(children); const propsStr = JSON.stringify(props); return `<div data-name=".error" class="book-box--error"> Error for element <i>${name}</i>: <p style="color: red"> ${error !== null && error !== void 0 ? error : ''} <pre>${propsStr}</pre> </p> ${childrenHtml} </div>`; }, empty: () => () => '', }; const htmlBuilder = { elements: (0, exports.useHtmlElements)({ renderColorCode: code_1.renderColorCode }), synteticElements: exports.htmlSynteticElements, }; exports.createHtmlBook = (0, core_1.getPartialApply)(core_1.createBook, { builder: htmlBuilder, }); function getFigure(content, caption) { return `<figure class="book-box_media-figure"> <div>${content}</div> <figcaption class="book-box_media-figure-caption"><div>${caption}</div></figcaption> </figure>`; } function getIframe({ id, width, height, src, type, alt, }) { const heightSize = (0, layout_1.parseSize)(height); const widthSize = (0, layout_1.parseSize)(width); const sizeBlockStyle = (0, layout_1.getCssSizeStyle)({ width: widthSize, height: heightSize, }); const loadableId = `${id}-loadable`; const altText = alt !== null && alt !== void 0 ? alt : `${type} <a href="${src}" class="book-box-link book-box_clickable">${src}</a>`; return `<div style="width: 100%; height:100%; ${sizeBlockStyle}" class="book-box_loadable book-box_loadable-loaded" id="${loadableId}"> <iframe style="width: 100vw; height:100vh; ${sizeBlockStyle}" src=${src} class="book-box_loadable-content" frameborder="0" allow="encrypted-media; picture-in-picture" allowfullscreen loading="lazy" data-alt="${alt}" onload="this.parentNode.classList.add('book-box_loadable-loaded')" onerror="this.parentNode.classList.add('book-box_loadable-error-loaded')" > </iframe> <script>window.setTimeout(() => document.getElementById('${loadableId}').classList.add('book-box_loadable-loaded'), 5000)</script> <div class="book-box_loadable-error">${alt}</div> <div class="book-box_loadable-progress" style="width: 100%; height: ${heightSize}vh; ${sizeBlockStyle}"> <p style="padding: 1rem;">${altText}<p> <button onclick="document.getElementById('${loadableId}').classList.add('book-box_loadable-loaded')">Show raw iframe</button> </div> </div>`; }