@avodado/render 0.1.1 → 0.1.2

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 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
- const h = HEAD_H + bodyRows * ROW_H + BOT_PAD;
2087
- return { name: e.name, rows, hidden, w: COL_W, h };
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: 34, ranksep: 86, marginx: 18, marginy: 18 });
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,35 @@ 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 topLeft = /* @__PURE__ */ new Map();
2107
+ const at2 = /* @__PURE__ */ new Map();
2101
2108
  for (const b of boxes) {
2102
2109
  const n = g.node(b.name);
2103
- topLeft.set(b.name, { x: n.x - b.w / 2, y: n.y - b.h / 2 });
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, i) => {
2107
- const e = g.edge(r.from, r.to, `e${i}`);
2108
- const pts = e?.points;
2109
- if (!pts || pts.length < 2) return;
2110
- const first = pts[0];
2111
- const last = pts[pts.length - 1];
2112
- const prev = pts[pts.length - 2];
2113
- if (first === void 0 || last === void 0 || prev === void 0) return;
2114
- const line = pts.map((p2) => `${round(p2.x)},${round(p2.y)}`).join(" ");
2115
- s += `<polyline points="${line}" fill="none" stroke="var(--gray)" stroke-width="1.5"/><circle cx="${round(first.x)}" cy="${round(first.y)}" r="2.6" fill="var(--gray)"/>` + arrowHead(prev, last);
2116
- const mid = r.card !== void 0 ? pts[Math.floor(pts.length / 2)] : void 0;
2117
- if (r.card !== void 0 && mid !== void 0) {
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
+ 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"/><circle cx="${round(sx)}" cy="${round(fkY)}" r="2.6" fill="var(--gray)"/>` + arrowHeadH(tx, pkY, rightward);
2128
+ if (r.card !== void 0) {
2118
2129
  const w2 = 30;
2119
- s += `<rect x="${round(mid.x - w2 / 2)}" y="${round(mid.y - 9)}" width="${w2}" height="18" rx="9" fill="var(--white)" stroke="var(--rule)"/><text x="${round(mid.x)}" y="${round(mid.y + 3)}" class="edge-label">${escapeHtml(r.card)}</text>`;
2130
+ const cy = (fkY + pkY) / 2;
2131
+ s += `<rect x="${round(midX - w2 / 2)}" y="${round(cy - 9)}" width="${w2}" height="18" rx="9" fill="var(--white)" stroke="var(--rule)"/><text x="${round(midX)}" y="${round(cy + 3)}" class="edge-label">${escapeHtml(r.card)}</text>`;
2120
2132
  }
2121
2133
  });
2122
2134
  for (const b of boxes) {
2123
- const p2 = topLeft.get(b.name);
2135
+ const p2 = at2.get(b.name);
2124
2136
  if (!p2) continue;
2125
2137
  const { x: x2, y } = p2;
2126
2138
  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 +2164,27 @@ function renderErd(data) {
2152
2164
  };
2153
2165
  return diagramFrame(opts, s);
2154
2166
  }
2155
- function arrowHead(from, to) {
2156
- const ang = Math.atan2(to.y - from.y, to.x - from.x);
2157
- const len = 10;
2158
- const spread = 0.42;
2159
- const b1x = to.x - len * Math.cos(ang - spread);
2160
- const b1y = to.y - len * Math.sin(ang - spread);
2161
- const b2x = to.x - len * Math.cos(ang + spread);
2162
- const b2y = to.y - len * Math.sin(ang + spread);
2163
- return `<path d="M${round(b1x)},${round(b1y)} L${round(to.x)},${round(to.y)} L${round(b2x)},${round(b2y)}" fill="none" stroke="var(--navy)" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>`;
2167
+ function rowAnchorY(box, topY, idx) {
2168
+ if (idx >= 0 && idx < box.rows.length) return topY + HEAD_H + idx * ROW_H + ROW_H / 2;
2169
+ return topY + box.h / 2;
2170
+ }
2171
+ function pickFkIndex(columns, toName) {
2172
+ const fks = columns.map((c, i) => ({ c, i })).filter((x2) => x2.c.fk === true);
2173
+ const first = fks[0];
2174
+ if (first === void 0) return -1;
2175
+ const t = toName.toLowerCase();
2176
+ const singular = t.replace(/s$/, "");
2177
+ const match = fks.find((x2) => {
2178
+ const n = x2.c.name.toLowerCase();
2179
+ return n.includes(t) || n.includes(singular);
2180
+ });
2181
+ return (match ?? first).i;
2182
+ }
2183
+ function arrowHeadH(x2, y, pointRight) {
2184
+ const dx = pointRight ? -10 : 10;
2185
+ return `<path d="M${round(x2 + dx)},${round(y - 5)} L${round(x2)},${round(y)} L${round(x2 + dx)},${round(y + 5)}" fill="none" stroke="var(--navy)" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>`;
2164
2186
  }
2187
+ var clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
2165
2188
  var round = (n) => Math.round(n * 10) / 10;
2166
2189
 
2167
2190
  // src/blocks/kanban.ts