@jbrowse/core 5.0.0-beta.11 → 5.0.0-beta.13
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 +97 -75
- package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -0
- package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +14 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.d.ts +2 -0
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +5 -5
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +1 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.d.ts +3 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.js +2 -2
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.d.ts +2 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.js +7 -14
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +3 -3
- package/esm/BaseFeatureWidget/stateModelFactory.d.ts +7 -0
- package/esm/BaseFeatureWidget/stateModelFactory.js +11 -0
- package/esm/BaseFeatureWidget/types.d.ts +2 -0
- package/esm/PluginManager.d.ts +6 -3
- package/esm/PluginManager.js +6 -3
- package/esm/ReExports/coreModules.generated.js +291 -285
- package/esm/ReExports/coreWorkerModules.generated.js +288 -283
- package/esm/ReExports/list.js +7 -0
- package/esm/TextSearch/TextSearchManager.d.ts +3 -3
- package/esm/TextSearch/TextSearchManager.js +6 -13
- package/esm/configuration/configurationSchema.d.ts +33 -95
- package/esm/configuration/configurationSchema.js +50 -218
- package/esm/configuration/configurationSlot.d.ts +18 -64
- package/esm/configuration/configurationSlot.js +31 -97
- package/esm/configuration/formatDetailsConfigSchema.js +2 -0
- package/esm/configuration/getConf.d.ts +7 -36
- package/esm/configuration/getConf.js +7 -37
- package/esm/configuration/index.d.ts +3 -2
- package/esm/configuration/index.js +2 -1
- package/esm/configuration/partitionAdvanced.d.ts +12 -0
- package/esm/configuration/partitionAdvanced.js +42 -0
- package/esm/configuration/plot.d.ts +6 -0
- package/esm/configuration/plot.js +34 -1
- package/esm/configuration/readConfObject.d.ts +15 -17
- package/esm/configuration/readConfObject.js +19 -50
- package/esm/configuration/schemaTypes.js +5 -1
- package/esm/configuration/snapshotPreprocess.js +1 -3
- package/esm/configuration/types.d.ts +60 -159
- package/esm/pluggableElementTypes/DisplayType.d.ts +7 -0
- package/esm/pluggableElementTypes/DisplayType.js +7 -0
- package/esm/pluggableElementTypes/RpcMethodType.d.ts +7 -0
- package/esm/pluggableElementTypes/RpcMethodType.js +9 -0
- package/esm/pluggableElementTypes/addMenuItems.d.ts +2 -1
- package/esm/pluggableElementTypes/addMenuItems.js +2 -1
- package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +3 -2
- package/esm/pluggableElementTypes/models/BaseDisplayModel.js +5 -5
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +2 -4
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +2 -4
- package/esm/pluggableElementTypes/models/baseTrackConfig.js +3 -0
- package/esm/rpc/RpcRegistry.d.ts +2 -1
- package/esm/rpc/byteBudget.d.ts +7 -0
- package/esm/rpc/byteBudget.js +10 -0
- package/esm/rpc/coreRpcMethods.d.ts +1 -1
- package/esm/rpc/coreRpcMethods.js +1 -1
- package/esm/rpc/explainTransferError.js +3 -3
- package/esm/rpc/methods/CoreLoadTrackCode.d.ts +17 -0
- package/esm/rpc/methods/CoreLoadTrackCode.js +24 -0
- package/esm/ui/AppLogo.js +43 -3
- package/esm/ui/buildExtraTrackMenuItems.d.ts +2 -2
- package/esm/ui/colors.d.ts +41 -30
- package/esm/ui/colors.js +81 -44
- package/esm/ui/multiTrackMenuItems.d.ts +2 -2
- package/esm/util/alignedBytes.d.ts +19 -0
- package/esm/util/alignedBytes.js +37 -0
- package/esm/util/analytics.d.ts +5 -6
- package/esm/util/analytics.js +5 -3
- package/esm/util/bezierConnector.js +5 -1
- package/esm/util/categoricalField.d.ts +23 -3
- package/esm/util/categoricalField.js +36 -3
- package/esm/util/cellMatches.js +14 -14
- package/esm/util/cellsStep.d.ts +3 -5
- package/esm/util/cellsStep.js +31 -29
- package/esm/util/color-bits/parse.js +22 -11
- package/esm/util/colorRamp.js +6 -0
- package/esm/util/colorScale.d.ts +9 -2
- package/esm/util/colorScale.js +33 -2
- package/esm/util/colorSchemes.d.ts +4 -2
- package/esm/util/colorSchemes.js +4 -1
- package/esm/util/isoformRank.d.ts +39 -0
- package/esm/util/isoformRank.js +118 -0
- package/esm/util/markEncodingTypes.d.ts +3 -1
- package/esm/util/quantileExtent.d.ts +22 -0
- package/esm/util/quantileExtent.js +24 -1
- package/esm/util/systemColorScheme.d.ts +4 -0
- package/esm/util/systemColorScheme.js +21 -0
- package/esm/util/tabix.d.ts +1 -1
- package/esm/util/tabix.js +1 -1
- package/esm/util/temporaryAssembly.d.ts +2 -2
- package/esm/util/temporaryAssembly.js +2 -3
- package/esm/util/trackConfigDelta.d.ts +1 -1
- package/esm/util/tracks.d.ts +25 -15
- package/esm/util/tracks.js +67 -43
- package/esm/util/types/index.d.ts +15 -7
- package/esm/util/types/index.js +21 -2
- package/esm/util/useRowVirtualScroll.d.ts +1 -0
- package/package.json +7 -4
- package/esm/rpc/methods/CoreLoadAdapterCode.d.ts +0 -13
- package/esm/rpc/methods/CoreLoadAdapterCode.js +0 -16
package/README.md
CHANGED
|
@@ -235,11 +235,13 @@ a refName.
|
|
|
235
235
|
### categoricalField
|
|
236
236
|
|
|
237
237
|
`labels` names the `domain`'s values in a key, one each in order, where a config
|
|
238
|
-
spells them for a reader rather than as the data does.
|
|
238
|
+
spells them for a reader rather than as the data does. `held` deals each key the
|
|
239
|
+
domain does not list a color of its own on first sight (`categoricalScale`); the
|
|
240
|
+
caller keeps it for as long as the colors should hold.
|
|
239
241
|
|
|
240
242
|
```js
|
|
241
243
|
// type signature
|
|
242
|
-
(field: string, {…}?: {
|
|
244
|
+
(field: string, {…}?: {…}) => CategoricalField
|
|
243
245
|
```
|
|
244
246
|
|
|
245
247
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/categoricalField.ts)
|
|
@@ -250,7 +252,9 @@ One categorical field as every channel reads it — a facet's sections, a color'
|
|
|
250
252
|
range entries, a key's rows. `key` files a value, `compare` orders keys (the
|
|
251
253
|
`domain` first, the rest by `compareGroupKeys`, `''` after them), `label` names
|
|
252
254
|
a key in a legend, `sectionLabel` on a chip, and `color` paints it. A key's
|
|
253
|
-
color depends only on the key and the declaration, so every region agrees on it
|
|
255
|
+
color depends only on the key and the declaration, so every region agrees on it,
|
|
256
|
+
except that a field dealing its colors (`categoricalField`'s `held`) also steps
|
|
257
|
+
a key off the colors of the keys met before it.
|
|
254
258
|
|
|
255
259
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/categoricalField.ts)
|
|
256
260
|
|
|
@@ -343,7 +347,9 @@ matplotlib's perceptual ramps, dark at the low end; `juicebox` fades from
|
|
|
343
347
|
transparent to red, as Juicebox paints contacts; `fall` runs white through
|
|
344
348
|
yellow and red to black, as HiGlass does; `reds` and `blues` are ColorBrewer's,
|
|
345
349
|
from white; `redblue` and `purpleorange` diverge through white, ColorBrewer's
|
|
346
|
-
RdBu and PuOr
|
|
350
|
+
RdBu and PuOr; `redgreyblue` diverges through a grey middle, which stays visible
|
|
351
|
+
on a white page where a white middle would not, as the MAF display's identity
|
|
352
|
+
ramp paints it.
|
|
347
353
|
|
|
348
354
|
```js
|
|
349
355
|
// type signature
|
|
@@ -357,7 +363,8 @@ readonly[
|
|
|
357
363
|
'reds',
|
|
358
364
|
'blues',
|
|
359
365
|
'redblue',
|
|
360
|
-
'purpleorange'
|
|
366
|
+
'purpleorange',
|
|
367
|
+
'redgreyblue')
|
|
361
368
|
]
|
|
362
369
|
```
|
|
363
370
|
|
|
@@ -492,7 +499,9 @@ ramp is `range`'s CSS colours, evenly spaced, where it lists any, else the named
|
|
|
492
499
|
|
|
493
500
|
Replace the features with runs of constant depth: how many of them overlap each
|
|
494
501
|
stretch of the region, in a field `as` (`coverage` by default), with the
|
|
495
|
-
stretches nothing overlaps left out.
|
|
502
|
+
stretches nothing overlaps left out. A read counts across its whole span,
|
|
503
|
+
deletions and introns included; gap-aware depth stays the alignments display's
|
|
504
|
+
(ADR-118).
|
|
496
505
|
|
|
497
506
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/markEncodingTypes.ts)
|
|
498
507
|
|
|
@@ -503,11 +512,24 @@ siblings do, juicebox, fall, reds and blues do not.
|
|
|
503
512
|
|
|
504
513
|
```js
|
|
505
514
|
// type signature
|
|
506
|
-
(scheme: "blues" | "cividis" | "fall" | "inferno" | "juicebox" | "magma" | "purpleorange" | "redblue" | "reds" | "viridis") => boolean
|
|
515
|
+
(scheme: "blues" | "cividis" | "fall" | "inferno" | "juicebox" | "magma" | "purpleorange" | "redblue" | "redgreyblue" | "reds" | "viridis") => boolean
|
|
507
516
|
```
|
|
508
517
|
|
|
509
518
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/colorRamp.ts)
|
|
510
519
|
|
|
520
|
+
### dealKeyColors
|
|
521
|
+
|
|
522
|
+
Deals `keys` their colours in `field.compare` order, so the colours a set of
|
|
523
|
+
keys first seen together takes do not depend on the order they arrive in. A
|
|
524
|
+
no-op for a field that deals nothing.
|
|
525
|
+
|
|
526
|
+
```js
|
|
527
|
+
// type signature
|
|
528
|
+
(field: CategoricalField, keys: Iterable<string>) => void
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/categoricalField.ts)
|
|
532
|
+
|
|
511
533
|
### DEFAULT_COLOR_SCHEME
|
|
512
534
|
|
|
513
535
|
The ramp a continuous scale samples while it names no `range` and no `scheme`,
|
|
@@ -523,6 +545,7 @@ so a declaration spelling it out and one leaving it unset resolve alike.
|
|
|
523
545
|
'magma' |
|
|
524
546
|
'purpleorange' |
|
|
525
547
|
'redblue' |
|
|
548
|
+
'redgreyblue' |
|
|
526
549
|
'reds' |
|
|
527
550
|
'viridis'
|
|
528
551
|
```
|
|
@@ -596,6 +619,18 @@ categorical over a `category` column per label.
|
|
|
596
619
|
|
|
597
620
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/markEncoding.ts)
|
|
598
621
|
|
|
622
|
+
### evaluateForFeature
|
|
623
|
+
|
|
624
|
+
A config value as `feature` reads it: a `jexl:` callback evaluated against the
|
|
625
|
+
feature, anything else as written.
|
|
626
|
+
|
|
627
|
+
```js
|
|
628
|
+
// type signature
|
|
629
|
+
(value: unknown, feature: Feature, jexl: JexlInstance) => unknown
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/configuration/readConfObject.ts)
|
|
633
|
+
|
|
599
634
|
### FacetedLayer
|
|
600
635
|
|
|
601
636
|
One layer of a faceted request: its rows in section order, and the stacked row
|
|
@@ -660,6 +695,23 @@ the hover.
|
|
|
660
695
|
|
|
661
696
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/featureTable.ts)
|
|
662
697
|
|
|
698
|
+
### fenceOutliers
|
|
699
|
+
|
|
700
|
+
Where a value axis's open ends stand below a `domainQuantile` of 1: at the
|
|
701
|
+
`extremes`, unless one would stretch the axis past twice the span the
|
|
702
|
+
quantileExtent ends `quantiles` draw, 0 included where the axis reaches it
|
|
703
|
+
(`zero`); that end stops at the fence. So a plot with no spike shows every value
|
|
704
|
+
and cuts none, and a spike leaves the values under it at least half the axis.
|
|
705
|
+
Quantiles that meet span their own size, as a flat domain widens in
|
|
706
|
+
`getNiceDomain`.
|
|
707
|
+
|
|
708
|
+
```js
|
|
709
|
+
// type signature
|
|
710
|
+
({ extremes, quantiles, zero, }: { extremes: readonly [number, number]; quantiles: readonly [number, number]; zero: boolean; }) => [number, number]
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/quantileExtent.ts)
|
|
714
|
+
|
|
663
715
|
### fieldReader
|
|
664
716
|
|
|
665
717
|
What a channel reads off a feature: a field by name, a dotted path into a
|
|
@@ -756,15 +808,8 @@ mate must drop both rather than treat `<DEL>` as a refName.
|
|
|
756
808
|
|
|
757
809
|
### getConf
|
|
758
810
|
|
|
759
|
-
Reads a configuration value from a
|
|
760
|
-
|
|
761
|
-
`readConfObject` instead.
|
|
762
|
-
|
|
763
|
-
**This is exactly `readConfObject(model.configuration, path)`** — sugar for the
|
|
764
|
-
`.configuration` hop, and nothing more. The two readers carry the same slot-name
|
|
765
|
-
check, so reaching for the other one does not get a typo past tsc. It does not
|
|
766
|
-
consult the session and has no per-slot behavior; what you read is what the
|
|
767
|
-
track stores.
|
|
811
|
+
Reads a configuration value from a track or display state model: exactly
|
|
812
|
+
`readConfObject(model.configuration, path)`, with the same slot-name check.
|
|
768
813
|
|
|
769
814
|
```js
|
|
770
815
|
// type signature
|
|
@@ -901,6 +946,18 @@ module actually uses.
|
|
|
901
946
|
|
|
902
947
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/sessionServices.ts)
|
|
903
948
|
|
|
949
|
+
### heldSlotsOf
|
|
950
|
+
|
|
951
|
+
The `HeldSlots` `owner` keeps under `key`, made empty on first ask. Lives as
|
|
952
|
+
long as `owner` and is never persisted, so a reload deals afresh.
|
|
953
|
+
|
|
954
|
+
```js
|
|
955
|
+
// type signature
|
|
956
|
+
(owner: object, key: string) => HeldSlots
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/core/src/util/categoricalField.ts)
|
|
960
|
+
|
|
904
961
|
### HitIndexed
|
|
905
962
|
|
|
906
963
|
An encoded payload as a display stores it: the hit index the worker built with
|
|
@@ -922,27 +979,15 @@ at 0 for a mark with no value.
|
|
|
922
979
|
|
|
923
980
|
### hydrateTrackConfig
|
|
924
981
|
|
|
925
|
-
Hydrate a plain track config
|
|
926
|
-
to find the schema.
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
has not run, and nothing that walks a live node applies to it.
|
|
982
|
+
Hydrate a plain track config, such as a `session.tracks` entry, into a live
|
|
983
|
+
config node, dispatching on its `type` to find the schema. A plain entry holds
|
|
984
|
+
only what was authored: a slot at its default is absent and `preProcessSnapshot`
|
|
985
|
+
has not run.
|
|
930
986
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
Returns **undefined** when the config names a track type no plugin registered,
|
|
936
|
-
or when `create` rejects it as invalid. An un-hydrated config has never been
|
|
937
|
-
validated, so the dialog opening over it should not throw. Callers fall back to
|
|
938
|
-
using the plain object.
|
|
939
|
-
|
|
940
|
-
Shares `TrackConfigurationReference`'s per-PluginManager cache, so hydrating the
|
|
941
|
-
same entry twice returns the same node, and in an admin session a track opened
|
|
942
|
-
later resolves to that same node. A non-admin's open track resolves to the
|
|
943
|
-
session's private working copy (ADR-032), and this function returns the pristine
|
|
944
|
-
mirror beside it. The two have the same content; `CopyConfigEntryPoints.test.ts`
|
|
945
|
-
tests both cases.
|
|
987
|
+
Returns **undefined** when no plugin registered the type or `create` rejects the
|
|
988
|
+
config, which has never been validated; callers fall back to the plain object.
|
|
989
|
+
Hydrating one entry twice returns one node. A shown track resolves to the
|
|
990
|
+
session's working copy instead (ADR-032), which has the same content.
|
|
946
991
|
|
|
947
992
|
```js
|
|
948
993
|
// type signature
|
|
@@ -1384,6 +1429,11 @@ it itself. Under 0.5 reads as 0.5, where the ends meet rather than cross.
|
|
|
1384
1429
|
`scales.y.domainQuantile` and a colour's `domainQuantile` both name it.
|
|
1385
1430
|
`[Infinity, -Infinity]` where nothing is finite.
|
|
1386
1431
|
|
|
1432
|
+
Nearest rank keeps ties: where one value holds the rank, as on a segmented
|
|
1433
|
+
copy-number track, a ramp clipped below 1 paints nearly every value one colour.
|
|
1434
|
+
A value axis fences instead (fenceOutliers); a ramp takes the same fence once a
|
|
1435
|
+
colour default below 1 meets tied data.
|
|
1436
|
+
|
|
1387
1437
|
```js
|
|
1388
1438
|
// type signature
|
|
1389
1439
|
(values: ArrayLike<number>, count: number, quantile?: number) => [number, number]
|
|
@@ -1466,20 +1516,13 @@ widened domain with no table baked again.
|
|
|
1466
1516
|
|
|
1467
1517
|
### readConfObject
|
|
1468
1518
|
|
|
1469
|
-
|
|
1470
|
-
configuration
|
|
1471
|
-
model directly, e.g. an entry from `session.tracks`.
|
|
1472
|
-
|
|
1473
|
-
Wants a **live config node**, not a snapshot of one, and passing a snapshot is a
|
|
1474
|
-
type error. Slots are built with `types.stripDefault`, so a slot sitting at its
|
|
1475
|
-
default is absent from a snapshot — "unset" and "at its default" are
|
|
1476
|
-
indistinguishable there, and a read off one reports a default as missing.
|
|
1519
|
+
Read the value at a path of a live config node, such as a track's
|
|
1520
|
+
`configuration`, evaluating a `jexl:` callback with `args`.
|
|
1477
1521
|
|
|
1478
|
-
|
|
1479
|
-
`
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
indistinguishable at runtime from the broken spelling.
|
|
1522
|
+
A snapshot is refused by the types, since a slot at its default is absent from
|
|
1523
|
+
one. A `session.tracks` entry is one (`TrackConfigEntry`): read its `trackId`
|
|
1524
|
+
and `type` directly, and a slot through a helper that supplies the default, such
|
|
1525
|
+
as `getConfAssemblyNamesOrNone`.
|
|
1483
1526
|
|
|
1484
1527
|
```js
|
|
1485
1528
|
// type signature
|
|
@@ -1761,32 +1804,11 @@ array naming a sub-schema's member at any depth —
|
|
|
1761
1804
|
`setConf(self, ['scales', 'y', 'domainMin'], 5)`. Takes the display or track
|
|
1762
1805
|
model, or a config node directly.
|
|
1763
1806
|
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
**Prefer this over a bare `self.configuration.setSlot('x', v)`.** The constraint
|
|
1771
|
-
here mirrors `getConf`'s, so on a model with a concrete schema an unknown slot
|
|
1772
|
-
name is a compile error. `setSlot` itself stays untyped on purpose — the config
|
|
1773
|
-
editor's slot facade routes dynamic slot names through it
|
|
1774
|
-
(`configurationSchema.ts`) — and guards the name at runtime instead (ADR-052),
|
|
1775
|
-
so a misspelled write is diagnosed one way or the other.
|
|
1776
|
-
|
|
1777
|
-
**The read is the half with no diagnostic at all.** `getConf` for a name the
|
|
1778
|
-
schema doesn't declare returns `undefined` and reports nothing, at any layer, so
|
|
1779
|
-
the slot keeps reading as its default forever. Which makes the compile-time
|
|
1780
|
-
constraint worth keeping _reachable_: it is only as good as the schema of the
|
|
1781
|
-
holder handed in, and a holder widened to `AnyConfigurationModel` switches it
|
|
1782
|
-
off entirely — the trap a mixin casting to reach its host walks into. Every such
|
|
1783
|
-
cast names a concrete schema instead (`ConfigModelForFields`, or the base schema
|
|
1784
|
-
when the slot is the base's), and `HostChecksSlotNames` pins each one.
|
|
1785
|
-
|
|
1786
|
-
A wrong _value_ type still throws at runtime (MST type-checks the assignment)
|
|
1787
|
-
rather than at compile time. `value` is deliberately `unknown` because
|
|
1788
|
-
`undefined` is a legitimate write — it resets a slot to its schema default — and
|
|
1789
|
-
the declared slot value type doesn't include it.
|
|
1807
|
+
A path ending on a slot writes that slot; one ending on a sub-schema replaces
|
|
1808
|
+
the whole object, so a channel's members (a facet's field and its domain) move
|
|
1809
|
+
together. On a concrete schema an unknown slot name is a compile error, and
|
|
1810
|
+
`setSlot` refuses one at runtime (ADR-052). A wrong value type throws at
|
|
1811
|
+
runtime; `null` or `undefined` resets the slot to its default (ADR-146).
|
|
1790
1812
|
|
|
1791
1813
|
```js
|
|
1792
1814
|
// type signature
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Descriptors, FeatureFormatter, FieldActions } from '../types.tsx';
|
|
2
|
+
export declare function withoutLocations(attributes: Record<string, unknown>): Record<string, unknown>;
|
|
3
|
+
/** Whether `Attributes` given these `attributes` and `omit` draws any row. */
|
|
4
|
+
export declare function hasAttributeRows(attributes: Record<string, unknown>, omit?: string[]): boolean;
|
|
2
5
|
export default function Attributes(props: {
|
|
3
6
|
attributes: Record<string, unknown>;
|
|
4
7
|
omit?: string[];
|
|
@@ -51,7 +51,7 @@ function isLocation(value) {
|
|
|
51
51
|
}
|
|
52
52
|
// hideUris prunes the data rather than each render branch checking: an array
|
|
53
53
|
// of objects reaches the data grid and ArrayValue, which print locations too
|
|
54
|
-
function withoutLocations(attributes) {
|
|
54
|
+
export function withoutLocations(attributes) {
|
|
55
55
|
return Object.fromEntries(Object.entries(attributes)
|
|
56
56
|
.filter(([, value]) => !isLocation(value))
|
|
57
57
|
.map(([key, value]) => [key, valueWithoutLocations(value)]));
|
|
@@ -103,6 +103,19 @@ function measureLabels(attributes, opts) {
|
|
|
103
103
|
}
|
|
104
104
|
return widest;
|
|
105
105
|
}
|
|
106
|
+
/** Whether `Attributes` given these `attributes` and `omit` draws any row. */
|
|
107
|
+
export function hasAttributeRows(attributes, omit = []) {
|
|
108
|
+
const omits = new Set([...omit, ...globalOmit]);
|
|
109
|
+
return Object.entries(attributes).some(([key, value]) => value == null || omits.has(key)
|
|
110
|
+
? false
|
|
111
|
+
: Array.isArray(value)
|
|
112
|
+
? isHomogeneousObjectArray(value) ||
|
|
113
|
+
!isObjectArray(value) ||
|
|
114
|
+
value.some(entry => hasAttributeRows(entry))
|
|
115
|
+
: isObject(value)
|
|
116
|
+
? isUriLocation(value) || hasAttributeRows(value, omit)
|
|
117
|
+
: true);
|
|
118
|
+
}
|
|
106
119
|
function widestLabel(attributes, opts) {
|
|
107
120
|
return Math.ceil(measureLabels(attributes, opts)) + FIELD_NAME_PADDING;
|
|
108
121
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AbstractSessionModel, SimpleFeatureSerialized } from '../../util/index.ts';
|
|
2
|
+
import type { CanonicalTranscripts } from '../../util/isoformRank.ts';
|
|
2
3
|
import type { SequenceFeatureDetailsModel, SequenceHoverTarget } from './model.ts';
|
|
3
4
|
interface SequenceFeatureDetailsProps {
|
|
4
5
|
model: SequenceFeatureDetailsModel;
|
|
@@ -7,6 +8,7 @@ interface SequenceFeatureDetailsProps {
|
|
|
7
8
|
feature: SimpleFeatureSerialized;
|
|
8
9
|
hoverTarget?: SequenceHoverTarget;
|
|
9
10
|
showOpenInDialog?: boolean;
|
|
11
|
+
canonicalTranscripts?: CanonicalTranscripts;
|
|
10
12
|
}
|
|
11
13
|
declare function SequenceFeatureDetails(props: SequenceFeatureDetailsProps): import("react").JSX.Element;
|
|
12
14
|
export default SequenceFeatureDetails;
|
|
@@ -10,15 +10,15 @@ import { useSequenceFetch } from './useSequenceFetch.js';
|
|
|
10
10
|
// lazies
|
|
11
11
|
const SequenceBody = lazy(() => import('./SequenceBody.js'));
|
|
12
12
|
const SequenceDialog = lazy(() => import('./dialogs/SequenceDialog.js'));
|
|
13
|
-
const SequenceFeatureDetailsPanel = observer(function SequenceFeatureDetailsPanel({ model, session, assemblyName, feature, hoverTarget, showOpenInDialog = true, }) {
|
|
13
|
+
const SequenceFeatureDetailsPanel = observer(function SequenceFeatureDetailsPanel({ model, session, assemblyName, feature, hoverTarget, showOpenInDialog = true, canonicalTranscripts, }) {
|
|
14
14
|
const { upDownBp } = model;
|
|
15
15
|
const seqPanelRef = useRef(null);
|
|
16
16
|
// A container feature (e.g. a gene) has no CDS/exon of its own — one of its
|
|
17
|
-
// transcript children does. Default to the
|
|
18
|
-
// and Protein sequence types work without an extra click into
|
|
19
|
-
// with a selector to switch
|
|
17
|
+
// transcript children does. Default to the transcript the track draws first
|
|
18
|
+
// so CDS and Protein sequence types work without an extra click into
|
|
19
|
+
// Subfeatures, with a selector to switch when the gene has more than one.
|
|
20
20
|
const transcripts = getTranscripts(feature);
|
|
21
|
-
const [transcriptIndex, setTranscriptIndex] = useState(() => pickDefaultTranscriptIndex(transcripts));
|
|
21
|
+
const [transcriptIndex, setTranscriptIndex] = useState(() => pickDefaultTranscriptIndex(transcripts, canonicalTranscripts));
|
|
22
22
|
const effectiveFeature = transcripts[transcriptIndex] ?? feature;
|
|
23
23
|
// mode and revcomp are per-panel state, not on the shared model, so each
|
|
24
24
|
// subfeature panel (e.g. coding vs noncoding transcripts of one gene) picks
|
|
@@ -38,6 +38,6 @@ const SequenceFeaturePanel = observer(function SequenceFeaturePanel({ model, fea
|
|
|
38
38
|
feature,
|
|
39
39
|
viewAssemblyNames: model.view?.assemblyNames,
|
|
40
40
|
assemblyManager: getSession(model).assemblyManager,
|
|
41
|
-
}), feature: feature, hoverTarget: model }) })) : null] }));
|
|
41
|
+
}), feature: feature, hoverTarget: model, canonicalTranscripts: model.canonicalTranscripts }) })) : null] }));
|
|
42
42
|
});
|
|
43
43
|
export default SequenceFeaturePanel;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { AbstractSessionModel, SimpleFeatureSerialized } from '../../../util/index.ts';
|
|
2
|
+
import type { CanonicalTranscripts } from '../../../util/isoformRank.ts';
|
|
2
3
|
import type { SequenceHoverTarget } from '../model.ts';
|
|
3
|
-
declare const FeatureSequenceDialog: ({ feature, error, session, assemblyName, hoverTarget, handleClose, }: {
|
|
4
|
+
declare const FeatureSequenceDialog: ({ feature, error, session, assemblyName, hoverTarget, canonicalTranscripts, handleClose, }: {
|
|
4
5
|
feature: SimpleFeatureSerialized | undefined;
|
|
5
6
|
error?: unknown;
|
|
6
7
|
session: AbstractSessionModel;
|
|
7
8
|
assemblyName: string | undefined;
|
|
8
9
|
hoverTarget?: SequenceHoverTarget;
|
|
10
|
+
canonicalTranscripts?: CanonicalTranscripts;
|
|
9
11
|
handleClose: () => void;
|
|
10
12
|
}) => import("react").JSX.Element;
|
|
11
13
|
export default FeatureSequenceDialog;
|
|
@@ -17,13 +17,13 @@ const useStyles = makeStyles()({
|
|
|
17
17
|
// The settings model lives for as long as the dialog does: it holds no
|
|
18
18
|
// snapshot and reads no tree, so nothing has to carry one around on the chance
|
|
19
19
|
// a sequence panel is opened.
|
|
20
|
-
const FeatureSequenceDialog = observer(function FeatureSequenceDialog({ feature, error, session, assemblyName, hoverTarget, handleClose, }) {
|
|
20
|
+
const FeatureSequenceDialog = observer(function FeatureSequenceDialog({ feature, error, session, assemblyName, hoverTarget, canonicalTranscripts, handleClose, }) {
|
|
21
21
|
const { classes } = useStyles();
|
|
22
22
|
const [model] = useState(() => SequenceFeatureDetailsF().create());
|
|
23
23
|
return (_jsx(InfoDialog, { maxWidth: "xl", open: true, title: feature
|
|
24
24
|
? `Feature sequence - ${feature.name || feature.id || feature.type}`
|
|
25
25
|
: 'Feature sequence', onClose: () => {
|
|
26
26
|
handleClose();
|
|
27
|
-
}, children: _jsx("div", { className: classes.content, children: error ? (_jsx(ErrorBanner, { error: error })) : feature ? (_jsx(SequenceFeatureDetails, { model: model, session: session, assemblyName: assemblyName, feature: feature, hoverTarget: hoverTarget, showOpenInDialog: false })) : (_jsx(LoadingEllipses, { message: "Loading feature" })) }) }));
|
|
27
|
+
}, children: _jsx("div", { className: classes.content, children: error ? (_jsx(ErrorBanner, { error: error })) : feature ? (_jsx(SequenceFeatureDetails, { model: model, session: session, assemblyName: assemblyName, feature: feature, hoverTarget: hoverTarget, showOpenInDialog: false, canonicalTranscripts: canonicalTranscripts })) : (_jsx(LoadingEllipses, { message: "Loading feature" })) }) }));
|
|
28
28
|
});
|
|
29
29
|
export default FeatureSequenceDialog;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { SimpleFeatureSerialized, SimpleFeatureSerializedNoId } from '../../util/index.ts';
|
|
2
|
+
import type { CanonicalTranscripts } from '../../util/isoformRank.ts';
|
|
2
3
|
import type { SequenceDisplayMode, ShowCoordinatesMode } from './model.ts';
|
|
3
4
|
export declare function featureHasCDS(feature: SimpleFeatureSerializedNoId): boolean;
|
|
4
5
|
export declare function featureHasExon(feature: SimpleFeatureSerializedNoId): boolean;
|
|
5
6
|
export declare function featureHasExonOrCDS(feature: SimpleFeatureSerializedNoId): boolean;
|
|
6
7
|
export declare function getTranscripts(feature: SimpleFeatureSerialized): SimpleFeatureSerialized[];
|
|
7
|
-
export declare function pickDefaultTranscriptIndex(transcripts: SimpleFeatureSerialized[]): number;
|
|
8
|
+
export declare function pickDefaultTranscriptIndex(transcripts: SimpleFeatureSerialized[], canonical?: CanonicalTranscripts): number;
|
|
8
9
|
export declare function getDefaultMode(feature: SimpleFeatureSerializedNoId): SequenceDisplayMode;
|
|
9
10
|
export declare function modeHasUpDownstream(mode: SequenceDisplayMode): boolean;
|
|
10
11
|
export declare function modeSupportsRevcomp(mode: SequenceDisplayMode): boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_CANONICAL_TRANSCRIPTS, isoformScorer, rankIsoforms, serializedIsoformReader, } from '../../util/isoformRank.js';
|
|
1
2
|
// these predicates only ever read type/subfeatures, so they accept a bare
|
|
2
3
|
// subfeature (no guaranteed uniqueId) as readily as a full feature
|
|
3
4
|
export function featureHasCDS(feature) {
|
|
@@ -30,20 +31,12 @@ export function getTranscripts(feature) {
|
|
|
30
31
|
uniqueId: `${feature.uniqueId}_${idx}`,
|
|
31
32
|
})) ?? []);
|
|
32
33
|
}
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export function pickDefaultTranscriptIndex(transcripts) {
|
|
40
|
-
if (transcripts.length === 0) {
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
const coding = transcripts.filter(t => featureHasCDS(t));
|
|
44
|
-
const candidates = coding.length > 0 ? coding : transcripts;
|
|
45
|
-
const longest = candidates.reduce((a, b) => b.end - b.start > a.end - a.start ? b : a);
|
|
46
|
-
return transcripts.indexOf(longest);
|
|
34
|
+
// The isoform the canvas gene glyph draws first, so the panel opens on the
|
|
35
|
+
// transcript the track showed. A feature that is itself a transcript has no
|
|
36
|
+
// nested transcripts to pick from.
|
|
37
|
+
export function pickDefaultTranscriptIndex(transcripts, canonical = DEFAULT_CANONICAL_TRANSCRIPTS) {
|
|
38
|
+
const [best] = rankIsoforms(transcripts, isoformScorer(serializedIsoformReader, canonical));
|
|
39
|
+
return best ? transcripts.indexOf(best) : 0;
|
|
47
40
|
}
|
|
48
41
|
export function getDefaultMode(feature) {
|
|
49
42
|
return featureHasCDS(feature)
|
|
@@ -51,9 +51,9 @@ const CDNASequence = observer(function CDNASequence({ cds, exons, sequence, upst
|
|
|
51
51
|
}
|
|
52
52
|
// The stretches of the feature its own exons do not reach. Real annotations
|
|
53
53
|
// produce them: CAT/liftoff emits a transcript spanning the source alignment
|
|
54
|
-
// with exons only where the CDS is supported, so KCNT2 in
|
|
55
|
-
//
|
|
56
|
-
//
|
|
54
|
+
// with exons only where the CDS is supported, so KCNT2 in HG00099.1's HPRC CAT
|
|
55
|
+
// annotation runs 10,207 bp past its first exon, and 28 of the 438 transcripts
|
|
56
|
+
// around CFH do the same.
|
|
57
57
|
//
|
|
58
58
|
// Uncolored, like the inter-exon gaps they are — the annotation does not call
|
|
59
59
|
// them exonic, so stretching the first and last exon over them (which is what
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type PluginManager from '../PluginManager.ts';
|
|
2
2
|
import type { AnyConfigurationModel } from '../configuration/index.ts';
|
|
3
3
|
import type { ParentFeatureSummary, SimpleFeatureSerialized } from '../util/index.ts';
|
|
4
|
+
import type { CanonicalTranscripts } from '../util/isoformRank.ts';
|
|
4
5
|
import type { SequenceHoverPosition } from './SequenceFeatureDetails/model.ts';
|
|
5
6
|
import type { FormatDetailsTiers } from './formatDetails.ts';
|
|
6
7
|
import type { Descriptors, MaybeSerializedFeat } from './types.tsx';
|
|
@@ -88,6 +89,12 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
88
89
|
feature?: SimpleFeatureSerialized;
|
|
89
90
|
error?: Error;
|
|
90
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* #getter
|
|
94
|
+
* the isoform tags the open track's display ranks by, so the sequence
|
|
95
|
+
* panel opens on the transcript the track draws first
|
|
96
|
+
*/
|
|
97
|
+
readonly canonicalTranscripts: CanonicalTranscripts;
|
|
91
98
|
/**
|
|
92
99
|
* #getter
|
|
93
100
|
* levels of subfeature card the panel renders; unset means no limit
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getEnv, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
import { hydrateTrackConfig } from '../configuration/index.js';
|
|
3
3
|
import { getSession } from '../util/index.js';
|
|
4
|
+
import { DEFAULT_CANONICAL_TRANSCRIPTS, isCanonicalTranscriptsHost, } from '../util/isoformRank.js';
|
|
4
5
|
import { ElementId } from '../util/types/mst.js';
|
|
5
6
|
import { SequenceFeatureDetailsF } from './SequenceFeatureDetails/model.js';
|
|
6
7
|
import { applyFormatDetails, formatDetailsNumber } from './formatDetails.js';
|
|
@@ -116,6 +117,16 @@ export function stateModelFactory(pluginManager) {
|
|
|
116
117
|
return { error };
|
|
117
118
|
}
|
|
118
119
|
},
|
|
120
|
+
/**
|
|
121
|
+
* #getter
|
|
122
|
+
* the isoform tags the open track's display ranks by, so the sequence
|
|
123
|
+
* panel opens on the transcript the track draws first
|
|
124
|
+
*/
|
|
125
|
+
get canonicalTranscripts() {
|
|
126
|
+
const track = self.track;
|
|
127
|
+
return (track?.displays.find(isCanonicalTranscriptsHost)
|
|
128
|
+
?.canonicalTranscripts ?? DEFAULT_CANONICAL_TRANSCRIPTS);
|
|
129
|
+
},
|
|
119
130
|
/**
|
|
120
131
|
* #getter
|
|
121
132
|
* levels of subfeature card the panel renders; unset means no limit
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MenuItem } from '../ui/MenuTypes.ts';
|
|
2
|
+
import type { CanonicalTranscripts } from '../util/isoformRank.ts';
|
|
2
3
|
import type { SimpleFeatureSerialized } from '../util/simpleFeature.ts';
|
|
3
4
|
import type { SequenceFeatureDetailsModel, SequenceHoverTarget } from './SequenceFeatureDetails/model.ts';
|
|
4
5
|
import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
@@ -9,6 +10,7 @@ export interface FeatureDetailsModel extends IStateTreeNode, SequenceHoverTarget
|
|
|
9
10
|
view: {
|
|
10
11
|
assemblyNames: string[];
|
|
11
12
|
} | undefined;
|
|
13
|
+
canonicalTranscripts?: CanonicalTranscripts;
|
|
12
14
|
}
|
|
13
15
|
export interface Descriptors {
|
|
14
16
|
[key: string]: React.ReactNode | Descriptors;
|
package/esm/PluginManager.d.ts
CHANGED
|
@@ -375,9 +375,12 @@ export default class PluginManager {
|
|
|
375
375
|
* frozen object, so a hit can only ever come from this same instance and this
|
|
376
376
|
* same track type. See ADR-031.
|
|
377
377
|
*
|
|
378
|
-
* The node is never mutated:
|
|
379
|
-
*
|
|
380
|
-
*
|
|
378
|
+
* The node is never mutated: every edit goes to a private session working
|
|
379
|
+
* copy, not here, and is stored as a delta whose merged config is a new
|
|
380
|
+
* object (ADR-032). In a session composing `SessionTracksManagerSessionMixin`,
|
|
381
|
+
* which every product does, `track.configuration` resolves to that working
|
|
382
|
+
* copy, so this memo serves `hydrateTrackConfig` and sessions without the
|
|
383
|
+
* mixin. Both levels are `WeakMap`s so
|
|
381
384
|
* entries collect normally — no manual invalidation needed, which is why this
|
|
382
385
|
* is a memo and not a cache with a lifecycle to expose.
|
|
383
386
|
*/
|
package/esm/PluginManager.js
CHANGED
|
@@ -155,9 +155,12 @@ export default class PluginManager {
|
|
|
155
155
|
* frozen object, so a hit can only ever come from this same instance and this
|
|
156
156
|
* same track type. See ADR-031.
|
|
157
157
|
*
|
|
158
|
-
* The node is never mutated:
|
|
159
|
-
*
|
|
160
|
-
*
|
|
158
|
+
* The node is never mutated: every edit goes to a private session working
|
|
159
|
+
* copy, not here, and is stored as a delta whose merged config is a new
|
|
160
|
+
* object (ADR-032). In a session composing `SessionTracksManagerSessionMixin`,
|
|
161
|
+
* which every product does, `track.configuration` resolves to that working
|
|
162
|
+
* copy, so this memo serves `hydrateTrackConfig` and sessions without the
|
|
163
|
+
* mixin. Both levels are `WeakMap`s so
|
|
161
164
|
* entries collect normally — no manual invalidation needed, which is why this
|
|
162
165
|
* is a memo and not a cache with a lifecycle to expose.
|
|
163
166
|
*/
|