@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
package/dist/index.cjs
CHANGED
|
@@ -1244,8 +1244,12 @@ function addScaleBar(svg, { scale, basis, defaultX, defaultY, scaleBar, fontSize
|
|
|
1244
1244
|
.text(opts.label ?? String(length));
|
|
1245
1245
|
}
|
|
1246
1246
|
|
|
1247
|
+
// input: a Newick string, or an already-parsed tree (the node shape returned
|
|
1248
|
+
// by readTree()/randomTree()). Passing the same parsed tree object back in
|
|
1249
|
+
// across re-renders (e.g. after mutating it with rotate()) keeps node ids
|
|
1250
|
+
// stable, which onNodeClick below relies on.
|
|
1247
1251
|
function drawPhylogeny(
|
|
1248
|
-
|
|
1252
|
+
input,
|
|
1249
1253
|
{
|
|
1250
1254
|
layout = "rect", // rect/radial/unrooted
|
|
1251
1255
|
width = 800,
|
|
@@ -1263,6 +1267,7 @@ function drawPhylogeny(
|
|
|
1263
1267
|
nodeLabelFontSize, // defaults to labelFontSize
|
|
1264
1268
|
scaleBar = false, // false | true | number (branch-length units) | { length, x, y, label }
|
|
1265
1269
|
alignTipLabels = false, // rect & radial only: align tip labels to a common column/ring, with dashed guide lines back to the true tip position
|
|
1270
|
+
onNodeClick, // (node, event) => void — fires when an internal node circle is clicked (requires internalNodeCircles: true)
|
|
1266
1271
|
showTooltips = true,
|
|
1267
1272
|
tooltipFormatter = (d, rtt) =>
|
|
1268
1273
|
`${d.thisLabel ?? "(unnamed)"}\nroot→tip: ${(+rtt).toFixed(4)}`,
|
|
@@ -1277,6 +1282,9 @@ function drawPhylogeny(
|
|
|
1277
1282
|
// shared helpers
|
|
1278
1283
|
const isNumber = (x) => typeof x === "number" && Number.isFinite(x);
|
|
1279
1284
|
const nodeLabelSize = nodeLabelFontSize ?? labelFontSize;
|
|
1285
|
+
const parsedTree = (input && typeof input === "object" && Array.isArray(input.children))
|
|
1286
|
+
? input
|
|
1287
|
+
: readTree(input);
|
|
1280
1288
|
// Works for both radial (uses `r`) and rect (uses `x1`).
|
|
1281
1289
|
// Falls back to summing branchLength up to the root if neither is present.
|
|
1282
1290
|
function makeRootToTipGetter(byId, { prefer = "auto" } = {}) {
|
|
@@ -1305,7 +1313,7 @@ function drawPhylogeny(
|
|
|
1305
1313
|
|
|
1306
1314
|
if (layout === "rect") {
|
|
1307
1315
|
// RECTANGULAR LAYOUT
|
|
1308
|
-
const tree_df = rectangleLayout(
|
|
1316
|
+
const tree_df = rectangleLayout(parsedTree);
|
|
1309
1317
|
const horizontal = tree_df.horizontal_lines;
|
|
1310
1318
|
const vertical = tree_df.vertical_lines;
|
|
1311
1319
|
const tips = horizontal.filter((d) => d.isTip);
|
|
@@ -1416,6 +1424,12 @@ function drawPhylogeny(
|
|
|
1416
1424
|
.append("title")
|
|
1417
1425
|
.text((d) => tooltipFormatter(d, rootToTip(d.thisId)));
|
|
1418
1426
|
}
|
|
1427
|
+
|
|
1428
|
+
if (onNodeClick) {
|
|
1429
|
+
internalDots
|
|
1430
|
+
.style("cursor", "pointer")
|
|
1431
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1432
|
+
}
|
|
1419
1433
|
}
|
|
1420
1434
|
|
|
1421
1435
|
// internal node labels (optional)
|
|
@@ -1551,7 +1565,6 @@ function drawPhylogeny(
|
|
|
1551
1565
|
if (width !== height) {
|
|
1552
1566
|
throw new Error("width and height must be the same for radial layout");
|
|
1553
1567
|
}
|
|
1554
|
-
const parsedTree = readTree(treeText);
|
|
1555
1568
|
const rad = radialLayout(parsedTree, {
|
|
1556
1569
|
angleStrategy: "fan",
|
|
1557
1570
|
arcsStyle: "fan"
|
|
@@ -1771,6 +1784,12 @@ function drawPhylogeny(
|
|
|
1771
1784
|
.append("title")
|
|
1772
1785
|
.text((d) => tooltipFormatter(d, rootToTip(d.thisId)));
|
|
1773
1786
|
}
|
|
1787
|
+
|
|
1788
|
+
if (onNodeClick) {
|
|
1789
|
+
internalDots
|
|
1790
|
+
.style("cursor", "pointer")
|
|
1791
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1792
|
+
}
|
|
1774
1793
|
}
|
|
1775
1794
|
|
|
1776
1795
|
// ===== INTERNAL NODE LABELS (optional) =====
|
|
@@ -1971,7 +1990,6 @@ function drawPhylogeny(
|
|
|
1971
1990
|
return svg.node();
|
|
1972
1991
|
} else if (layout === "unrooted") {
|
|
1973
1992
|
// UNROOTED LAYOUT
|
|
1974
|
-
const parsedTree = readTree(treeText);
|
|
1975
1993
|
const unrootedPhylo = unrooted(parsedTree);
|
|
1976
1994
|
|
|
1977
1995
|
const w = width;
|
|
@@ -2033,6 +2051,13 @@ function drawPhylogeny(
|
|
|
2033
2051
|
.attr("stroke-width", 2)
|
|
2034
2052
|
.attr("fill", (d) => (d.isTip ? "black" : "white"));
|
|
2035
2053
|
|
|
2054
|
+
if (internalNodeCircles && onNodeClick) {
|
|
2055
|
+
nodes
|
|
2056
|
+
.filter((d) => !d.isTip)
|
|
2057
|
+
.style("cursor", "pointer")
|
|
2058
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2036
2061
|
if (nodeLabels) {
|
|
2037
2062
|
const labeledInternalNodes = unrootedPhylo.data.filter((d) => !d.isTip && d.thisLabel);
|
|
2038
2063
|
group
|