myst-to-typst
Version:
Export from MyST mdast to Typst
76 lines (69 loc) • 2.54 kB
text/typescript
import type { Handler } from './types.js';
import { fileError, type GenericNode } from 'myst-common';
function countColumns(table: GenericNode) {
const firstRow = table.children?.find((child) => child.type === 'tableRow');
const columns = firstRow?.children
?.filter((cell: GenericNode) => cell.type === 'tableCell')
.reduce((val: number, cell: GenericNode) => val + (cell.colspan ?? 1), 0);
return columns;
}
function isHeaderRow(node: GenericNode) {
if (node.type !== 'tableRow') return false;
return node.children
?.filter((child) => child.type === 'tableCell')
.every((child) => child.header);
}
function countHeaderRows(table: GenericNode) {
const headerRows = table.children?.filter((child) => isHeaderRow(child));
return headerRows?.length ?? 0;
}
export const tableHandler: Handler = (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: Handler = (node, state) => {
state.renderChildren(node, 1);
};
export const tableCellHandler: Handler = (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 as string;
const rgb = fill.startsWith('#');
state.write(`fill: ${rgb ? `rgb("${fill}")` : fill}, `);
}
state.write(')');
}
state.write('[\n');
state.renderChildren(node, 1);
state.write('],\n');
};