@avodado/render 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +113 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2083,13 +2083,20 @@ function renderErd(data) {
|
|
|
2083
2083
|
hidden = cols.length - rows.length;
|
|
2084
2084
|
}
|
|
2085
2085
|
const bodyRows = rows.length + (hidden > 0 ? 1 : 0);
|
|
2086
|
-
|
|
2087
|
-
|
|
2086
|
+
return {
|
|
2087
|
+
name: e.name,
|
|
2088
|
+
cols,
|
|
2089
|
+
rows,
|
|
2090
|
+
hidden,
|
|
2091
|
+
pkIdx: cols.findIndex((c) => c.pk === true),
|
|
2092
|
+
w: COL_W,
|
|
2093
|
+
h: HEAD_H + bodyRows * ROW_H + BOT_PAD
|
|
2094
|
+
};
|
|
2088
2095
|
});
|
|
2089
2096
|
const byName = new Map(boxes.map((b) => [b.name, b]));
|
|
2090
2097
|
const validRels = rels.filter((r) => byName.has(r.from) && byName.has(r.to));
|
|
2091
2098
|
const g = new To.graphlib.Graph({ multigraph: true });
|
|
2092
|
-
g.setGraph({ rankdir: "LR", nodesep:
|
|
2099
|
+
g.setGraph({ rankdir: "LR", nodesep: 38, ranksep: 96, marginx: 18, marginy: 18 });
|
|
2093
2100
|
g.setDefaultEdgeLabel(() => ({}));
|
|
2094
2101
|
for (const b of boxes) g.setNode(b.name, { width: b.w, height: b.h });
|
|
2095
2102
|
validRels.forEach((r, i) => g.setEdge(r.from, r.to, {}, `e${i}`));
|
|
@@ -2097,30 +2104,36 @@ function renderErd(data) {
|
|
|
2097
2104
|
const graph = g.graph();
|
|
2098
2105
|
const W2 = Math.ceil(graph.width ?? 0);
|
|
2099
2106
|
const H2 = Math.ceil(graph.height ?? 0);
|
|
2100
|
-
const
|
|
2107
|
+
const at2 = /* @__PURE__ */ new Map();
|
|
2101
2108
|
for (const b of boxes) {
|
|
2102
2109
|
const n = g.node(b.name);
|
|
2103
|
-
|
|
2110
|
+
at2.set(b.name, { x: n.x - b.w / 2, y: n.y - b.h / 2 });
|
|
2104
2111
|
}
|
|
2105
2112
|
let s = `<svg viewBox="0 0 ${W2} ${H2}" role="img"><title>Entity-relationship diagram</title>`;
|
|
2106
|
-
validRels.forEach((r
|
|
2107
|
-
const
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
const
|
|
2111
|
-
|
|
2112
|
-
const
|
|
2113
|
-
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
const
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2113
|
+
validRels.forEach((r) => {
|
|
2114
|
+
const src = byName.get(r.from);
|
|
2115
|
+
const tgt = byName.get(r.to);
|
|
2116
|
+
const sp = at2.get(r.from);
|
|
2117
|
+
const tp = at2.get(r.to);
|
|
2118
|
+
if (!src || !tgt || !sp || !tp) return;
|
|
2119
|
+
const fkY = rowAnchorY(src, sp.y, pickFkIndex(src.cols, tgt.name));
|
|
2120
|
+
const pkY = rowAnchorY(tgt, tp.y, tgt.pkIdx);
|
|
2121
|
+
const rightward = tp.x + tgt.w / 2 >= sp.x + src.w / 2;
|
|
2122
|
+
const sx = rightward ? sp.x + src.w : sp.x;
|
|
2123
|
+
const tx = rightward ? tp.x : tp.x + tgt.w;
|
|
2124
|
+
const lo = Math.min(sx, tx) + 10;
|
|
2125
|
+
const hi = Math.max(sx, tx) - 10;
|
|
2126
|
+
const midX = hi > lo ? clamp((sx + tx) / 2, lo, hi) : (sx + tx) / 2;
|
|
2127
|
+
const card = parseCard(r.card);
|
|
2128
|
+
s += `<path d="M${round(sx)},${round(fkY)} H${round(midX)} V${round(pkY)} H${round(tx)}" fill="none" stroke="var(--gray)" stroke-width="1.5"/>` + crowFoot(sx, fkY, rightward ? 1 : -1, card.fromMany) + crowFoot(tx, pkY, rightward ? -1 : 1, card.toMany);
|
|
2129
|
+
if (r.label !== void 0 && r.label !== "") {
|
|
2130
|
+
const w2 = Math.max(30, r.label.length * 6.4);
|
|
2131
|
+
const cy = (fkY + pkY) / 2;
|
|
2132
|
+
s += `<rect x="${round(midX - w2 / 2)}" y="${round(cy - 9)}" width="${round(w2)}" height="18" rx="9" fill="var(--white)" stroke="var(--rule)"/><text x="${round(midX)}" y="${round(cy + 3)}" class="edge-label">${escapeHtml(r.label)}</text>`;
|
|
2120
2133
|
}
|
|
2121
2134
|
});
|
|
2122
2135
|
for (const b of boxes) {
|
|
2123
|
-
const p2 =
|
|
2136
|
+
const p2 = at2.get(b.name);
|
|
2124
2137
|
if (!p2) continue;
|
|
2125
2138
|
const { x: x2, y } = p2;
|
|
2126
2139
|
s += `<rect x="${round(x2)}" y="${round(y)}" width="${b.w}" height="${b.h}" rx="5" fill="var(--white)" stroke="var(--navy)"/><path d="M${round(x2)},${round(y + HEAD_H)} v${ -27} a5,5 0 0 1 5,-5 h${b.w - 10} a5,5 0 0 1 5,5 v${HEAD_H - 5} z" fill="var(--navy)"/><text x="${round(x2 + b.w / 2)}" y="${round(y + 21)}" class="er-head-text">${escapeHtml(b.name)}</text>`;
|
|
@@ -2152,16 +2165,37 @@ function renderErd(data) {
|
|
|
2152
2165
|
};
|
|
2153
2166
|
return diagramFrame(opts, s);
|
|
2154
2167
|
}
|
|
2155
|
-
function
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
const
|
|
2161
|
-
const
|
|
2162
|
-
|
|
2163
|
-
|
|
2168
|
+
function rowAnchorY(box, topY, idx) {
|
|
2169
|
+
if (idx >= 0 && idx < box.rows.length) return topY + HEAD_H + idx * ROW_H + ROW_H / 2;
|
|
2170
|
+
return topY + box.h / 2;
|
|
2171
|
+
}
|
|
2172
|
+
function pickFkIndex(columns, toName) {
|
|
2173
|
+
const fks = columns.map((c, i) => ({ c, i })).filter((x2) => x2.c.fk === true);
|
|
2174
|
+
const first = fks[0];
|
|
2175
|
+
if (first === void 0) return -1;
|
|
2176
|
+
const t = toName.toLowerCase();
|
|
2177
|
+
const singular = t.replace(/s$/, "");
|
|
2178
|
+
const match = fks.find((x2) => {
|
|
2179
|
+
const n = x2.c.name.toLowerCase();
|
|
2180
|
+
return n.includes(t) || n.includes(singular);
|
|
2181
|
+
});
|
|
2182
|
+
return (match ?? first).i;
|
|
2183
|
+
}
|
|
2184
|
+
function parseCard(card) {
|
|
2185
|
+
if (card === void 0) return { fromMany: true, toMany: false };
|
|
2186
|
+
const parts = card.split(":");
|
|
2187
|
+
const many = (p2) => p2 !== void 0 && p2.trim().toUpperCase() !== "1";
|
|
2188
|
+
return { fromMany: many(parts[0]), toMany: many(parts[1]) };
|
|
2164
2189
|
}
|
|
2190
|
+
function crowFoot(bx, y, outward, many) {
|
|
2191
|
+
if (many) {
|
|
2192
|
+
const ax = bx + outward * 14;
|
|
2193
|
+
return `<path d="M${round(ax)},${round(y)} L${round(bx)},${round(y - 7)} M${round(ax)},${round(y)} L${round(bx)},${round(y)} M${round(ax)},${round(y)} L${round(bx)},${round(y + 7)}" fill="none" stroke="var(--navy)" stroke-width="1.4" stroke-linecap="round"/>`;
|
|
2194
|
+
}
|
|
2195
|
+
const tx = bx + outward * 9;
|
|
2196
|
+
return `<line x1="${round(tx)}" y1="${round(y - 6)}" x2="${round(tx)}" y2="${round(y + 6)}" stroke="var(--navy)" stroke-width="1.4" stroke-linecap="round"/>`;
|
|
2197
|
+
}
|
|
2198
|
+
var clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
|
2165
2199
|
var round = (n) => Math.round(n * 10) / 10;
|
|
2166
2200
|
|
|
2167
2201
|
// src/blocks/kanban.ts
|
|
@@ -2698,6 +2732,51 @@ function edgePill(p2, label, err = false) {
|
|
|
2698
2732
|
return `<g><rect x="${p2.lx - w2 / 2}" y="${p2.ly - 9}" width="${w2}" height="18" rx="9" fill="#fff" stroke="#d1d5db"/><text x="${p2.lx}" y="${p2.ly + 3}" class="edge-label${errClass}">${escapeHtml(label)}</text></g>`;
|
|
2699
2733
|
}
|
|
2700
2734
|
|
|
2735
|
+
// src/blocks/autoLayout.ts
|
|
2736
|
+
function ensureGrid(items, edges, rankdir) {
|
|
2737
|
+
const allPlaced = items.length > 0 && items.every((n) => n.col !== void 0 && n.row !== void 0);
|
|
2738
|
+
if (allPlaced) {
|
|
2739
|
+
return items.map((n) => ({ ...n, col: n.col, row: n.row }));
|
|
2740
|
+
}
|
|
2741
|
+
const grid = autoGrid(
|
|
2742
|
+
items.map((n) => n.id),
|
|
2743
|
+
edges,
|
|
2744
|
+
rankdir
|
|
2745
|
+
);
|
|
2746
|
+
return items.map((n) => {
|
|
2747
|
+
const g = grid.get(n.id) ?? { col: 1, row: 1 };
|
|
2748
|
+
return { ...n, col: g.col, row: g.row };
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
function autoGrid(ids, edges, rankdir) {
|
|
2752
|
+
const idSet = new Set(ids);
|
|
2753
|
+
const g = new To.graphlib.Graph();
|
|
2754
|
+
g.setGraph({ rankdir, nodesep: 16, ranksep: 16, marginx: 0, marginy: 0 });
|
|
2755
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2756
|
+
for (const id of ids) g.setNode(id, { width: 10, height: 10 });
|
|
2757
|
+
for (const e of edges) {
|
|
2758
|
+
if (e.from !== e.to && idSet.has(e.from) && idSet.has(e.to)) g.setEdge(e.from, e.to);
|
|
2759
|
+
}
|
|
2760
|
+
To.layout(g);
|
|
2761
|
+
const pos = ids.map((id) => {
|
|
2762
|
+
const n = g.node(id);
|
|
2763
|
+
return { id, x: Math.round(n?.x ?? 0), y: Math.round(n?.y ?? 0) };
|
|
2764
|
+
});
|
|
2765
|
+
const out = /* @__PURE__ */ new Map();
|
|
2766
|
+
if (rankdir === "TB") {
|
|
2767
|
+
const ranks = [...new Set(pos.map((p2) => p2.y))].sort((a, b) => a - b);
|
|
2768
|
+
ranks.forEach((yv, ri) => {
|
|
2769
|
+
pos.filter((p2) => p2.y === yv).sort((a, b) => a.x - b.x).forEach((p2, ci) => out.set(p2.id, { col: ci + 1, row: ri + 1 }));
|
|
2770
|
+
});
|
|
2771
|
+
} else {
|
|
2772
|
+
const ranks = [...new Set(pos.map((p2) => p2.x))].sort((a, b) => a - b);
|
|
2773
|
+
ranks.forEach((xv, ci) => {
|
|
2774
|
+
pos.filter((p2) => p2.x === xv).sort((a, b) => a.y - b.y).forEach((p2, ri) => out.set(p2.id, { col: ci + 1, row: ri + 1 }));
|
|
2775
|
+
});
|
|
2776
|
+
}
|
|
2777
|
+
return out;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2701
2780
|
// src/blocks/flow.ts
|
|
2702
2781
|
function flowStyle(kind) {
|
|
2703
2782
|
switch (kind ?? "process") {
|
|
@@ -2713,8 +2792,8 @@ function flowStyle(kind) {
|
|
|
2713
2792
|
}
|
|
2714
2793
|
var ERR_LABEL_RE = /^(no|fail|error|reject)/i;
|
|
2715
2794
|
function renderFlowSvg(data) {
|
|
2716
|
-
const nodes = data.nodes ?? [];
|
|
2717
2795
|
const edges = data.edges ?? [];
|
|
2796
|
+
const nodes = ensureGrid(data.nodes ?? [], edges, "TB");
|
|
2718
2797
|
const cellW = 176;
|
|
2719
2798
|
const cellH = 70;
|
|
2720
2799
|
const gapX = 60;
|
|
@@ -2805,8 +2884,8 @@ function pillCls(kind) {
|
|
|
2805
2884
|
return "pill pill-active";
|
|
2806
2885
|
}
|
|
2807
2886
|
function renderState(data) {
|
|
2808
|
-
const states = data.states ?? [];
|
|
2809
2887
|
const trans = data.transitions ?? [];
|
|
2888
|
+
const states = ensureGrid(data.states ?? [], trans, "LR");
|
|
2810
2889
|
const cellW = 168;
|
|
2811
2890
|
const cellH = 64;
|
|
2812
2891
|
const gapX = 74;
|
|
@@ -2878,8 +2957,8 @@ function dfdStyle(kind) {
|
|
|
2878
2957
|
}
|
|
2879
2958
|
}
|
|
2880
2959
|
function renderDfd(data) {
|
|
2881
|
-
const nodes = data.nodes ?? [];
|
|
2882
2960
|
const edges = data.edges ?? [];
|
|
2961
|
+
const nodes = ensureGrid(data.nodes ?? [], edges, "LR");
|
|
2883
2962
|
const cellW = 168;
|
|
2884
2963
|
const cellH = 76;
|
|
2885
2964
|
const gapX = 60;
|
|
@@ -3401,8 +3480,8 @@ var LEGEND = [
|
|
|
3401
3480
|
{ sw: "#f3f4f6", label: "External" }
|
|
3402
3481
|
];
|
|
3403
3482
|
function renderC4(data) {
|
|
3404
|
-
const nodes = data.nodes ?? [];
|
|
3405
3483
|
const edges = data.edges ?? [];
|
|
3484
|
+
const nodes = ensureGrid(data.nodes ?? [], edges, "TB");
|
|
3406
3485
|
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
3407
3486
|
const cellW = 212;
|
|
3408
3487
|
const cellH = 102;
|
|
@@ -3501,8 +3580,8 @@ function umlRel(kind) {
|
|
|
3501
3580
|
}
|
|
3502
3581
|
}
|
|
3503
3582
|
function renderUml(data) {
|
|
3504
|
-
const classes = data.classes ?? [];
|
|
3505
3583
|
const rels = data.rels ?? [];
|
|
3584
|
+
const classes = ensureGrid(data.classes ?? [], rels, "TB");
|
|
3506
3585
|
const colW = 204;
|
|
3507
3586
|
const gapX = 64;
|
|
3508
3587
|
const gapY = 50;
|