@euphrasiologist/lwphylo 1.4.0 → 1.5.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/README.md +3 -2
- package/dist/drawPhylogeny.cjs +29 -4
- package/dist/drawPhylogeny.cjs.map +1 -1
- package/dist/drawPhylogeny.esm.js +29 -4
- package/dist/drawPhylogeny.esm.js.map +1 -1
- package/dist/drawPhylogeny.umd.js +1 -1
- package/dist/drawPhylogeny.umd.js.map +1 -1
- package/dist/index.cjs +29 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +29 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -925,8 +925,12 @@ function addScaleBar(svg, { scale, basis, defaultX, defaultY, scaleBar, fontSize
|
|
|
925
925
|
.text(opts.label ?? String(length));
|
|
926
926
|
}
|
|
927
927
|
|
|
928
|
+
// input: a Newick string, or an already-parsed tree (the node shape returned
|
|
929
|
+
// by readTree()/randomTree()). Passing the same parsed tree object back in
|
|
930
|
+
// across re-renders (e.g. after mutating it with rotate()) keeps node ids
|
|
931
|
+
// stable, which onNodeClick below relies on.
|
|
928
932
|
function drawPhylogeny(
|
|
929
|
-
|
|
933
|
+
input,
|
|
930
934
|
{
|
|
931
935
|
layout = "rect", // rect/radial/unrooted
|
|
932
936
|
width = 800,
|
|
@@ -944,6 +948,7 @@ function drawPhylogeny(
|
|
|
944
948
|
nodeLabelFontSize, // defaults to labelFontSize
|
|
945
949
|
scaleBar = false, // false | true | number (branch-length units) | { length, x, y, label }
|
|
946
950
|
alignTipLabels = false, // rect & radial only: align tip labels to a common column/ring, with dashed guide lines back to the true tip position
|
|
951
|
+
onNodeClick, // (node, event) => void — fires when an internal node circle is clicked (requires internalNodeCircles: true)
|
|
947
952
|
showTooltips = true,
|
|
948
953
|
tooltipFormatter = (d, rtt) =>
|
|
949
954
|
`${d.thisLabel ?? "(unnamed)"}\nroot→tip: ${(+rtt).toFixed(4)}`,
|
|
@@ -958,6 +963,9 @@ function drawPhylogeny(
|
|
|
958
963
|
// shared helpers
|
|
959
964
|
const isNumber = (x) => typeof x === "number" && Number.isFinite(x);
|
|
960
965
|
const nodeLabelSize = nodeLabelFontSize ?? labelFontSize;
|
|
966
|
+
const parsedTree = (input && typeof input === "object" && Array.isArray(input.children))
|
|
967
|
+
? input
|
|
968
|
+
: readTree(input);
|
|
961
969
|
// Works for both radial (uses `r`) and rect (uses `x1`).
|
|
962
970
|
// Falls back to summing branchLength up to the root if neither is present.
|
|
963
971
|
function makeRootToTipGetter(byId, { prefer = "auto" } = {}) {
|
|
@@ -986,7 +994,7 @@ function drawPhylogeny(
|
|
|
986
994
|
|
|
987
995
|
if (layout === "rect") {
|
|
988
996
|
// RECTANGULAR LAYOUT
|
|
989
|
-
const tree_df = rectangleLayout(
|
|
997
|
+
const tree_df = rectangleLayout(parsedTree);
|
|
990
998
|
const horizontal = tree_df.horizontal_lines;
|
|
991
999
|
const vertical = tree_df.vertical_lines;
|
|
992
1000
|
const tips = horizontal.filter((d) => d.isTip);
|
|
@@ -1097,6 +1105,12 @@ function drawPhylogeny(
|
|
|
1097
1105
|
.append("title")
|
|
1098
1106
|
.text((d) => tooltipFormatter(d, rootToTip(d.thisId)));
|
|
1099
1107
|
}
|
|
1108
|
+
|
|
1109
|
+
if (onNodeClick) {
|
|
1110
|
+
internalDots
|
|
1111
|
+
.style("cursor", "pointer")
|
|
1112
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1113
|
+
}
|
|
1100
1114
|
}
|
|
1101
1115
|
|
|
1102
1116
|
// internal node labels (optional)
|
|
@@ -1232,7 +1246,6 @@ function drawPhylogeny(
|
|
|
1232
1246
|
if (width !== height) {
|
|
1233
1247
|
throw new Error("width and height must be the same for radial layout");
|
|
1234
1248
|
}
|
|
1235
|
-
const parsedTree = readTree(treeText);
|
|
1236
1249
|
const rad = radialLayout(parsedTree, {
|
|
1237
1250
|
angleStrategy: "fan",
|
|
1238
1251
|
arcsStyle: "fan"
|
|
@@ -1452,6 +1465,12 @@ function drawPhylogeny(
|
|
|
1452
1465
|
.append("title")
|
|
1453
1466
|
.text((d) => tooltipFormatter(d, rootToTip(d.thisId)));
|
|
1454
1467
|
}
|
|
1468
|
+
|
|
1469
|
+
if (onNodeClick) {
|
|
1470
|
+
internalDots
|
|
1471
|
+
.style("cursor", "pointer")
|
|
1472
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1473
|
+
}
|
|
1455
1474
|
}
|
|
1456
1475
|
|
|
1457
1476
|
// ===== INTERNAL NODE LABELS (optional) =====
|
|
@@ -1652,7 +1671,6 @@ function drawPhylogeny(
|
|
|
1652
1671
|
return svg.node();
|
|
1653
1672
|
} else if (layout === "unrooted") {
|
|
1654
1673
|
// UNROOTED LAYOUT
|
|
1655
|
-
const parsedTree = readTree(treeText);
|
|
1656
1674
|
const unrootedPhylo = unrooted(parsedTree);
|
|
1657
1675
|
|
|
1658
1676
|
const w = width;
|
|
@@ -1714,6 +1732,13 @@ function drawPhylogeny(
|
|
|
1714
1732
|
.attr("stroke-width", 2)
|
|
1715
1733
|
.attr("fill", (d) => (d.isTip ? "black" : "white"));
|
|
1716
1734
|
|
|
1735
|
+
if (internalNodeCircles && onNodeClick) {
|
|
1736
|
+
nodes
|
|
1737
|
+
.filter((d) => !d.isTip)
|
|
1738
|
+
.style("cursor", "pointer")
|
|
1739
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1717
1742
|
if (nodeLabels) {
|
|
1718
1743
|
const labeledInternalNodes = unrootedPhylo.data.filter((d) => !d.isTip && d.thisLabel);
|
|
1719
1744
|
group
|