UNPKG

@curvenote/schema

Version:

Schema and markdown parser for @curvenote/editor

207 lines 9.06 kB
import { createLatexStatement } from '../serialize/tex/utils'; import { CaptionKind, NodeGroups } from './types'; import { determineCaptionKind } from '../process/utils'; import { getColumnWidths, getFirstChildWithName, getNumberedAttrs, getNumberedDefaultAttrs, readBooleanDomAttr, normalizeLabel, readBooleanAttr, setNumberedAttrs, hasFancyTable, writeMdastSnippet, } from './utils'; import { nodeNames } from '../types'; import { toMarkdown as codeToMarkdown } from './code'; import { writeDirectiveOptions } from '../serialize/markdown/utils'; const figure = { group: NodeGroups.block, content: NodeGroups.insideFigure, isolating: true, attrs: Object.assign(Object.assign({}, getNumberedDefaultAttrs()), { align: { default: 'center' }, multipage: { default: false }, landscape: { default: false }, fullpage: { default: false } }), toDOM(node) { const { align, multipage, landscape, fullpage } = node.attrs; return [ 'figure', Object.assign(Object.assign({}, setNumberedAttrs(node.attrs)), { align, multipage, landscape, fullpage }), 0, ]; }, parseDOM: [ { tag: 'figure', getAttrs(dom) { var _a; return Object.assign(Object.assign({}, getNumberedAttrs(dom)), { align: (_a = dom.getAttribute('align')) !== null && _a !== void 0 ? _a : 'center', multipage: readBooleanDomAttr(dom, 'multipage'), landscape: readBooleanDomAttr(dom, 'landscape'), fullpage: readBooleanDomAttr(dom, 'fullpage') }); }, }, ], attrsFromMyst: (token) => { var _a; const match = (_a = token.class) === null || _a === void 0 ? void 0 : _a.match(/align-(left|right|center)/); return { id: token.identifier || null, label: null, numbered: token.enumerated || false, align: (match ? match[1] : 'center'), multipage: false, landscape: false, fullpage: false, }; }, toMyst: (props, options) => { var _a, _b, _c, _d, _e; let containerKind = 'figure'; (_a = props.children) === null || _a === void 0 ? void 0 : _a.forEach((child) => { if (child.type === 'image' || child.type === 'table') { child.align = props.align || undefined; } if (child.type === 'table') { containerKind = 'table'; } }); const localizedId = (_e = (_d = (_b = options.localizeId) === null || _b === void 0 ? void 0 : _b.call(options, (_c = props.id) !== null && _c !== void 0 ? _c : '')) !== null && _d !== void 0 ? _d : props.id) !== null && _e !== void 0 ? _e : ''; return Object.assign(Object.assign({ type: 'container', kind: containerKind }, normalizeLabel(localizedId)), { enumerated: readBooleanAttr(props.numbered), class: props.align ? `align-${props.align}` : undefined, children: (props.children || []) }); }, }; export const toMarkdown = (state, node, parent, index) => { var _a, _b, _c, _d, _e, _f, _g; state.ensureNewLine(); const kind = determineCaptionKind(node); const { id } = node.attrs; // TODO: Translate between callout/admonition const caption = getFirstChildWithName(node, nodeNames.figcaption); state.nextCaptionId = (_d = (_c = (_b = (_a = state.options).localizeId) === null || _b === void 0 ? void 0 : _b.call(_a, id !== null && id !== void 0 ? id : '')) !== null && _c !== void 0 ? _c : id) !== null && _d !== void 0 ? _d : undefined; switch (kind) { case CaptionKind.fig: { const image = getFirstChildWithName(node, [nodeNames.image, nodeNames.iframe]); if (!image) return; const { src, align, width } = image === null || image === void 0 ? void 0 : image.attrs; const href = (_g = (_f = (_e = state.options).localizeImageSrc) === null || _f === void 0 ? void 0 : _f.call(_e, src)) !== null && _g !== void 0 ? _g : src; if (image.type.name === nodeNames.iframe) { state.render(image, parent, index); return; } state.write(`\`\`\`{figure} ${href}\n`); const opts = { name: state.nextCaptionId, align, width: `${width}%` }; writeDirectiveOptions(state, opts); if (caption) { state.renderInline(caption); state.ensureNewLine(); } state.write('```'); state.closeBlock(node); return; } case CaptionKind.eq: { state.renderContent(node); return; } case CaptionKind.table: { const table = getFirstChildWithName(node, [nodeNames.table]); if (table && hasFancyTable(table)) { writeMdastSnippet(state, node); return; } state.nextTableCaption = caption; if (table) state.render(table, parent, index); state.closeBlock(node); return; } case CaptionKind.code: { const code = getFirstChildWithName(node, [nodeNames.code_block]); // TODO: add a figure caption for code if (code) codeToMarkdown(state, code, node, index); state.closeBlock(node); return; } default: console.log(`Unknown figure kind: "${kind}", id=${id}, children=${node.childCount}`); return; } }; function nodeToCommand(node) { const kind = determineCaptionKind(node); switch (kind) { case CaptionKind.fig: return node.attrs.fullpage ? 'figure*' : 'figure'; case CaptionKind.table: return node.attrs.fullpage ? 'table*' : 'table'; case CaptionKind.code: // TODO full width code return 'code'; case CaptionKind.eq: return 'figure'; // not sure what to do here. default: return 'figure'; } } function nodeToLaTeXOptions(node) { const kind = determineCaptionKind(node); switch (kind) { case CaptionKind.fig: case CaptionKind.table: return '!htbp'; case CaptionKind.code: return 'H'; case CaptionKind.eq: default: return undefined; } } function figureContainsTable(node) { const table = node.content.content.find((n) => n.type.name === nodeNames.table); return table; } export const toTex = createLatexStatement((state, node) => { // if the figure is in a table, skip to child content if (state.isInTable) return null; // if figure contains a table, we need find out which table environment to use state.containsTable = false; const table = figureContainsTable(node); let tableInfo; if (table && node.attrs.multipage) { state.containsTable = true; tableInfo = getColumnWidths(table); } let before; let after; if (node.attrs.landscape) { // requires pdflscape package to be loaded before = '\\begin{landscape}'; after = '\\end{landscape}'; } // TODO for longtable to work with two columns we need to flip out to single column first // and then back to multi column, if we were in multicolumn mode // Q: we can know if we are in a two column mode from the template we are using, but how is this made available at this level? return { command: state.containsTable && node.attrs.multipage ? 'longtable' : nodeToCommand(node), commandOpts: state.containsTable && tableInfo ? tableInfo.columnSpec : undefined, bracketOpts: state.containsTable ? undefined : nodeToLaTeXOptions(node), before, after, }; }, (state, node) => { var _a, _b, _c, _d; // if the figure is in a table, skip to child content if (state.isInTable) { state.renderContent(node); return; } const { numbered, id, multipage } = node.attrs; const localId = (_d = (_c = (_b = (_a = state.options).localizeId) === null || _b === void 0 ? void 0 : _b.call(_a, id !== null && id !== void 0 ? id : '')) !== null && _c !== void 0 ? _c : id) !== null && _d !== void 0 ? _d : undefined; // TODO: Based on align attr // may have to modify string returned by state.renderContent(n); // https://tex.stackexchange.com/questions/91566/syntax-similar-to-centering-for-right-and-left // centering does not work in a longtable environment if (!multipage || !state.containsTable) state.write('\\centering'); state.ensureNewLine(); // Pass the relevant information to the child nodes state.nextCaptionNumbered = numbered; state.nextCaptionId = localId; state.longFigure = multipage; state.renderContent(node); state.longFigure = undefined; state.containsTable = false; }); export default figure; //# sourceMappingURL=figure.js.map