@bookbox/view-html
Version:
Bookbox view for html
442 lines (441 loc) • 21.8 kB
JavaScript
;
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, '<').replace(/>/g, '>')).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>`;
}