react-msaview 9.2.0 → 9.3.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 (80) hide show
  1. package/bundle/index.js +74 -74
  2. package/bundle/index.js.map +4 -4
  3. package/dist/components/DragHandle.js +2 -1
  4. package/dist/components/DragHandle.js.map +1 -1
  5. package/dist/components/MSAViewer.d.ts +29 -19
  6. package/dist/components/MSAViewer.js +2 -1
  7. package/dist/components/MSAViewer.js.map +1 -1
  8. package/dist/components/Track.js +2 -5
  9. package/dist/components/Track.js.map +1 -1
  10. package/dist/components/VerticalScrollbar.js +1 -1
  11. package/dist/components/VerticalScrollbar.js.map +1 -1
  12. package/dist/components/header/Header.js +2 -1
  13. package/dist/components/header/Header.js.map +1 -1
  14. package/dist/components/minimap/Minimap.js +1 -1
  15. package/dist/components/minimap/Minimap.js.map +1 -1
  16. package/dist/components/msa/MSACanvas.js +16 -27
  17. package/dist/components/msa/MSACanvas.js.map +1 -1
  18. package/dist/components/rowpanels/RowPanels.js +8 -6
  19. package/dist/components/rowpanels/RowPanels.js.map +1 -1
  20. package/dist/components/settings/SettingsPanel.js +2 -1
  21. package/dist/components/settings/SettingsPanel.js.map +1 -1
  22. package/dist/components/tree/TreeCanvas.js +8 -6
  23. package/dist/components/tree/TreeCanvas.js.map +1 -1
  24. package/dist/constants.d.ts +1 -0
  25. package/dist/constants.js +2 -0
  26. package/dist/constants.js.map +1 -1
  27. package/dist/element.d.ts +11 -7
  28. package/dist/element.js +46 -8
  29. package/dist/element.js.map +1 -1
  30. package/dist/featurePanels.d.ts +0 -7
  31. package/dist/featurePanels.js +1 -16
  32. package/dist/featurePanels.js.map +1 -1
  33. package/dist/ggplotPalettes.js +12 -1
  34. package/dist/ggplotPalettes.js.map +1 -1
  35. package/dist/groupKeys.d.ts +6 -0
  36. package/dist/groupKeys.js +52 -0
  37. package/dist/groupKeys.js.map +1 -0
  38. package/dist/index.d.ts +1 -1
  39. package/dist/model.d.ts +34 -17
  40. package/dist/model.js +100 -54
  41. package/dist/model.js.map +1 -1
  42. package/dist/scales.d.ts +114 -13
  43. package/dist/scales.js +154 -19
  44. package/dist/scales.js.map +1 -1
  45. package/dist/svgTestUtil.d.ts +20 -12
  46. package/dist/types.d.ts +29 -27
  47. package/dist/useDragScroll.d.ts +4 -3
  48. package/dist/useDragScroll.js +9 -6
  49. package/dist/useDragScroll.js.map +1 -1
  50. package/dist/useWheelScroll.d.ts +26 -8
  51. package/dist/useWheelScroll.js +108 -71
  52. package/dist/useWheelScroll.js.map +1 -1
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/dist/viewerSnapshot.js +2 -1
  56. package/dist/viewerSnapshot.js.map +1 -1
  57. package/package.json +3 -3
  58. package/src/components/DragHandle.tsx +2 -1
  59. package/src/components/MSAViewer.tsx +11 -8
  60. package/src/components/Track.tsx +2 -8
  61. package/src/components/VerticalScrollbar.tsx +1 -1
  62. package/src/components/header/Header.tsx +2 -1
  63. package/src/components/minimap/Minimap.tsx +1 -1
  64. package/src/components/msa/MSACanvas.tsx +14 -35
  65. package/src/components/rowpanels/RowPanels.tsx +8 -10
  66. package/src/components/settings/SettingsPanel.tsx +2 -1
  67. package/src/components/tree/TreeCanvas.tsx +8 -10
  68. package/src/constants.ts +2 -0
  69. package/src/element.ts +53 -8
  70. package/src/featurePanels.ts +1 -25
  71. package/src/ggplotPalettes.ts +13 -1
  72. package/src/groupKeys.ts +54 -0
  73. package/src/index.ts +13 -2
  74. package/src/model.ts +149 -72
  75. package/src/scales.ts +268 -25
  76. package/src/types.ts +28 -25
  77. package/src/useDragScroll.ts +11 -8
  78. package/src/useWheelScroll.ts +124 -84
  79. package/src/version.ts +1 -1
  80. package/src/viewerSnapshot.ts +3 -1
