@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.js
CHANGED
|
@@ -1223,8 +1223,12 @@ function addScaleBar(svg, { scale, basis, defaultX, defaultY, scaleBar, fontSize
|
|
|
1223
1223
|
.text(opts.label ?? String(length));
|
|
1224
1224
|
}
|
|
1225
1225
|
|
|
1226
|
+
// input: a Newick string, or an already-parsed tree (the node shape returned
|
|
1227
|
+
// by readTree()/randomTree()). Passing the same parsed tree object back in
|
|
1228
|
+
// across re-renders (e.g. after mutating it with rotate()) keeps node ids
|
|
1229
|
+
// stable, which onNodeClick below relies on.
|
|
1226
1230
|
function drawPhylogeny(
|
|
1227
|
-
|
|
1231
|
+
input,
|
|
1228
1232
|
{
|
|
1229
1233
|
layout = "rect", // rect/radial/unrooted
|
|
1230
1234
|
width = 800,
|
|
@@ -1242,6 +1246,7 @@ function drawPhylogeny(
|
|
|
1242
1246
|
nodeLabelFontSize, // defaults to labelFontSize
|
|
1243
1247
|
scaleBar = false, // false | true | number (branch-length units) | { length, x, y, label }
|
|
1244
1248
|
alignTipLabels = false, // rect & radial only: align tip labels to a common column/ring, with dashed guide lines back to the true tip position
|
|
1249
|
+
onNodeClick, // (node, event) => void — fires when an internal node circle is clicked (requires internalNodeCircles: true)
|
|
1245
1250
|
showTooltips = true,
|
|
1246
1251
|
tooltipFormatter = (d, rtt) =>
|
|
1247
1252
|
`${d.thisLabel ?? "(unnamed)"}\nroot→tip: ${(+rtt).toFixed(4)}`,
|
|
@@ -1256,6 +1261,9 @@ function drawPhylogeny(
|
|
|
1256
1261
|
// shared helpers
|
|
1257
1262
|
const isNumber = (x) => typeof x === "number" && Number.isFinite(x);
|
|
1258
1263
|
const nodeLabelSize = nodeLabelFontSize ?? labelFontSize;
|
|
1264
|
+
const parsedTree = (input && typeof input === "object" && Array.isArray(input.children))
|
|
1265
|
+
? input
|
|
1266
|
+
: readTree(input);
|
|
1259
1267
|
// Works for both radial (uses `r`) and rect (uses `x1`).
|
|
1260
1268
|
// Falls back to summing branchLength up to the root if neither is present.
|
|
1261
1269
|
function makeRootToTipGetter(byId, { prefer = "auto" } = {}) {
|
|
@@ -1284,7 +1292,7 @@ function drawPhylogeny(
|
|
|
1284
1292
|
|
|
1285
1293
|
if (layout === "rect") {
|
|
1286
1294
|
// RECTANGULAR LAYOUT
|
|
1287
|
-
const tree_df = rectangleLayout(
|
|
1295
|
+
const tree_df = rectangleLayout(parsedTree);
|
|
1288
1296
|
const horizontal = tree_df.horizontal_lines;
|
|
1289
1297
|
const vertical = tree_df.vertical_lines;
|
|
1290
1298
|
const tips = horizontal.filter((d) => d.isTip);
|
|
@@ -1395,6 +1403,12 @@ function drawPhylogeny(
|
|
|
1395
1403
|
.append("title")
|
|
1396
1404
|
.text((d) => tooltipFormatter(d, rootToTip(d.thisId)));
|
|
1397
1405
|
}
|
|
1406
|
+
|
|
1407
|
+
if (onNodeClick) {
|
|
1408
|
+
internalDots
|
|
1409
|
+
.style("cursor", "pointer")
|
|
1410
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1411
|
+
}
|
|
1398
1412
|
}
|
|
1399
1413
|
|
|
1400
1414
|
// internal node labels (optional)
|
|
@@ -1530,7 +1544,6 @@ function drawPhylogeny(
|
|
|
1530
1544
|
if (width !== height) {
|
|
1531
1545
|
throw new Error("width and height must be the same for radial layout");
|
|
1532
1546
|
}
|
|
1533
|
-
const parsedTree = readTree(treeText);
|
|
1534
1547
|
const rad = radialLayout(parsedTree, {
|
|
1535
1548
|
angleStrategy: "fan",
|
|
1536
1549
|
arcsStyle: "fan"
|
|
@@ -1750,6 +1763,12 @@ function drawPhylogeny(
|
|
|
1750
1763
|
.append("title")
|
|
1751
1764
|
.text((d) => tooltipFormatter(d, rootToTip(d.thisId)));
|
|
1752
1765
|
}
|
|
1766
|
+
|
|
1767
|
+
if (onNodeClick) {
|
|
1768
|
+
internalDots
|
|
1769
|
+
.style("cursor", "pointer")
|
|
1770
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
1771
|
+
}
|
|
1753
1772
|
}
|
|
1754
1773
|
|
|
1755
1774
|
// ===== INTERNAL NODE LABELS (optional) =====
|
|
@@ -1950,7 +1969,6 @@ function drawPhylogeny(
|
|
|
1950
1969
|
return svg.node();
|
|
1951
1970
|
} else if (layout === "unrooted") {
|
|
1952
1971
|
// UNROOTED LAYOUT
|
|
1953
|
-
const parsedTree = readTree(treeText);
|
|
1954
1972
|
const unrootedPhylo = unrooted(parsedTree);
|
|
1955
1973
|
|
|
1956
1974
|
const w = width;
|
|
@@ -2012,6 +2030,13 @@ function drawPhylogeny(
|
|
|
2012
2030
|
.attr("stroke-width", 2)
|
|
2013
2031
|
.attr("fill", (d) => (d.isTip ? "black" : "white"));
|
|
2014
2032
|
|
|
2033
|
+
if (internalNodeCircles && onNodeClick) {
|
|
2034
|
+
nodes
|
|
2035
|
+
.filter((d) => !d.isTip)
|
|
2036
|
+
.style("cursor", "pointer")
|
|
2037
|
+
.on("click", (event, d) => onNodeClick(d, event));
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2015
2040
|
if (nodeLabels) {
|
|
2016
2041
|
const labeledInternalNodes = unrootedPhylo.data.filter((d) => !d.isTip && d.thisLabel);
|
|
2017
2042
|
group
|