@almadar/ui 5.117.0 → 5.119.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.
@@ -34688,11 +34688,11 @@ var init_DocumentViewer = __esm({
34688
34688
  DocumentViewer.displayName = "DocumentViewer";
34689
34689
  }
34690
34690
  });
34691
- function measureLabelWidth(text) {
34691
+ function measureLabelWidth(text, fontFamily = "system-ui") {
34692
34692
  if (typeof document === "undefined") return text.length * 6;
34693
34693
  if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
34694
34694
  if (!labelMeasureCtx) return text.length * 6;
34695
- labelMeasureCtx.font = "12px system-ui";
34695
+ labelMeasureCtx.font = `12px ${fontFamily}`;
34696
34696
  return labelMeasureCtx.measureText(text).width + 4;
34697
34697
  }
34698
34698
  function getGroupColor(group, groups) {
@@ -34700,6 +34700,26 @@ function getGroupColor(group, groups) {
34700
34700
  const idx = groups.indexOf(group);
34701
34701
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
34702
34702
  }
34703
+ function hashSeed(str) {
34704
+ let h = 1779033703 ^ str.length;
34705
+ for (let i = 0; i < str.length; i++) {
34706
+ h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
34707
+ h = h << 13 | h >>> 19;
34708
+ }
34709
+ return (h ^= h >>> 16) >>> 0;
34710
+ }
34711
+ function mulberry32(a) {
34712
+ return function() {
34713
+ a |= 0;
34714
+ a = a + 1831565813 | 0;
34715
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
34716
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
34717
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
34718
+ };
34719
+ }
34720
+ function edgeKeyOf(a, b) {
34721
+ return a < b ? `${a}\0${b}` : `${b}\0${a}`;
34722
+ }
34703
34723
  function resolveColor3(color, el) {
34704
34724
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
34705
34725
  if (!m) return color;
@@ -34731,6 +34751,7 @@ var init_GraphCanvas = __esm({
34731
34751
  title,
34732
34752
  nodes: propNodes = [],
34733
34753
  edges: propEdges = [],
34754
+ similarity: propSimilarity = [],
34734
34755
  height = 400,
34735
34756
  showLabels = true,
34736
34757
  interactive = true,
@@ -34832,6 +34853,7 @@ var init_GraphCanvas = __esm({
34832
34853
  if (!canvas || propNodes.length === 0) return;
34833
34854
  const w = logicalW;
34834
34855
  const h = height;
34856
+ const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
34835
34857
  const prevPos = /* @__PURE__ */ new Map();
34836
34858
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
34837
34859
  const simNodes = propNodes.map((n, idx) => {
@@ -34851,8 +34873,9 @@ var init_GraphCanvas = __esm({
34851
34873
  x = gapX * (idx % cols + 1);
34852
34874
  y = gapY * (Math.floor(idx / cols) + 1);
34853
34875
  } else {
34854
- x = w * 0.2 + Math.random() * w * 0.6;
34855
- y = h * 0.2 + Math.random() * h * 0.6;
34876
+ const rand = mulberry32(hashSeed(n.id));
34877
+ x = w * 0.2 + rand() * w * 0.6;
34878
+ y = h * 0.2 + rand() * h * 0.6;
34856
34879
  }
34857
34880
  }
34858
34881
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
@@ -34865,10 +34888,15 @@ var init_GraphCanvas = __esm({
34865
34888
  const fullRelayout = !laidOutRef.current || overlap < 0.5;
34866
34889
  if (layout === "force") {
34867
34890
  const maxIterations = 300;
34868
- const tick = () => {
34891
+ const COOL = 0.12;
34892
+ const COLLIDE_PASSES = 6;
34893
+ const LABEL_GAP = 12;
34894
+ const LABEL_H = 16;
34895
+ const tick = (iter) => {
34869
34896
  const nodes = nodesRef.current;
34870
34897
  const centerX = w / 2;
34871
34898
  const centerY = h / 2;
34899
+ const temp = Math.max(COOL, 1 - iter / maxIterations);
34872
34900
  for (const node of nodes) {
34873
34901
  node.fx = 0;
34874
34902
  node.fy = 0;
@@ -34878,7 +34906,7 @@ var init_GraphCanvas = __esm({
34878
34906
  const dx = nodes[j].x - nodes[i].x;
34879
34907
  const dy = nodes[j].y - nodes[i].y;
34880
34908
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34881
- const force = repulsion / (dist * dist);
34909
+ const force = repulsion / (dist * dist) * temp;
34882
34910
  const fx = dx / dist * force;
34883
34911
  const fy = dy / dist * force;
34884
34912
  nodes[i].fx -= fx;
@@ -34887,22 +34915,35 @@ var init_GraphCanvas = __esm({
34887
34915
  nodes[j].fy += fy;
34888
34916
  }
34889
34917
  }
34890
- for (const edge of propEdges) {
34891
- const source = nodes.find((n) => n.id === edge.source);
34892
- const target = nodes.find((n) => n.id === edge.target);
34893
- if (!source || !target) continue;
34894
- const dx = target.x - source.x;
34895
- const dy = target.y - source.y;
34918
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
34919
+ const spring = (a, b, rest, k) => {
34920
+ const dx = b.x - a.x;
34921
+ const dy = b.y - a.y;
34896
34922
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34897
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34898
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
34899
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34923
+ const force = (dist - rest) * k * temp;
34900
34924
  const fx = dx / dist * force;
34901
34925
  const fy = dy / dist * force;
34902
- source.fx += fx;
34903
- source.fy += fy;
34904
- target.fx -= fx;
34905
- target.fy -= fy;
34926
+ a.fx += fx;
34927
+ a.fy += fy;
34928
+ b.fx -= fx;
34929
+ b.fy -= fy;
34930
+ };
34931
+ const drawnPairs = /* @__PURE__ */ new Set();
34932
+ for (const edge of propEdges) {
34933
+ const source = nodeById.get(edge.source);
34934
+ const target = nodeById.get(edge.target);
34935
+ if (!source || !target) continue;
34936
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
34937
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34938
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
34939
+ }
34940
+ for (const pair of propSimilarity) {
34941
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
34942
+ const source = nodeById.get(pair.source);
34943
+ const target = nodeById.get(pair.target);
34944
+ if (!source || !target) continue;
34945
+ const w2 = Math.min(1, Math.max(0, pair.weight));
34946
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
34906
34947
  }
34907
34948
  const centroids = /* @__PURE__ */ new Map();
34908
34949
  for (const node of nodes) {
@@ -34916,14 +34957,20 @@ var init_GraphCanvas = __esm({
34916
34957
  c.y += node.y;
34917
34958
  c.n += 1;
34918
34959
  }
34960
+ const multiCluster = centroids.size > 1;
34919
34961
  for (const node of nodes) {
34920
- const c = centroids.get(node.group ?? "__none__");
34921
- if (c && c.n > 1) {
34922
- node.fx += (c.x / c.n - node.x) * 0.04;
34923
- node.fy += (c.y / c.n - node.y) * 0.04;
34962
+ if (multiCluster) {
34963
+ const c = centroids.get(node.group ?? "__none__");
34964
+ if (c && c.n > 1) {
34965
+ node.fx += (c.x / c.n - node.x) * 0.04 * temp;
34966
+ node.fy += (c.y / c.n - node.y) * 0.04 * temp;
34967
+ } else {
34968
+ node.fx += (centerX - node.x) * 0.01 * temp;
34969
+ node.fy += (centerY - node.y) * 0.01 * temp;
34970
+ }
34924
34971
  } else {
34925
- node.fx += (centerX - node.x) * 0.01;
34926
- node.fy += (centerY - node.y) * 0.01;
34972
+ node.fx += (centerX - node.x) * 8e-3 * temp;
34973
+ node.fy += (centerY - node.y) * 8e-3 * temp;
34927
34974
  }
34928
34975
  }
34929
34976
  const damping = 0.9;
@@ -34935,42 +34982,53 @@ var init_GraphCanvas = __esm({
34935
34982
  node.x = Math.max(30, Math.min(w - 30, node.x));
34936
34983
  node.y = Math.max(30, Math.min(h - 30, node.y));
34937
34984
  }
34938
- const LABEL_GAP = 12;
34939
- const LABEL_H = 16;
34940
34985
  const pad = nodeSpacing / 2;
34941
- for (let i = 0; i < nodes.length; i++) {
34942
- for (let j = i + 1; j < nodes.length; j++) {
34943
- const a = nodes[i];
34944
- const b = nodes[j];
34945
- const ar = a.size || 8;
34946
- const br = b.size || 8;
34947
- if (showLabels) {
34948
- if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
34949
- if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
34950
- }
34951
- const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
34952
- const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
34953
- const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
34954
- const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
34955
- const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
34956
- const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
34957
- const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
34958
- if (overlapX > 0 && overlapY > 0) {
34959
- if (overlapX <= overlapY) {
34960
- const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
34961
- a.x = Math.max(30, Math.min(w - 30, a.x - push));
34962
- b.x = Math.max(30, Math.min(w - 30, b.x + push));
34963
- } else {
34964
- const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
34965
- a.y = Math.max(30, Math.min(h - 30, a.y - push));
34966
- b.y = Math.max(30, Math.min(h - 30, b.y + push));
34986
+ const rectsOf = (n) => {
34987
+ const r = n.size || 8;
34988
+ const lw = showLabels ? n.labelW ?? (n.labelW = n.label ? measureLabelWidth(n.label, labelFont) : 0) : 0;
34989
+ return {
34990
+ circle: [n.x - r - pad, n.y - r - pad, n.x + r + pad, n.y + r + pad],
34991
+ label: [n.x - lw / 2 - pad, n.y + r + LABEL_GAP - 8, n.x + lw / 2 + pad, n.y + r + LABEL_GAP + LABEL_H]
34992
+ };
34993
+ };
34994
+ const sep = (r1, r2) => {
34995
+ const ox = Math.min(r1[2], r2[2]) - Math.max(r1[0], r2[0]);
34996
+ const oy = Math.min(r1[3], r2[3]) - Math.max(r1[1], r2[1]);
34997
+ if (ox <= 0 || oy <= 0) return null;
34998
+ return ox <= oy ? { axis: "x", depth: ox, sign: r1[0] + r1[2] < r2[0] + r2[2] ? 1 : -1 } : { axis: "y", depth: oy, sign: r1[1] + r1[3] < r2[1] + r2[3] ? 1 : -1 };
34999
+ };
35000
+ for (let pass = 0; pass < COLLIDE_PASSES; pass++) {
35001
+ for (let i = 0; i < nodes.length; i++) {
35002
+ for (let j = i + 1; j < nodes.length; j++) {
35003
+ const a = nodes[i];
35004
+ const b = nodes[j];
35005
+ const ra = rectsOf(a);
35006
+ const rb = rectsOf(b);
35007
+ let best = null;
35008
+ for (const [r1, r2] of [[ra.circle, rb.circle], [ra.circle, rb.label], [ra.label, rb.circle], [ra.label, rb.label]]) {
35009
+ const s = sep(r1, r2);
35010
+ if (s && (!best || s.depth < best.depth)) best = s;
35011
+ }
35012
+ if (best) {
35013
+ const push = best.depth / 2;
35014
+ if (best.axis === "x") {
35015
+ a.x = Math.max(30, Math.min(w - 30, a.x - push * best.sign));
35016
+ b.x = Math.max(30, Math.min(w - 30, b.x + push * best.sign));
35017
+ a.vx = 0;
35018
+ b.vx = 0;
35019
+ } else {
35020
+ a.y = Math.max(30, Math.min(h - 30, a.y - push * best.sign));
35021
+ b.y = Math.max(30, Math.min(h - 30, b.y + push * best.sign));
35022
+ a.vy = 0;
35023
+ b.vy = 0;
35024
+ }
34967
35025
  }
34968
35026
  }
34969
35027
  }
34970
35028
  }
34971
35029
  };
34972
35030
  if (fullRelayout) {
34973
- for (let i = 0; i < maxIterations; i++) tick();
35031
+ for (let i = 0; i < maxIterations; i++) tick(i);
34974
35032
  laidOutRef.current = true;
34975
35033
  } else {
34976
35034
  const centroids = /* @__PURE__ */ new Map();
@@ -35008,7 +35066,7 @@ var init_GraphCanvas = __esm({
35008
35066
  return () => {
35009
35067
  cancelAnimationFrame(animRef.current);
35010
35068
  };
35011
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35069
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35012
35070
  useEffect(() => {
35013
35071
  const canvas = canvasRef.current;
35014
35072
  if (!canvas) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.117.0",
3
+ "version": "5.119.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [