react-msaview 8.4.1 → 9.0.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.
Files changed (83) hide show
  1. package/bundle/index.js +66 -66
  2. package/bundle/index.js.map +4 -4
  3. package/dist/components/MSAView.js +2 -2
  4. package/dist/components/MSAView.js.map +1 -1
  5. package/dist/components/MSAViewer.d.ts +1 -3
  6. package/dist/components/MSAViewer.js +1 -2
  7. package/dist/components/MSAViewer.js.map +1 -1
  8. package/dist/components/ResizeHandles.js +1 -1
  9. package/dist/components/ResizeHandles.js.map +1 -1
  10. package/dist/components/Track.js +1 -1
  11. package/dist/components/Track.js.map +1 -1
  12. package/dist/components/dialogs/ExportSVGDialog.js +2 -2
  13. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  14. package/dist/components/header/settingsMenuItems.js +4 -1
  15. package/dist/components/header/settingsMenuItems.js.map +1 -1
  16. package/dist/components/tree/CladeLabels.js +3 -3
  17. package/dist/components/tree/CladeLabels.js.map +1 -1
  18. package/dist/components/tree/TreeCanvas.js +4 -4
  19. package/dist/components/tree/TreeCanvas.js.map +1 -1
  20. package/dist/components/tree/TreeCanvasBlock.js +4 -4
  21. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  22. package/dist/components/tree/TreeOverview.js +10 -5
  23. package/dist/components/tree/TreeOverview.js.map +1 -1
  24. package/dist/components/tree/TreePanel.js +6 -2
  25. package/dist/components/tree/TreePanel.js.map +1 -1
  26. package/dist/components/tree/TreeRuler.js +2 -2
  27. package/dist/components/tree/TreeRuler.js.map +1 -1
  28. package/dist/components/tree/cladeBrackets.d.ts +8 -10
  29. package/dist/components/tree/cladeBrackets.js +7 -6
  30. package/dist/components/tree/cladeBrackets.js.map +1 -1
  31. package/dist/components/tree/clickMap.d.ts +2 -0
  32. package/dist/components/tree/clickMap.js.map +1 -1
  33. package/dist/components/tree/ellipsize.d.ts +8 -0
  34. package/dist/components/tree/ellipsize.js +20 -0
  35. package/dist/components/tree/ellipsize.js.map +1 -0
  36. package/dist/components/tree/renderTreeCanvas.js +30 -26
  37. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  38. package/dist/components/tree/renderTreeMouseover.js +1 -1
  39. package/dist/components/tree/renderTreeMouseover.js.map +1 -1
  40. package/dist/constants.d.ts +6 -3
  41. package/dist/constants.js +16 -6
  42. package/dist/constants.js.map +1 -1
  43. package/dist/measureTextCanvas.js +2 -2
  44. package/dist/measureTextCanvas.js.map +1 -1
  45. package/dist/model/treeModel.d.ts +14 -20
  46. package/dist/model/treeModel.js +16 -23
  47. package/dist/model/treeModel.js.map +1 -1
  48. package/dist/model.d.ts +72 -37
  49. package/dist/model.js +108 -100
  50. package/dist/model.js.map +1 -1
  51. package/dist/renderToSvg.js +24 -22
  52. package/dist/renderToSvg.js.map +1 -1
  53. package/dist/svgTestUtil.d.ts +36 -28
  54. package/dist/tipLabels.d.ts +8 -0
  55. package/dist/tipLabels.js +46 -0
  56. package/dist/tipLabels.js.map +1 -0
  57. package/dist/version.d.ts +1 -1
  58. package/dist/version.js +1 -1
  59. package/package.json +3 -3
  60. package/src/components/MSAView.tsx +2 -2
  61. package/src/components/MSAViewer.tsx +1 -5
  62. package/src/components/ResizeHandles.tsx +1 -1
  63. package/src/components/Track.tsx +1 -1
  64. package/src/components/dialogs/ExportSVGDialog.tsx +2 -2
  65. package/src/components/header/settingsMenuItems.ts +8 -0
  66. package/src/components/tree/CladeLabels.tsx +3 -3
  67. package/src/components/tree/TreeCanvas.tsx +10 -4
  68. package/src/components/tree/TreeCanvasBlock.tsx +4 -4
  69. package/src/components/tree/TreeOverview.tsx +10 -5
  70. package/src/components/tree/TreePanel.tsx +8 -2
  71. package/src/components/tree/TreeRuler.tsx +7 -3
  72. package/src/components/tree/cladeBrackets.ts +10 -17
  73. package/src/components/tree/clickMap.ts +2 -0
  74. package/src/components/tree/ellipsize.ts +24 -0
  75. package/src/components/tree/renderTreeCanvas.ts +42 -30
  76. package/src/components/tree/renderTreeMouseover.ts +1 -1
  77. package/src/constants.ts +17 -6
  78. package/src/measureTextCanvas.ts +2 -2
  79. package/src/model/treeModel.ts +17 -27
  80. package/src/model.ts +129 -124
  81. package/src/renderToSvg.tsx +24 -22
  82. package/src/tipLabels.ts +51 -0
  83. package/src/version.ts +1 -1