package/dist/model.js CHANGED
@@ -13,11 +13,11 @@ import { packDomainLanes } from './components/msa/packDomainLanes.js';
13
13
  import { visibleColRange } from './components/msa/visibleColRange.js';
14
14
  import { cladeGutterWidth } from './components/tree/cladeBrackets.js';
15
15
  import { scaleBarLength } from './components/tree/scaleBar.js';
16
- import { defaultAllowedGappyness, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawMsaLetters, 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, settingsPanelWidth, treeScaleBarHeight, } from './constants.js';
16
+ import { defaultAllowedGappyness, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawMsaLetters, 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, resizeHandleWidth, rowPanelHeaderHeight, rowTintAlpha, scrollZoomAxes, segmentFeatureTypes, segmentShades, settingsPanelWidth, treeScaleBarHeight, } from './constants.js';
17
17
  import { createPaletteMap } from './createPaletteMap.js';
18
18
  import { exportFileName } from './exportFileName.js';
19
- import { featureName } from './featureFields.js';
20
- import { colorByRow, featureColorMap, featureLabelMap, featurePanelSpans, resolveFeatureScale, rowPanelWidth, stringFields, } from './featurePanels.js';
19
+ import { featureField, featureName } from './featureFields.js';
20
+ import { colorByRow, featureColorMap, featureLabelMap, featurePanelSpans, rowPanelWidth, stringFields, } from './featurePanels.js';
21
21
  import { featuresToAnnotations } from './featuresLayer.js';
22
22
  import { installFilehandleLoader } from './filehandleLoader.js';
23
23
  import { find, hierarchy, layoutTree, leaves, nodeCoveringRows, } from './hierarchy.js';
@@ -29,7 +29,7 @@ import { calculateNeighborJoiningTree } from './neighborJoining.js';
29
29
  import { calculatePropertyConservation } from './propertyConservation.js';
30
30
  import { preorder } from './rerootTree.js';
31
31
  import { globalColToVisibleCol, seqPosOfGlobalCol, visibleColToGlobalCol, visibleColsBefore, } from './rowCoordinateCalculations.js';
32
- import { resolveScale } from './scales.js';
32
+ import { colorTable, declarationKey, readColumnTrack, readEncoding, readRowPanel, resolveShared, } from './scales.js';
33
33
  import { buildSeqPosIndex } from './seqPosToGlobalCol.js';
34
34
  import { maxBitsFor } from './sequenceLogo.js';
35
35
  import { blosum62ColumnColors, identityColumnColors, } from './similarityColors.js';
@@ -391,7 +391,9 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
391
391
  * tracks supplied as data: per-column values drawn as bars, or a
392
392
  * per-column string drawn as a text track. See docs/layers.md
393
393
  */
394
- columnTracks: stripDefault(types.array(types.frozen()), []),
394
+ columnTracks: stripDefault(types.array(types.snapshotProcessor(types.frozen(), {
395
+ preProcessor: readColumnTrack,
396
+ })), []),
395
397
  /**
396
398
  * #property
397
399
  * row-to-structure residue correspondence, computed outside the viewer
@@ -471,20 +473,25 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
471
473
  clades: stripDefault(types.array(types.frozen()), []),
472
474
  /**
473
475
  * #property
474
- * what the viewer's own marks read from `rowData`:
475
- * `{channel, field, scale?}` per channel, where `channel` is
476
- * `tipLabel` or `rowTint`. See docs/layers.md
476
+ * what the viewer's own marks read from `rowData` or the feature
477
+ * table: `{channel, field, range?, domain?, title?}` per channel. The
478
+ * earlier `scale: {palette}` and `scale: {map}` read as `range` and
479
+ * `domain`. See docs/layers.md
477
480
  */
