feat(md-deck): support structured data-config with template layers
Allow md-deck layers to render markdown templates via {{var}}
substitution instead of only CSV props. Add config.ts to normalize
a JSON data-config attribute (size, grid, shape, layers) emitted by
the yaml code-block tag, taking precedence over legacy string props.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import markedCodeBlockYamlTag from "./code-block-yaml-tag";
|
||||
|
||||
const ext = markedCodeBlockYamlTag().extensions?.[0] as unknown as {
|
||||
tokenizer: (src: string) => any;
|
||||
renderer: (token: any) => string;
|
||||
};
|
||||
|
||||
function render(src: string): string {
|
||||
const token = ext.tokenizer(src);
|
||||
return ext.renderer(token);
|
||||
}
|
||||
|
||||
describe("code-block-yaml-tag", () => {
|
||||
test("renders body and scalar props as attributes", () => {
|
||||
const html = render(
|
||||
"```yaml/tag\ntag: md-deck\nbody: ./cards.csv\nsize: 54x86\n```",
|
||||
);
|
||||
expect(html).toContain("<md-deck size=\"54x86\">./cards.csv</md-deck>");
|
||||
});
|
||||
|
||||
test("serializes data-config to a JSON attribute", () => {
|
||||
const html = render(
|
||||
[
|
||||
"```yaml/tag",
|
||||
"tag: md-deck",
|
||||
"body: ./cards.csv",
|
||||
"data-config:",
|
||||
" size: 63x88",
|
||||
" layers:",
|
||||
" - prop: title",
|
||||
" x1: 1",
|
||||
" y1: 1",
|
||||
" x2: 5",
|
||||
" y2: 1",
|
||||
" font: 12",
|
||||
"```",
|
||||
].join("\n"),
|
||||
);
|
||||
expect(html).toContain("<md-deck data-config=");
|
||||
expect(html).toContain("./cards.csv</md-deck>");
|
||||
// data-config must be valid JSON with escaped quotes for the attribute
|
||||
const m = html.match(/data-config="([^"]*)"/);
|
||||
expect(m).not.toBeNull();
|
||||
const decoded = (m![1] || "").replace(/"/g, '"');
|
||||
const config = JSON.parse(decoded);
|
||||
expect(config.size).toBe("63x88");
|
||||
expect(config.layers).toHaveLength(1);
|
||||
expect(config.layers[0]).toMatchObject({
|
||||
prop: "title",
|
||||
x1: 1,
|
||||
y1: 1,
|
||||
font: 12,
|
||||
});
|
||||
});
|
||||
|
||||
test("handles missing tag and body", () => {
|
||||
const html = render("```yaml/tag\nclass: foo\n```");
|
||||
expect(html).toContain("<tag-unknown class=\"foo\"></tag-unknown>");
|
||||
});
|
||||
});
|
||||
@@ -33,6 +33,18 @@ export default function markedCodeBlockYamlTag(): MarkedExtension {
|
||||
delete (rest as Record<string, unknown>).body;
|
||||
}
|
||||
|
||||
// A structured `data-config` object is serialized to JSON and
|
||||
// passed as a single attribute (e.g. md-deck layers/templates).
|
||||
let configAttr = "";
|
||||
if ("data-config" in rest) {
|
||||
const rawConfig = rest["data-config"];
|
||||
delete (rest as Record<string, unknown>)["data-config"];
|
||||
if (rawConfig !== undefined && rawConfig !== null) {
|
||||
const json = JSON.stringify(rawConfig).replace(/"/g, """);
|
||||
configAttr = ` data-config="${json}"`;
|
||||
}
|
||||
}
|
||||
|
||||
const propsStr = Object.entries(rest)
|
||||
.map(([key, value]) => {
|
||||
const strValue = String(value);
|
||||
@@ -48,13 +60,15 @@ export default function markedCodeBlockYamlTag(): MarkedExtension {
|
||||
raw: match[0],
|
||||
tagName,
|
||||
props: propsStr,
|
||||
config: configAttr,
|
||||
content,
|
||||
};
|
||||
}
|
||||
},
|
||||
renderer(token: any) {
|
||||
const propsAttr = token.props ? ` ${token.props}` : "";
|
||||
return `<${token.tagName}${propsAttr}>${token.content || ""}</${token.tagName}>\n`;
|
||||
const configAttr = token.config || "";
|
||||
return `<${token.tagName}${propsAttr}${configAttr}>${token.content || ""}</${token.tagName}>\n`;
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user