package/dist/model.d.ts CHANGED
@@ -49,11 +49,11 @@ export declare const preservedOnReset: Set<string>;
49
49
  */
50
50
  declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
51
51
  maxInlineSnapshotBytes?: number;
52
- }): import("@jbrowse/mobx-state-tree").IModelType<Omit<Omit<Omit<{}, "autoTreeAreaWidth" | "drawLabels" | "drawNodeBubbles" | "drawNodeLabels" | "drawTree" | "labelsAlignRight" | "overviewHeight" | "showBranchLen" | "showTreeOverview" | "treeAreaWidth" | "treeOrder" | "treeWidth"> & {
52
+ }): import("@jbrowse/mobx-state-tree").IModelType<Omit<Omit<Omit<{}, "drawLabels" | "drawNodeBubbles" | "drawNodeLabels" | "drawTree" | "labelsAlignRight" | "overviewHeight" | "showBranchLen" | "showTreeOverview" | "treeAreaWidth" | "treeOrder" | "treeWidth"> & {
53
53
  drawLabels: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
54
54
  labelsAlignRight: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
55
- treeAreaWidth: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<number>, [undefined]>;
56
- treeWidth: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<number>, [undefined]>;
55
+ treeAreaWidth: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<number>>;
56
+ treeWidth: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<number>>;
57
57
  showBranchLen: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
58
58
  treeOrder: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<TreeOrder>, [undefined]>;
59
59
  drawTree: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
@@ -61,7 +61,6 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
61
61
  drawNodeLabels: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
62
62
  showTreeOverview: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
63
63
  overviewHeight: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<number>, [undefined]>;
64
- autoTreeAreaWidth: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
65
64
  }, "bgColor" | "colorSchemeName" | "customColorScheme" | "msaFormat" | "showColumnStats"> & {
66
65
  bgColor: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
67
66
  colorSchemeName: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
@@ -643,11 +642,10 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
643
642
  removeActiveDialog(): void;
644
643
  queueDialog(cb: (doneCallback: () => void) => [import("@jbrowse/core/util").DialogComponentType, unknown]): void;
645
644
  } & {
646
- setTreeAreaWidth(n: number): void;
647
- setTreeWidth(n: number): void;
645
+ setTreeAreaWidth(n: number | undefined): void;
646
+ setTreeWidth(n: number | undefined): void;
648
647
  setLabelsAlignRight(arg: boolean): void;
649
648
  setDrawTree(arg: boolean): void;
650
- setAutoTreeAreaWidth(arg: boolean): void;
651
649
  setShowBranchLen(arg: boolean): void;
652
650
  setTreeOrder(order: TreeOrder): void;
653
651
  setDrawNodeBubbles(arg: boolean): void;
@@ -1105,10 +1103,10 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
1105
1103
  * the row table: extra fields per row, keyed by row name, which the
1106
1104
  * `encodings` channels read. It is stored as the JSON string
1107
1105
  * `data.treeMetadata`, the name that travels in existing links, so the
1108
- * inline size limit and `treeMetadataFilehandle` cover it. labelWidthMap
1109
- * reads it on every layout, so a malformed user-supplied file returns {}
1110
- * instead of throwing out of rendering, and every field is a string by
1111
- * the time a scale sorts it.
1106
+ * inline size limit and `treeMetadataFilehandle` cover it. The tree area
1107
+ * fit reads it on every layout, so a malformed user-supplied file
1108
+ * returns {} instead of throwing out of rendering, and every field is a
1109
+ * string by the time a scale sorts it.
1112
1110
  */
1113
1111
  readonly rowData: Record<string, Record<string, string> | undefined>;
1114
1112
  /**
@@ -1168,6 +1166,60 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
1168
1166
  * where no clade draws a bar or a label.
1169
1167
  */
1170
1168
  readonly cladeGutterWidth: number;
1169
+ /**
1170
+ * #getter
1171
+ * whether a tree draws beside the labels: false for a file with no
1172
+ * tree, and with drawTree off
1173
+ */
1174
+ readonly treeDrawn: boolean;
1175
+ /**
1176
+ * #getter
1177
+ * the leaf names of the whole tree and its depth in levels. The fit
1178
+ * reads these, so a collapse or a focus leaves the tree area as it is
1179
+ */
1180
+ readonly treeTips: {
1181
+ names: string[];
1182
+ depth: number;
1183
+ };
1184
+ /**
1185
+ * #getter
1186
+ * the widest tip label at labelFitFontSize, measured whether or not a
1187
+ * zoom hides the labels
1188
+ */
1189
+ readonly maxTipLabelWidth: number;
1190
+ /**
1191
+ * #getter
1192
+ * the px the tip labels take across the tree area: the widest label at
1193
+ * labelFitFontSize and the gaps either side of it, or 0 with drawLabels
1194
+ * off
1195
+ */
1196
+ readonly labelColumnWidth: number;
1197
+ /**
1198
+ * #getter
1199
+ * the tree area width while treeAreaWidth is undefined: the label column
1200
+ * beside a tree that widens with its depth, up to maxTreeAreaShare of the
1201
+ * view, or the room the row panels leave where there is no alignment.
1202
+ * Where that binds, the tree keeps minTreeWidth and the renderer
1203
+ * ellipsizes the labels
1204
+ */
1205
+ readonly fittedTreeAreaWidth: number;
1206
+ /**
1207
+ * #getter
1208
+ * the width of the area the tree and its labels draw in, px:
1209
+ * treeAreaWidth where set, the fit otherwise
1210
+ */
1211
+ readonly effectiveTreeAreaWidth: number;
1212
+ /**
1213
+ * #getter
1214
+ * the width of the tree, px: treeWidth where set, otherwise the tree
1215
+ * area less the label column, which a zoom hiding the labels gives to
1216
+ * the tree
1217
+ */
1218
+ readonly effectiveTreeWidth: number;
1219
+ /**
1220
+ * #getter
1221
+ */
1222
+ readonly showTreeText: boolean;
1171
1223
  /**
1172
1224
  * #getter
1173
1225
  */
@@ -1357,9 +1409,9 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
1357
1409
  /**
1358
1410
  * #getter
1359
1411
  * `root` laid out on new nodes in row and branch-length units, read in
1360
- * pixels through `rowHeight` and `treeWidth`. A zoom leaves it and every
1361
- * node in it as they are, and an observer reading a node's `x` or `len`
1362
- * observes the size it scales by.
1412
+ * pixels through `rowHeight` and `effectiveTreeWidth`. A zoom leaves it
1413
+ * and every node in it as they are, and an observer reading a node's `x`
1414
+ * or `len` observes the size it scales by.
1363
1415
  */
1364
1416
  readonly treeLayout: {
1365
1417
  root: import("./hierarchy.ts").LaidOutNode<NodeWithIds>;
@@ -1387,8 +1439,8 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
1387
1439
  readonly rootToTipLength: number;
1388
1440
  /**
1389
1441
  * #getter
1390
- * x-position of the farthest tip in a phylogram, px: treeWidth, or 0 for
1391
- * a tree with no branch lengths (drawn as a cladogram)
1442
+ * x-position of the farthest tip in a phylogram, px: effectiveTreeWidth,
1443
+ * or 0 for a tree with no branch lengths (drawn as a cladogram)
1392
1444
  */
1393
1445
  readonly maxBranchLength: number;
1394
1446
  /**
@@ -1421,10 +1473,6 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
1421
1473
  * #getter
1422
1474
  */
1423
1475
  readonly showMsaLetters: boolean;
1424
- /**
1425
- * #getter
1426
- */
1427
- readonly showTreeText: boolean;
1428
1476
  } & {
1429
1477
  /**
1430
1478
  * #getter
@@ -1456,17 +1504,6 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
1456
1504
  */
1457
1505
  readonly columnTrackModels: BasicTrack[];
1458
1506
  } & {
1459
- /**
1460
- * #getter
1461
- */
1462
- readonly labelWidthMap: Map<string, number>;
1463
- /**
1464
- * #getter
1465
- * factor turning a labelWidthMap entry into its width at the current
1466
- * font size
1467
- */
1468
- readonly labelWidthScale: number;
1469
- readonly labelsWidth: number;
1470
1507
  /**
1471
1508
  * #getter
1472
1509
  */
@@ -2271,8 +2308,8 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
2271
2308
  msaFormat: import("msa-parsers").MSAFormat | undefined;
2272
2309
  drawLabels: boolean;
2273
2310
  labelsAlignRight: boolean;
2274
- treeAreaWidth: number;
2275
- treeWidth: number;
2311
+ treeAreaWidth: number | undefined;
2312
+ treeWidth: number | undefined;
2276
2313
  showBranchLen: boolean;
2277
2314
  treeOrder: TreeOrder;
2278
2315
  drawTree: boolean;
@@ -2280,7 +2317,6 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
2280
2317
  drawNodeLabels: boolean;
2281
2318
  showTreeOverview: boolean;
2282
2319
  overviewHeight: number;
2283
- autoTreeAreaWidth: boolean;
2284
2320
  columnTracks: ColumnTrackSpec[];
2285
2321
  id: string;
2286
2322
  showDomains: boolean;
@@ -2409,8 +2445,8 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
2409
2445
  msaFormat: import("msa-parsers").MSAFormat | undefined;
2410
2446
  drawLabels: boolean;
2411
2447
  labelsAlignRight: boolean;
2412
- treeAreaWidth: number;
2413
- treeWidth: number;
2448
+ treeAreaWidth: number | undefined;
2449
+ treeWidth: number | undefined;
2414
2450
  showBranchLen: boolean;
2415
2451
  treeOrder: TreeOrder;
2416
2452
  drawTree: boolean;
@@ -2418,7 +2454,6 @@ declare function stateModelFactory({ maxInlineSnapshotBytes, }?: {
2418
2454
  drawNodeLabels: boolean;
2419
2455
  showTreeOverview: boolean;
2420
2456
  overviewHeight: number;
2421
- autoTreeAreaWidth: boolean;
2422
2457
  columnTracks?: undefined;
2423
2458
  id: string;
2424
2459
  showDomains: boolean;
package/dist/model.js CHANGED
@@ -15,7 +15,7 @@ import { visibleColRange } from './components/msa/visibleColRange.js';
15
15
  import TrackBlocks from './components/tracks/TrackBlocks.js';
16
16
  import { cladeGutterWidth } from './components/tree/cladeBrackets.js';
17
17
  import { scaleBarLength } from './components/tree/scaleBar.js';
18
- import { cladeHighlightAlpha, cladeHighlightColor, defaultAllowedGappyness, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawMsaLetters, defaultFeaturePanelWidth, defaultHeight, defaultHideGaps, defaultRowHeight, defaultScrollX, defaultScrollY, defaultScrollZoom, defaultScrollZoomAxis, defaultShowDomainLegend, defaultShowDomains, defaultSubFeatureRows, defaultTreeWidth, labelReferenceFontSize, maxCellSize, maxInlineSnapshotBytes as defaultMaxInlineSnapshotBytes, maxNeighborJoiningRows, minColWidth, minLetterColWidth, minLetterRowHeight, minRowHeight, rowPanelHeaderHeight, rowTintAlpha, scrollZoomAxes, segmentFeatureTypes, segmentShades, treeScaleBarHeight, } from './constants.js';
18
+ import { cladeHighlightAlpha, cladeHighlightColor, defaultAllowedGappyness, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawMsaLetters, defaultFeaturePanelWidth, defaultHeight, defaultHideGaps, defaultRowHeight, defaultScrollX, defaultScrollY, defaultScrollZoom, defaultScrollZoomAxis, defaultShowDomainLegend, defaultShowDomains, defaultSubFeatureRows, fontSizeFor, labelFitFontSize, maxCellSize, maxFitTreeWidth, maxTreeAreaShare, maxInlineSnapshotBytes as defaultMaxInlineSnapshotBytes, maxNeighborJoiningRows, minColWidth, minLetterColWidth, minLetterRowHeight, minRowHeight, minTreeWidth, pxPerTreeDepth, rowPanelHeaderHeight, rowTintAlpha, scrollZoomAxes, segmentFeatureTypes, segmentShades, treeScaleBarHeight, } from './constants.js';
19
19
  import { createPaletteMap } from './createPaletteMap.js';
20
20
  import { exportFileName } from './exportFileName.js';
21
21
  import { featureField, featureName } from './featureFields.js';
@@ -23,7 +23,6 @@ import { featuresToAnnotations } from './featuresLayer.js';
23
23
  import { fetchTextWithProgress, isAbortError } from './fetchUtils.js';
24
24
  import { flatToTree } from './flatToTree.js';
25
25
  import { calcDepthToLeaf, clusterLayout, collapse, find, forEachDescendant, hierarchy, layoutTree, leafIndex, leaves, maxLength, mrca, nodeCoveringRows, setBrLength, sort, sum as hierarchySum, } from './hierarchy.js';
26
- import { measureTextCanvas } from './measureTextCanvas.js';
27
26
  import { DataModelF } from './model/DataModel.js';
28
27
  import { DialogQueueSessionMixin } from './model/DialogQueue.js';
29
28
  import { MSAModelF } from './model/msaModel.js';
@@ -37,6 +36,7 @@ import { resolveScale } from './scales.js';
37
36
  import { buildSeqPosIndex } from './seqPosToGlobalCol.js';
38
37
  import { maxBitsFor } from './sequenceLogo.js';
39
38
  import { stripDefault } from './stripDefault.js';
39
+ import { treeTips, widestLabel } from './tipLabels.js';
40
40
  import { computeRowInsertions, dropBlanks, len, outlineColor, skipBlanks, withAlpha, } from './util.js';
41
41
  import { saveAs } from './vendor/fileSaver.js';
42
42
  import { writeNewick } from './writeNewick.js';
@@ -303,7 +303,6 @@ export const preservedOnReset = new Set([
303
303
  'drawNodeLabels',
304
304
  'showTreeOverview',
305
305
  'overviewHeight',
306
- 'autoTreeAreaWidth',
307
306
  'turnedOffTracks',
308
307
  'trackHeights',
309
308
  'hideGaps',
@@ -1420,10 +1419,10 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1420
1419
  * the row table: extra fields per row, keyed by row name, which the
1421
1420
  * `encodings` channels read. It is stored as the JSON string
1422
1421
  * `data.treeMetadata`, the name that travels in existing links, so the
1423
- * inline size limit and `treeMetadataFilehandle` cover it. labelWidthMap
1424
- * reads it on every layout, so a malformed user-supplied file returns {}
1425
- * instead of throwing out of rendering, and every field is a string by
1426
- * the time a scale sorts it.
1422
+ * inline size limit and `treeMetadataFilehandle` cover it. The tree area
1423
+ * fit reads it on every layout, so a malformed user-supplied file
1424
+ * returns {} instead of throwing out of rendering, and every field is a
1425
+ * string by the time a scale sorts it.
1427
1426
  */
1428
1427
  get rowData() {
1429
1428
  const text = self.data.treeMetadata;
@@ -1544,11 +1543,93 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1544
1543
  * where no clade draws a bar or a label.
1545
1544
  */
1546
1545
  get cladeGutterWidth() {
1547
- return cladeGutterWidth({
1548
- clades: this.resolvedClades,
1549
- rowHeight: self.rowHeight,
1550
- fontSize: this.fontSize,
1551
- });
1546
+ return cladeGutterWidth(self.clades);
1547
+ },
1548
+ /**
1549
+ * #getter
1550
+ * whether a tree draws beside the labels: false for a file with no
1551
+ * tree, and with drawTree off
1552
+ */
1553
+ get treeDrawn() {
1554
+ return self.drawTree && !this.noTree;
1555
+ },
1556
+ /**
1557
+ * #getter
1558
+ * the leaf names of the whole tree and its depth in levels. The fit
1559
+ * reads these, so a collapse or a focus leaves the tree area as it is
1560
+ */
1561
+ get treeTips() {
1562
+ return treeTips(this.tree);
1563
+ },
1564
+ /**
1565
+ * #getter
1566
+ * the widest tip label at labelFitFontSize, measured whether or not a
1567
+ * zoom hides the labels
1568
+ */
1569
+ get maxTipLabelWidth() {
1570
+ return widestLabel(this.treeTips.names.map(name => this.rowDataOf(name)?.genome || name), labelFitFontSize);
1571
+ },
1572
+ /**
1573
+ * #getter
1574
+ * the px the tip labels take across the tree area: the widest label at
1575
+ * labelFitFontSize and the gaps either side of it, or 0 with drawLabels
1576
+ * off
1577
+ */
1578
+ get labelColumnWidth() {
1579
+ return self.drawLabels
1580
+ ? this.maxTipLabelWidth + (this.treeDrawn ? 10 : 6)
1581
+ : 0;
1582
+ },
1583
+ /**
1584
+ * #getter
1585
+ * the tree area width while treeAreaWidth is undefined: the label column
1586
+ * beside a tree that widens with its depth, up to maxTreeAreaShare of the
1587
+ * view, or the room the row panels leave where there is no alignment.
1588
+ * Where that binds, the tree keeps minTreeWidth and the renderer
1589
+ * ellipsizes the labels
1590
+ */
1591
+ get fittedTreeAreaWidth() {
1592
+ const chrome = self.marginLeft + this.cladeGutterWidth;
1593
+ const tree = this.treeDrawn
1594
+ ? (self.treeWidth ??
1595
+ clamp(this.treeTips.depth * pxPerTreeDepth, minTreeWidth, maxFitTreeWidth))
1596
+ : 0;
1597
+ const fit = chrome + tree + this.labelColumnWidth;
1598
+ const room = self.viewInitialized
1599
+ ? this.numColumns > 0
1600
+ ? self.width * maxTreeAreaShare
1601
+ : self.width - this.rowPanelsWidth - self.resizeHandleWidth
1602
+ : Infinity;
1603
+ const cap = Math.max(room, chrome + Math.min(tree, minTreeWidth));
1604
+ return Math.ceil(Math.min(fit, cap));
1605
+ },
1606
+ /**
1607
+ * #getter
1608
+ * the width of the area the tree and its labels draw in, px:
1609
+ * treeAreaWidth where set, the fit otherwise
1610
+ */
1611
+ get effectiveTreeAreaWidth() {
1612
+ return self.treeAreaWidth ?? this.fittedTreeAreaWidth;
1613
+ },
1614
+ /**
1615
+ * #getter
1616
+ * the width of the tree, px: treeWidth where set, otherwise the tree
1617
+ * area less the label column, which a zoom hiding the labels gives to
1618
+ * the tree
1619
+ */
1620
+ get effectiveTreeWidth() {
1621
+ const labels = this.showTreeText ? this.labelColumnWidth : 0;
1622
+ return (self.treeWidth ??
1623
+ Math.max(minTreeWidth, Math.floor(this.effectiveTreeAreaWidth -
1624
+ self.marginLeft -
1625
+ this.cladeGutterWidth -
1626
+ labels)));
1627
+ },
1628
+ /**
1629
+ * #getter
1630
+ */
1631
+ get showTreeText() {
1632
+ return self.drawLabels && self.rowHeight >= minLetterRowHeight;
1552
1633
  },
1553
1634
  /**
1554
1635
  * #getter
@@ -1714,7 +1795,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1714
1795
  */
1715
1796
  get msaAreaWidth() {
1716
1797
  return (self.width -
1717
- self.treeAreaWidth -
1798
+ this.effectiveTreeAreaWidth -
1718
1799
  this.rowPanelsWidth -
1719
1800
  self.resizeHandleWidth);
1720
1801
  },
@@ -1724,7 +1805,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1724
1805
  * margin and the bracket gutter
1725
1806
  */
1726
1807
  get treeAreaWidthMinusMargin() {
1727
- return self.treeAreaWidth - self.marginLeft - this.cladeGutterWidth;
1808
+ return (this.effectiveTreeAreaWidth - self.marginLeft - this.cladeGutterWidth);
1728
1809
  },
1729
1810
  /**
1730
1811
  * #getter
@@ -1860,7 +1941,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1860
1941
  * #getter
1861
1942
  */
1862
1943
  get fontSize() {
1863
- return Math.min(Math.max(6, self.rowHeight - 3), 18);
1944
+ return fontSizeFor(self.rowHeight);
1864
1945
  },
1865
1946
  /**
1866
1947
  * #getter
@@ -1959,17 +2040,18 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1959
2040
  /**
1960
2041
  * #getter
1961
2042
  * `root` laid out on new nodes in row and branch-length units, read in
1962
- * pixels through `rowHeight` and `treeWidth`. A zoom leaves it and every
1963
- * node in it as they are, and an observer reading a node's `x` or `len`
1964
- * observes the size it scales by.
2043
+ * pixels through `rowHeight` and `effectiveTreeWidth`. A zoom leaves it
2044
+ * and every node in it as they are, and an observer reading a node's `x`
2045
+ * or `len` observes the size it scales by.
1965
2046
  */
1966
2047
  get treeLayout() {
2048
+ const treeWidth = () => this.effectiveTreeWidth;
1967
2049
  return layoutTree(this.root, {
1968
2050
  get rowHeight() {
1969
2051
  return self.rowHeight;
1970
2052
  },
1971
2053
  get treeWidth() {
1972
- return self.treeWidth;
2054
+ return treeWidth();
1973
2055
  },
1974
2056
  });
1975
2057
  },
@@ -2002,11 +2084,11 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2002
2084
  },
2003
2085
  /**
2004
2086
  * #getter
2005
- * x-position of the farthest tip in a phylogram, px: treeWidth, or 0 for
2006
- * a tree with no branch lengths (drawn as a cladogram)
2087
+ * x-position of the farthest tip in a phylogram, px: effectiveTreeWidth,
2088
+ * or 0 for a tree with no branch lengths (drawn as a cladogram)
2007
2089
  */
2008
2090
  get maxBranchLength() {
2009
- return this.rootToTipLength ? self.treeWidth : 0;
2091
+ return this.rootToTipLength ? this.effectiveTreeWidth : 0;
2010
2092
  },
2011
2093
  /**
2012
2094
  * #getter
@@ -2015,7 +2097,9 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2015
2097
  */
2016
2098
  get pxPerBranchLength() {
2017
2099
  const max = this.rootToTipLength;
2018
- return this.showBranchLenEffective && max ? self.treeWidth / max : 0;
2100
+ return this.showBranchLenEffective && max
2101
+ ? this.effectiveTreeWidth / max
2102
+ : 0;
2019
2103
  },
2020
2104
  /**
2021
2105
  * #getter
@@ -2056,12 +2140,6 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2056
2140
  self.colWidth >= minLetterColWidth &&
2057
2141
  self.colWidth > self.rowHeight / 2);
2058
2142
  },
2059
- /**
2060
- * #getter
2061
- */
2062
- get showTreeText() {
2063
- return self.drawLabels && self.rowHeight >= minLetterRowHeight;
2064
- },
2065
2143
  }))
2066
2144
  .views(self => {
2067
2145
  // a spec is a frozen value, so it keys its computed until setColumnTracks
@@ -2183,48 +2261,6 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2183
2261
  };
2184
2262
  })
2185
2263
  .views(self => ({
2186
- /**
2187
- * #getter
2188
- */
2189
- get labelWidthMap() {
2190
- const { showTreeText, leaves } = self;
2191
- // gated on the renderer's label condition, so hidden labels reserve no
2192
- // gutter. Measured once at a reference size and scaled by
2193
- // labelWidthScale: re-measuring per vertical-zoom frame cost ~200ms on a
2194
- // 50k-leaf tree
2195
- return showTreeText
2196
- ? new Map(leaves.map(node => {
2197
- const { name } = node.data;
2198
- // `||`, matching renderTreeLabels: an empty genome falls back
2199
- // to the row name
2200
- const displayName = self.rowDataOf(name)?.genome || name;
2201
- return [
2202
- name,
2203
- measureTextCanvas(displayName, labelReferenceFontSize),
2204
- ];
2205
- }))
2206
- : new Map();
2207
- },
2208
- /**
2209
- * #getter
2210
- * factor turning a labelWidthMap entry into its width at the current
2211
- * font size
2212
- */
2213
- get labelWidthScale() {
2214
- return self.fontSize / labelReferenceFontSize;
2215
- },
2216
- get labelsWidth() {
2217
- // a loop, not Math.max(...): spreading passes one argument per leaf,
2218
- // and the ~125k argument limit throws RangeError on the 230k-tip COVID
2219
- // tree
2220
- let max = 0;
2221
- for (const width of this.labelWidthMap.values()) {
2222
- if (width > max) {
2223
- max = width;
2224
- }
2225
- }
2226
- return max * this.labelWidthScale;
2227
- },
2228
2264
  /**
2229
2265
  * #getter
2230
2266
  */
@@ -2663,7 +2699,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2663
2699
  * or when the tree area is too narrow for one
2664
2700
  */
2665
2701
  get treeScaleBar() {
2666
- return scaleBarLength(self.pxPerBranchLength, self.treeAreaWidth - self.marginLeft * 2);
2702
+ return scaleBarLength(self.pxPerBranchLength, self.effectiveTreeAreaWidth - self.marginLeft * 2);
2667
2703
  },
2668
2704
  /**
2669
2705
  * #getter
@@ -4166,34 +4202,6 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
4166
4202
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
4167
4203
  self.columns;
4168
4204
  }));
4169
- // with autoTreeAreaWidth and no tree drawn, size the tree area to the
4170
- // labels. Gated on noTree/!drawTree so it does not loop with the
4171
- // treeWidth autorun below
4172
- addDisposer(self, autorun(() => {
4173
- if (self.autoTreeAreaWidth &&
4174
- (self.noTree || !self.drawTree) &&
4175
- self.labelsWidth) {
4176
- self.setTreeAreaWidth(self.labelsWidth + self.marginLeft + 12 + self.cladeGutterWidth);
4177
- }
4178
- }));
4179
- // treeWidth follows the tree area less the labels. A non-default
4180
- // snapshot treeWidth (jbrowse-plugin-msaview opens at 100 in a 200px
4181
- // area) is kept until the tree area changes. Not a getter, because
4182
- // labelsWidth is measured off leaves laid out against treeWidth.
4183
- let pinnedAreaWidth = self.treeWidth === defaultTreeWidth ? undefined : self.treeAreaWidth;
4184
- addDisposer(self, autorun(() => {
4185
- const areaWidth = self.treeAreaWidth;
4186
- const labelsWidth = self.labelsWidth;
4187
- if (pinnedAreaWidth === areaWidth) {
4188
- return;
4189
- }
4190
- pinnedAreaWidth = undefined;
4191
- self.setTreeWidth(Math.max(50, areaWidth -
4192
- labelsWidth -
4193
- 10 -
4194
- self.marginLeft -
4195
- self.cladeGutterWidth));
4196
- }));
4197
4205
  },
4198
4206
  }))
4199
4207
  .postProcessSnapshot(({ data, columnTracks, ...rest }) => ({