478
- encodings: stripDefault(types.array(types.frozen()), []),
481
+ encodings: stripDefault(types.array(types.snapshotProcessor(types.frozen(), {
482
+ preProcessor: readEncoding,
483
+ })), []),
479
484
  /**
480
485
  * #property
481
486
  * panels drawn between the tree and the alignment:
482
- * `{kind: "strip", field, scale?, width?, header?}` colors one cell
483
- * per row from a `rowData` field, and
487
+ * `{kind: "strip", field, range?, domain?, title?, width?, header?}`
488
+ * colors one cell per row from a `rowData` field, and
484
489
  * `{kind: "features", x, encoding?, transform?, width?, header?}`
485
490
  * draws the spans the GFF carries. See docs/layers.md
486
491
  */
487
- rowPanels: stripDefault(types.array(types.frozen()), []),
492
+ rowPanels: stripDefault(types.array(types.snapshotProcessor(types.frozen(), {
493
+ preProcessor: readRowPanel,
494
+ })), []),
488
495
  }))
489
496
  .volatile(() => ({
490
497
  /**
@@ -545,9 +552,10 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
545
552
  volatileWidth: undefined,
546
553
  /**
547
554
  * #volatile
548
- * resize handle width between tree and msa area, px
555
+ * thickness of the dividers, between the tree and the alignment and under
556
+ * the view, px
549
557
  */
550
- resizeHandleWidth: 5,
558
+ resizeHandleWidth,
551
559
  /**
552
560
  * #volatile
553
561
  * size of blocks of content to be drawn, px
@@ -1943,7 +1951,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1943
1951
  return [track.id, { arcs }];
1944
1952
  }
1945
1953
  if (track.kind === 'bar') {
1946
- const max = track.max ?? 1;
1954
+ const max = track.scales?.y?.domainMax ?? 1;
1947
1955
  const values = skip(project(track, track.values ?? [], 0)).map(v => Math.min(1, Math.max(0, v / max)));
1948
1956
  return [track.id, { values }];
1949
1957
  }
@@ -1987,7 +1995,12 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
1987
1995
  height: this.trackHeight(track.kind, heightKey, track.height),
1988
1996
  barColor: track.color,
1989
1997
  arcColor: track.color,
1990
- customColorScheme: track.colors,
1998
+ customColorScheme: track.encoding?.color
1999
+ ? colorTable(track.encoding.color, [
2000
+ ...(track.encoding.color.domain ?? []),
2001
+ ...new Set(track.data),
2002
+ ])
2003
+ : undefined,
1991
2004
  data: content?.data,
1992
2005
  arcs: content?.arcs,
1993
2006
  },
@@ -2911,7 +2924,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2911
2924
  * #action
2912
2925
  */
2913
2926
  setColumnTracks(tracks) {
2914
- self.columnTracks.replace(tracks);
2927
+ self.columnTracks.replace(tracks.map(readColumnTrack));
2915
2928
  },
2916
2929
  /**
2917
2930
  * #action
@@ -2926,14 +2939,14 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
2926
2939
  * replace what the viewer's marks read from the row table
2927
2940
  */
2928
2941
  setEncodings(encodings) {
2929
- self.encodings.replace(encodings);
2942
+ self.encodings.replace(encodings.map(readEncoding));
2930
2943
  },
2931
2944
  /**
2932
2945
  * #action
2933
2946
  * replace the panels drawn between the tree and the alignment
2934
2947
  */
2935
2948
  setRowPanels(panels) {
2936
- self.rowPanels.replace(panels);
2949
+ self.rowPanels.replace(panels.map(readRowPanel));
2937
2950
  },
2938
2951
  /**
2939
2952
  * #action
@@ -3116,52 +3129,47 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
3116
3129
  * the categorical color keys drawn for this view, shared by the on-screen
3117
3130
  * legend overlay and the SVG export's reserved column. The domain overlay
3118
3131
  * produces the first, listing the `featureFill` scale where an encoding
3119
- * names one. Every field a row-table encoding or a row panel reads
3120
- * produces one more, so two channels over one field, or two strips over
3121
- * it, list that field once. A strip's `legend` names the title it lists
3122
- * under, so strips over eight fields with one set of colors list one
3123
- * legend between them
3132
+ * names one. Every scale a row-table encoding or a row panel reads
3133
+ * produces one more, keyed by its declaration, so two channels or two
3134
+ * strips declaring one scale list it once, and strips over eight fields
3135
+ * naming one `title` list one legend between them
3124
3136
  */
3125
3137
  get legends() {
3126
3138
  const { featureFillEncoding, fillPalette, visibleDomainTypes } = this;
3127
3139
  const byKey = new Map();
3140
+ const ids = new Set();
3128
3141
  // the accession palette keys on no field, so it takes a key of its own
3129
- const add = (field, entries, id) => {
3130
- if (entries.length === 0) {
3142
+ const add = (key, title, entries, overlay = false) => {
3143
+ const k = key ?? 'domains';
3144
+ if (entries.length === 0 || byKey.has(k)) {
3131
3145
  return;
3132
3146
  }
3133
- const key = field === undefined ? 'domains' : `field:${field}`;
3134
- const legend = byKey.get(key);
3135
- if (legend) {
3136
- const seen = new Set(legend.entries.map(e => e.id));
3137
- legend.entries.push(...entries.filter(e => !seen.has(e.id)));
3138
- }
3139
- else {
3140
- byKey.set(key, {
3141
- id: id ?? (field === undefined ? 'domains' : `rowData-${field}`),
3142
- title: field ?? 'Domains',
3143
- entries: [...entries],
3144
- });
3147
+ const base = overlay || key === undefined ? 'domains' : `rowData-${title}`;
3148
+ let id = base;
3149
+ for (let n = 2; ids.has(id); n++) {
3150
+ id = `${base}-${n}`;
3145
3151
  }
3152
+ ids.add(id);
3153
+ byKey.set(k, { id, title: title ?? 'Domains', entries });
3146
3154
  };
3147
3155
  // the overlay draws the domain key; with no columns it draws nothing,
3148
3156
  // and a features panel taking the same colors lists them instead
3149
3157
  if (self.actuallyShowDomains && self.numColumns > 0) {
3150
- add(featureFillEncoding?.field, featureFillEncoding
3158
+ add(featureFillEncoding?.key, featureFillEncoding?.title, featureFillEncoding
3151
3159
  ? featureFillEncoding.legend
3152
3160
  : visibleDomainTypes.map(d => ({
3153
3161
  id: d.accession,
3154
3162
  label: d.name,
3155
3163
  color: d.color ?? fillPalette[d.accession],
3156
- })), 'domains');
3164
+ })), true);
3157
3165
  }
3158
- for (const { channel, field, legend } of this.resolvedEncodings) {
3166
+ for (const { channel, key, title, legend } of this.resolvedEncodings) {
3159
3167
  if (!featureChannels.has(channel)) {
3160
- add(field, legend);
3168
+ add(key, title, legend);
3161
3169
  }
3162
3170
  }
3163
- for (const { legendTitle, legend } of this.rowPanelScales) {
3164
- add(legendTitle, legend);
3171
+ for (const { legendKey, legendTitle, legend } of this.rowPanelScales) {
3172
+ add(legendKey, legendTitle, legend);
3165
3173
  }
3166
3174
  return [...byKey.values()];
3167
3175
  },
@@ -3435,14 +3443,52 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
3435
3443
  * table or the encodings, never per row per frame.
3436
3444
  */
3437
3445
  get resolvedEncodings() {
3438
- const rows = Object.values(self.rowData);
3439
3446
  return self.encodings.map(encoding => ({
3440
3447
  ...encoding,
3441
- ...(featureChannels.has(encoding.channel)
3442
- ? resolveFeatureScale(encoding.field, encoding.scale, self.allAnnotations, self.filteredAnnotations)
3443
- : resolveScale(encoding.scale, rows.map(row => row?.[encoding.field]).filter(notEmpty))),
3448
+ ...this.scaleOf(encoding, featureChannels.has(encoding.channel) ? 'features' : 'rows'),
3444
3449
  }));
3445
3450
  },
3451
+ /**
3452
+ * #getter
3453
+ * every color object the encodings and the row panels declare, keyed
3454
+ * by declaration (`declarationKey`) and resolved once over the union
3455
+ * of the values its records' fields take, so a value two strips share
3456
+ * takes one color in both. A feature field reads every feature loaded,
3457
+ * so hiding a type in the filter dialog leaves the rest in their
3458
+ * colors, and its legend lists the values of the drawn features.
3459
+ */
3460
+ get resolvedScales() {
3461
+ const rows = Object.values(self.rowData);
3462
+ const featureValues = (list, field) => list.map(a => featureField(a, field)).filter(notEmpty);
3463
+ const use = (channel, table) => ({
3464
+ spec: channel,
3465
+ field: channel.field,
3466
+ table,
3467
+ ...(table === 'rows'
3468
+ ? {
3469
+ values: rows.map(row => row?.[channel.field]).filter(notEmpty),
3470
+ }
3471
+ : {
3472
+ values: featureValues(self.allAnnotations, channel.field),
3473
+ shown: featureValues(self.filteredAnnotations, channel.field),
3474
+ }),
3475
+ });
3476
+ return resolveShared([
3477
+ ...self.encodings.map(encoding => use(encoding, featureChannels.has(encoding.channel) ? 'features' : 'rows')),
3478
+ ...self.rowPanels.flatMap(panel => panel.kind === 'strip'
3479
+ ? [use(panel, 'rows')]
3480
+ : panel.encoding?.color
3481
+ ? [use(panel.encoding.color, 'features')]
3482
+ : []),
3483
+ ]);
3484
+ },
3485
+ /**
3486
+ * #method
3487
+ * the shared scale a record's color object resolves to
3488
+ */
3489
+ scaleOf(channel, table) {
3490
+ return this.resolvedScales.get(declarationKey({ spec: channel, field: channel.field, table }));
3491
+ },
3446
3492
  /**
3447
3493
  * #getter
3448
3494
  * each row panel's scale resolved against the values its field takes,
@@ -3457,7 +3503,7 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
3457
3503
  if (panel.kind === 'features') {
3458
3504
  const color = panel.encoding?.color;
3459
3505
  const encoding = color
3460
- ? resolveFeatureScale(color.field, color.scale, self.allAnnotations, annotations)
3506
+ ? { field: color.field, ...this.scaleOf(color, 'features') }
3461
3507
  : featureFillEncoding;
3462
3508
  const label = panel.encoding?.label;
3463
3509
  return {
@@ -3465,7 +3511,8 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
3465
3511
  x: panel.x,
3466
3512
  header: panel.header ?? '',
3467
3513
  field: encoding?.field,
3468
- legendTitle: encoding?.field,
3514
+ legendKey: encoding?.key,
3515
+ legendTitle: encoding?.title,
3469
3516
  colors: color
3470
3517
  ? featureColorMap(annotations, encoding, fillPalette)
3471
3518
  : this.featureColors,
@@ -3480,14 +3527,13 @@ function stateModelFactory({ maxInlineSnapshotBytes = defaultMaxInlineSnapshotBy
3480
3527
  })),
3481
3528
  };
3482
3529
  }
3483
- const { colorOf, legend } = resolveScale(panel.scale, Object.values(rowData)
3484
- .map(row => row?.[panel.field])
3485
- .filter(notEmpty));
3530
+ const { colorOf, legend, key, title } = this.scaleOf(panel, 'rows');
3486
3531
  return {
3487
3532
  kind: panel.kind,
3488
3533
  field: panel.field,
3489
3534
  header: panel.header ?? panel.field,
3490
- legendTitle: panel.legend ?? panel.field,
3535
+ legendKey: key,
3536
+ legendTitle: title,
3491
3537
  colors: colorByRow(rowData, panel.field, colorOf),
3492
3538
  legend,
3493
3539
  };