Build and publish Eldorado documentation / build (pull_request) Successful in 42s
46 lines
3.3 KiB
JavaScript
46 lines
3.3 KiB
JavaScript
// Native editable geometry and edges; scoped editorial typography for Stage 01.
|
|
export const isSkillsStory = name => /^strategy-(governance|quality|adoption|measurement|baseline|maintenance|stage01|stage02)(-mobile)?$/.test(name);
|
|
const escape = text => text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"');
|
|
export function skillsViewer(html, spec) {
|
|
if (spec.diagram_type !== 'architecture' || !spec.components.length) throw new Error('Skills story contract drift');
|
|
const stageMobile = /strategy-stage0[12]-mobile\//.test(spec.meta.output);
|
|
const detailSize = stageMobile ? 38 : spec.meta.output.includes('-mobile/') ? 34 : 32;
|
|
for (const node of spec.components) {
|
|
const pattern = new RegExp(`<g\\b[^>]*data-node-id="${node.id}"[^>]*>[\\s\\S]*?<text data-node-label=[\\s\\S]*?</g>`);
|
|
const match = html.match(pattern);
|
|
if (!match) throw new Error(`Missing Skills node: ${node.id}`);
|
|
const original = match[0];
|
|
const rectangles = [...original.matchAll(/<rect\b[^>]*class="c-[^"]+"[^>]*\/>/g)];
|
|
if (rectangles.length !== 2) throw new Error('Skills shape contract drift');
|
|
const [x,y] = node.pos;
|
|
const lines = node.sublabel.split('\n');
|
|
if (!node.tag || !node.sublabel || lines.length > 4) throw new Error(`Skills copy exceeds capacity: ${node.id}`);
|
|
const text = (value, top, size, attrs) => `<text x="${x+28}" y="${y+top}" font-size="${size}" ${attrs}>${escape(value)}</text>`;
|
|
const content = text(node.tag, 40, detailSize, 'class="t-muted" data-detail="context"')
|
|
+ text(node.label, 89, stageMobile ? 40 : 36, 'class="t-primary" data-node-label="" font-weight="700"')
|
|
+ lines.map((line,i) => text(line, 138+i*42, detailSize, 'class="t-primary" data-detail="context"')).join('');
|
|
const prefix = original.slice(0,rectangles[1].index+rectangles[1][0].length).replaceAll('rx="6"','rx="0"');
|
|
html = html.replace(original,prefix+content+'</g>');
|
|
}
|
|
const roots = [...html.matchAll(/<svg\b[^>]*role="img"[^>]*>/g)];
|
|
if (roots.length !== 1) throw new Error('Skills SVG contract drift');
|
|
const [width,height] = spec.meta.viewBox;
|
|
const root = roots[0][0].replace(/viewBox="[^"]*"/,`viewBox="0 0 ${width} ${height}"`);
|
|
// Embedded in SVG so downloaded files retain typography without the viewer.
|
|
html = html.replace(roots[0][0],root+`<style id="eldorado-skills-typography">
|
|
[data-node-id] text {font-family:Arial,sans-serif;}
|
|
rect[fill="url(#grid)"] {opacity:0;}
|
|
[data-node-id] > rect:not(.c-mask) {stroke-width:3;}
|
|
[data-edge-id] {opacity:1;}
|
|
</style>`);
|
|
// Native labels use tiny technical annotations. Enlarge both text and mask,
|
|
// retaining the native edge identity, routing and explicit label position.
|
|
html = html.replace(/(<g data-detail="context"[^>]*data-edge-id[^>]*>)([\s\S]*?)(<\/g>)/g, (all,open,body,close) => {
|
|
const text = body.match(/<text x="([^"]+)" y="([^"]+)"([^>]*)>([\s\S]*?)<\/text>/);
|
|
if (!text) throw new Error('Skills edge label contract drift');
|
|
const x=Number(text[1]), y=Number(text[2]), width=text[4].replace(/&[^;]+;/g,'x').length*17+24;
|
|
return open+`<rect x="${x-width/2}" y="${y-34}" width="${width}" height="44" class="c-mask"/>`+text[0].replace('font-size="8"',`font-size="${detailSize}"`)+close;
|
|
});
|
|
return html;
|
|
}
|