myst-to-typst
Version:
Export from MyST mdast to Typst
68 lines (67 loc) • 2.53 kB
JavaScript
import { fileError } from 'myst-common';
function countColumns(table) {
const firstRow = table.children?.find((child) => child.type === 'tableRow');
const columns = firstRow?.children
?.filter((cell) => cell.type === 'tableCell')
.reduce((val, cell) => val + (cell.colspan ?? 1), 0);
return columns;
}
function isHeaderRow(node) {
if (node.type !== 'tableRow')
return false;
return node.children
?.filter((child) => child.type === 'tableCell')
.every((child) => child.header);
}
function countHeaderRows(table) {
const headerRows = table.children?.filter((child) => isHeaderRow(child));
return headerRows?.length ?? 0;
}
export const tableHandler = (node, state) => {
const prevState = state.data.isInTable;
state.data.isInTable = true;
const command = state.data.isInFigure ? 'tablex' : '#tablex';
const columns = countColumns(node);
if (!columns) {
fileError(state.file, 'Unable to count table columns', {
node,
source: 'myst-to-typst',
});
return;
}
state.useMacro('#import "@preview/tablex:0.0.9": tablex, cellx, hlinex, vlinex');
// These two separate style hooks are somewhat redundant, but they allow defining
// article-wide styles and single-table styles separately
state.useMacro('#let tableStyle = (:)');
state.useMacro('#let columnStyle = (:)');
state.write(`${command}(columns: ${columns}, header-rows: ${countHeaderRows(node)}, repeat-header: true, ..tableStyle, ..columnStyle,\n`);
state.renderChildren(node, 1);
state.write(')\n');
state.data.isInTable = prevState;
};
export const tableRowHandler = (node, state) => {
state.renderChildren(node, 1);
};
export const tableCellHandler = (node, state) => {
if (node.rowspan || node.colspan || node.align || node.style?.backgroundColor) {
state.write('cellx(');
if (node.rowspan) {
state.write(`rowspan: ${node.rowspan}, `);
}
if (node.colspan) {
state.write(`colspan: ${node.colspan}, `);
}
if (node.align) {
state.write(`align: ${node.align}, `);
}
if (node.style?.backgroundColor) {
const fill = node.style.backgroundColor;
const rgb = fill.startsWith('#');
state.write(`fill: ${rgb ? `rgb("${fill}")` : fill}, `);
}
state.write(')');
}
state.write('[\n');
state.renderChildren(node, 1);
state.write('],\n');
};