@almadar/ui 5.116.1 → 5.118.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.
@@ -34744,6 +34744,23 @@ function getGroupColor(group, groups) {
34744
34744
  const idx = groups.indexOf(group);
34745
34745
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
34746
34746
  }
34747
+ function hashSeed(str) {
34748
+ let h = 1779033703 ^ str.length;
34749
+ for (let i = 0; i < str.length; i++) {
34750
+ h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
34751
+ h = h << 13 | h >>> 19;
34752
+ }
34753
+ return (h ^= h >>> 16) >>> 0;
34754
+ }
34755
+ function mulberry32(a) {
34756
+ return function() {
34757
+ a |= 0;
34758
+ a = a + 1831565813 | 0;
34759
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
34760
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
34761
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
34762
+ };
34763
+ }
34747
34764
  function resolveColor3(color, el) {
34748
34765
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
34749
34766
  if (!m) return color;
@@ -34775,6 +34792,7 @@ var init_GraphCanvas = __esm({
34775
34792
  title,
34776
34793
  nodes: propNodes = [],
34777
34794
  edges: propEdges = [],
34795
+ similarity: propSimilarity = [],
34778
34796
  height = 400,
34779
34797
  showLabels = true,
34780
34798
  interactive = true,
@@ -34806,6 +34824,7 @@ var init_GraphCanvas = __esm({
34806
34824
  offsetRef.current = offset;
34807
34825
  const [hoveredNode, setHoveredNode] = React81.useState(null);
34808
34826
  const nodesRef = React81.useRef([]);
34827
+ const laidOutRef = React81.useRef(false);
34809
34828
  const [, forceUpdate] = React81.useState(0);
34810
34829
  const [logicalW, setLogicalW] = React81.useState(800);
34811
34830
  React81.useEffect(() => {
@@ -34894,13 +34913,19 @@ var init_GraphCanvas = __esm({
34894
34913
  x = gapX * (idx % cols + 1);
34895
34914
  y = gapY * (Math.floor(idx / cols) + 1);
34896
34915
  } else {
34897
- x = w * 0.2 + Math.random() * w * 0.6;
34898
- y = h * 0.2 + Math.random() * h * 0.6;
34916
+ const rand = mulberry32(hashSeed(n.id));
34917
+ x = w * 0.2 + rand() * w * 0.6;
34918
+ y = h * 0.2 + rand() * h * 0.6;
34899
34919
  }
34900
34920
  }
34901
34921
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
34902
34922
  });
34903
34923
  nodesRef.current = simNodes;
34924
+ const prevIds = /* @__PURE__ */ new Set([...prevPos.keys()]);
34925
+ const newIdList = simNodes.map((n) => n.id);
34926
+ const kept = newIdList.filter((id) => prevIds.has(id)).length;
34927
+ const overlap = prevIds.size === 0 ? 0 : kept / Math.max(prevIds.size, newIdList.length);
34928
+ const fullRelayout = !laidOutRef.current || overlap < 0.5;
34904
34929
  if (layout === "force") {
34905
34930
  const maxIterations = 300;
34906
34931
  const tick = () => {
@@ -34925,22 +34950,43 @@ var init_GraphCanvas = __esm({
34925
34950
  nodes[j].fy += fy;
34926
34951
  }
34927
34952
  }
34928
- for (const edge of propEdges) {
34929
- const source = nodes.find((n) => n.id === edge.source);
34930
- const target = nodes.find((n) => n.id === edge.target);
34931
- if (!source || !target) continue;
34932
- const dx = target.x - source.x;
34933
- const dy = target.y - source.y;
34934
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34935
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34936
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
34937
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34938
- const fx = dx / dist * force;
34939
- const fy = dy / dist * force;
34940
- source.fx += fx;
34941
- source.fy += fy;
34942
- target.fx -= fx;
34943
- target.fy -= fy;
34953
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
34954
+ if (propSimilarity.length > 0) {
34955
+ for (const pair of propSimilarity) {
34956
+ const source = nodeById.get(pair.source);
34957
+ const target = nodeById.get(pair.target);
34958
+ if (!source || !target) continue;
34959
+ const dx = target.x - source.x;
34960
+ const dy = target.y - source.y;
34961
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34962
+ const w2 = Math.min(1, Math.max(0, pair.weight));
34963
+ const linkTarget = linkDistance * (1 - w2);
34964
+ const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34965
+ const fx = dx / dist * force;
34966
+ const fy = dy / dist * force;
34967
+ source.fx += fx;
34968
+ source.fy += fy;
34969
+ target.fx -= fx;
34970
+ target.fy -= fy;
34971
+ }
34972
+ } else {
34973
+ for (const edge of propEdges) {
34974
+ const source = nodeById.get(edge.source);
34975
+ const target = nodeById.get(edge.target);
34976
+ if (!source || !target) continue;
34977
+ const dx = target.x - source.x;
34978
+ const dy = target.y - source.y;
34979
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34980
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34981
+ const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
34982
+ const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34983
+ const fx = dx / dist * force;
34984
+ const fy = dy / dist * force;
34985
+ source.fx += fx;
34986
+ source.fy += fy;
34987
+ target.fx -= fx;
34988
+ target.fy -= fy;
34989
+ }
34944
34990
  }
34945
34991
  const centroids = /* @__PURE__ */ new Map();
34946
34992
  for (const node of nodes) {
@@ -35007,7 +35053,38 @@ var init_GraphCanvas = __esm({
35007
35053
  }
35008
35054
  }
35009
35055
  };
35010
- for (let i = 0; i < maxIterations; i++) tick();
35056
+ if (fullRelayout) {
35057
+ for (let i = 0; i < maxIterations; i++) tick();
35058
+ laidOutRef.current = true;
35059
+ } else {
35060
+ const centroids = /* @__PURE__ */ new Map();
35061
+ for (const node of simNodes) {
35062
+ if (!prevPos.has(node.id)) continue;
35063
+ const g = node.group ?? "__none__";
35064
+ let c = centroids.get(g);
35065
+ if (!c) {
35066
+ c = { x: 0, y: 0, n: 0 };
35067
+ centroids.set(g, c);
35068
+ }
35069
+ c.x += node.x;
35070
+ c.y += node.y;
35071
+ c.n += 1;
35072
+ }
35073
+ const ringCount = /* @__PURE__ */ new Map();
35074
+ for (const node of simNodes) {
35075
+ if (prevPos.has(node.id)) continue;
35076
+ const g = node.group ?? "__none__";
35077
+ const c = centroids.get(g);
35078
+ const cx = c && c.n > 0 ? c.x / c.n : w / 2;
35079
+ const cy = c && c.n > 0 ? c.y / c.n : h / 2;
35080
+ const k = ringCount.get(g) ?? 0;
35081
+ ringCount.set(g, k + 1);
35082
+ const angle = k * 2.399963;
35083
+ const r = 22 + k * 8;
35084
+ node.x = Math.max(30, Math.min(w - 30, cx + r * Math.cos(angle)));
35085
+ node.y = Math.max(30, Math.min(h - 30, cy + r * Math.sin(angle)));
35086
+ }
35087
+ }
35011
35088
  forceUpdate((n) => n + 1);
35012
35089
  } else {
35013
35090
  forceUpdate((n) => n + 1);
@@ -35015,7 +35092,7 @@ var init_GraphCanvas = __esm({
35015
35092
  return () => {
35016
35093
  cancelAnimationFrame(animRef.current);
35017
35094
  };
35018
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
35095
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35019
35096
  React81.useEffect(() => {
35020
35097
  const canvas = canvasRef.current;
35021
35098
  if (!canvas) return;
@@ -34700,6 +34700,23 @@ 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
+ }
34703
34720
  function resolveColor3(color, el) {
34704
34721
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
34705
34722
  if (!m) return color;
@@ -34731,6 +34748,7 @@ var init_GraphCanvas = __esm({
34731
34748
  title,
34732
34749
  nodes: propNodes = [],
34733
34750
  edges: propEdges = [],
34751
+ similarity: propSimilarity = [],
34734
34752
  height = 400,
34735
34753
  showLabels = true,
34736
34754
  interactive = true,
@@ -34762,6 +34780,7 @@ var init_GraphCanvas = __esm({
34762
34780
  offsetRef.current = offset;
34763
34781
  const [hoveredNode, setHoveredNode] = useState(null);
34764
34782
  const nodesRef = useRef([]);
34783
+ const laidOutRef = useRef(false);
34765
34784
  const [, forceUpdate] = useState(0);
34766
34785
  const [logicalW, setLogicalW] = useState(800);
34767
34786
  useEffect(() => {
@@ -34850,13 +34869,19 @@ var init_GraphCanvas = __esm({
34850
34869
  x = gapX * (idx % cols + 1);
34851
34870
  y = gapY * (Math.floor(idx / cols) + 1);
34852
34871
  } else {
34853
- x = w * 0.2 + Math.random() * w * 0.6;
34854
- y = h * 0.2 + Math.random() * h * 0.6;
34872
+ const rand = mulberry32(hashSeed(n.id));
34873
+ x = w * 0.2 + rand() * w * 0.6;
34874
+ y = h * 0.2 + rand() * h * 0.6;
34855
34875
  }
34856
34876
  }
34857
34877
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
34858
34878
  });
34859
34879
  nodesRef.current = simNodes;
34880
+ const prevIds = /* @__PURE__ */ new Set([...prevPos.keys()]);
34881
+ const newIdList = simNodes.map((n) => n.id);
34882
+ const kept = newIdList.filter((id) => prevIds.has(id)).length;
34883
+ const overlap = prevIds.size === 0 ? 0 : kept / Math.max(prevIds.size, newIdList.length);
34884
+ const fullRelayout = !laidOutRef.current || overlap < 0.5;
34860
34885
  if (layout === "force") {
34861
34886
  const maxIterations = 300;
34862
34887
  const tick = () => {
@@ -34881,22 +34906,43 @@ var init_GraphCanvas = __esm({
34881
34906
  nodes[j].fy += fy;
34882
34907
  }
34883
34908
  }
34884
- for (const edge of propEdges) {
34885
- const source = nodes.find((n) => n.id === edge.source);
34886
- const target = nodes.find((n) => n.id === edge.target);
34887
- if (!source || !target) continue;
34888
- const dx = target.x - source.x;
34889
- const dy = target.y - source.y;
34890
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34891
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34892
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
34893
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34894
- const fx = dx / dist * force;
34895
- const fy = dy / dist * force;
34896
- source.fx += fx;
34897
- source.fy += fy;
34898
- target.fx -= fx;
34899
- target.fy -= fy;
34909
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
34910
+ if (propSimilarity.length > 0) {
34911
+ for (const pair of propSimilarity) {
34912
+ const source = nodeById.get(pair.source);
34913
+ const target = nodeById.get(pair.target);
34914
+ if (!source || !target) continue;
34915
+ const dx = target.x - source.x;
34916
+ const dy = target.y - source.y;
34917
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34918
+ const w2 = Math.min(1, Math.max(0, pair.weight));
34919
+ const linkTarget = linkDistance * (1 - w2);
34920
+ const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34921
+ const fx = dx / dist * force;
34922
+ const fy = dy / dist * force;
34923
+ source.fx += fx;
34924
+ source.fy += fy;
34925
+ target.fx -= fx;
34926
+ target.fy -= fy;
34927
+ }
34928
+ } else {
34929
+ for (const edge of propEdges) {
34930
+ const source = nodeById.get(edge.source);
34931
+ const target = nodeById.get(edge.target);
34932
+ if (!source || !target) continue;
34933
+ const dx = target.x - source.x;
34934
+ const dy = target.y - source.y;
34935
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34936
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34937
+ const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
34938
+ const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34939
+ const fx = dx / dist * force;
34940
+ const fy = dy / dist * force;
34941
+ source.fx += fx;
34942
+ source.fy += fy;
34943
+ target.fx -= fx;
34944
+ target.fy -= fy;
34945
+ }
34900
34946
  }
34901
34947
  const centroids = /* @__PURE__ */ new Map();
34902
34948
  for (const node of nodes) {
@@ -34963,7 +35009,38 @@ var init_GraphCanvas = __esm({
34963
35009
  }
34964
35010
  }
34965
35011
  };
34966
- for (let i = 0; i < maxIterations; i++) tick();
35012
+ if (fullRelayout) {
35013
+ for (let i = 0; i < maxIterations; i++) tick();
35014
+ laidOutRef.current = true;
35015
+ } else {
35016
+ const centroids = /* @__PURE__ */ new Map();
35017
+ for (const node of simNodes) {
35018
+ if (!prevPos.has(node.id)) continue;
35019
+ const g = node.group ?? "__none__";
35020
+ let c = centroids.get(g);
35021
+ if (!c) {
35022
+ c = { x: 0, y: 0, n: 0 };
35023
+ centroids.set(g, c);
35024
+ }
35025
+ c.x += node.x;
35026
+ c.y += node.y;
35027
+ c.n += 1;
35028
+ }
35029
+ const ringCount = /* @__PURE__ */ new Map();
35030
+ for (const node of simNodes) {
35031
+ if (prevPos.has(node.id)) continue;
35032
+ const g = node.group ?? "__none__";
35033
+ const c = centroids.get(g);
35034
+ const cx = c && c.n > 0 ? c.x / c.n : w / 2;
35035
+ const cy = c && c.n > 0 ? c.y / c.n : h / 2;
35036
+ const k = ringCount.get(g) ?? 0;
35037
+ ringCount.set(g, k + 1);
35038
+ const angle = k * 2.399963;
35039
+ const r = 22 + k * 8;
35040
+ node.x = Math.max(30, Math.min(w - 30, cx + r * Math.cos(angle)));
35041
+ node.y = Math.max(30, Math.min(h - 30, cy + r * Math.sin(angle)));
35042
+ }
35043
+ }
34967
35044
  forceUpdate((n) => n + 1);
34968
35045
  } else {
34969
35046
  forceUpdate((n) => n + 1);
@@ -34971,7 +35048,7 @@ var init_GraphCanvas = __esm({
34971
35048
  return () => {
34972
35049
  cancelAnimationFrame(animRef.current);
34973
35050
  };
34974
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
35051
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
34975
35052
  useEffect(() => {
34976
35053
  const canvas = canvasRef.current;
34977
35054
  if (!canvas) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.116.1",
3
+ "version": "5.118.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [