@jbrowse/core 5.0.0-beta.4 → 5.0.0-beta.8

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 (110) hide show
  1. package/README.md +278 -250
  2. package/esm/PluginManager.js +1 -1
  3. package/esm/ReExports/publicUi.d.ts +1 -1
  4. package/esm/ReExports/publicUi.js +1 -1
  5. package/esm/ReExports/publicUtil.d.ts +1 -1
  6. package/esm/ReExports/publicUtil.js +1 -1
  7. package/esm/ReExports/workerNamespaceNames.js +3 -9
  8. package/esm/assemblyManager/assemblyConfigSchema.d.ts +2 -1
  9. package/esm/assemblyManager/assemblyConfigSchema.js +2 -1
  10. package/esm/assemblyManager/expandAssemblyConfigShorthand.d.ts +15 -15
  11. package/esm/assemblyManager/expandAssemblyConfigShorthand.js +34 -18
  12. package/esm/configuration/configurationSchema.d.ts +1 -2
  13. package/esm/configuration/configurationSchema.js +3 -8
  14. package/esm/configuration/configurationSlot.d.ts +0 -47
  15. package/esm/configuration/configurationSlot.js +4 -54
  16. package/esm/configuration/fillLocations.d.ts +14 -0
  17. package/esm/configuration/fillLocations.js +14 -0
  18. package/esm/configuration/fullConfSnapshot.d.ts +0 -15
  19. package/esm/configuration/fullConfSnapshot.js +0 -28
  20. package/esm/configuration/getConf.d.ts +4 -34
  21. package/esm/configuration/getConf.js +3 -38
  22. package/esm/configuration/index.d.ts +3 -4
  23. package/esm/configuration/index.js +3 -2
  24. package/esm/configuration/schemaRegistry.d.ts +1 -2
  25. package/esm/configuration/schemaRegistry.js +1 -2
  26. package/esm/configuration/slotFacade.d.ts +1 -16
  27. package/esm/configuration/slotFacade.js +1 -2
  28. package/esm/configuration/tabixShorthand.js +3 -3
  29. package/esm/configuration/types.d.ts +4 -16
  30. package/esm/data_adapters/CytobandAdapter/configSchema.js +3 -4
  31. package/esm/pluggableElementTypes/DisplayType.d.ts +5 -3
  32. package/esm/pluggableElementTypes/DisplayType.js +3 -1
  33. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +30 -0
  34. package/esm/pluggableElementTypes/models/BaseTrackModel.js +45 -17
  35. package/esm/rpc/RpcRegistry.d.ts +7 -1
  36. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  37. package/esm/rpc/coreRpcMethods.js +1 -0
  38. package/esm/rpc/methods/CoreEncodeFeatures.d.ts +15 -0
  39. package/esm/rpc/methods/CoreEncodeFeatures.js +62 -0
  40. package/esm/svg/svgId.d.ts +1 -1
  41. package/esm/svg/svgId.js +1 -1
  42. package/esm/ui/LegendSwatchGlyph.js +26 -21
  43. package/esm/ui/MenuTypes.d.ts +5 -52
  44. package/esm/ui/SizeSliderRow.d.ts +1 -3
  45. package/esm/ui/SizeSliderRow.js +4 -6
  46. package/esm/ui/SvgColorLegend.d.ts +9 -3
  47. package/esm/ui/SvgColorLegend.js +74 -33
  48. package/esm/ui/colorScale.d.ts +64 -0
  49. package/esm/ui/colorScale.js +41 -0
  50. package/esm/ui/colors.d.ts +11 -0
  51. package/esm/ui/colors.js +7 -0
  52. package/esm/ui/index.d.ts +5 -7
  53. package/esm/ui/index.js +3 -13
  54. package/esm/ui/legendMenuItem.d.ts +5 -16
  55. package/esm/ui/legendMenuItem.js +3 -14
  56. package/esm/ui/legendSpec.d.ts +11 -5
  57. package/esm/ui/legendSpec.js +8 -11
  58. package/esm/ui/makeSizeMenu.d.ts +0 -6
  59. package/esm/ui/makeSizeMenu.js +6 -30
  60. package/esm/ui/measureLegendText.js +1 -1
  61. package/esm/ui/menuItemAdornment.d.ts +9 -19
  62. package/esm/ui/menuItemAdornment.js +10 -31
  63. package/esm/ui/menuItems.d.ts +2 -6
  64. package/esm/ui/menuItems.js +0 -4
  65. package/esm/ui/palette.js +2 -1
  66. package/esm/ui/toggleMenuItems.d.ts +6 -13
  67. package/esm/ui/toggleMenuItems.js +13 -29
  68. package/esm/util/colorRamp.d.ts +3 -3
  69. package/esm/util/colorRamp.js +2 -2
  70. package/esm/util/featureTransforms.d.ts +15 -0
  71. package/esm/util/featureTransforms.js +342 -0
  72. package/esm/util/freezeDeep.d.ts +8 -10
  73. package/esm/util/freezeDeep.js +8 -10
  74. package/esm/util/glyphNames.d.ts +8 -0
  75. package/esm/util/glyphNames.js +12 -0
  76. package/esm/util/index.d.ts +0 -1
  77. package/esm/util/index.js +0 -1
  78. package/esm/util/markEncoding.d.ts +76 -0
  79. package/esm/util/markEncoding.js +436 -0
  80. package/esm/util/markEncodingTypes.d.ts +331 -0
  81. package/esm/util/markEncodingTypes.js +8 -0
  82. package/esm/util/renderToStaticMarkup.js +1 -1
  83. package/esm/util/starFusion.d.ts +26 -0
  84. package/esm/util/starFusion.js +37 -0
  85. package/esm/util/trackConfigDelta.d.ts +4 -8
  86. package/esm/util/trackConfigDelta.js +2 -4
  87. package/esm/util/tracks.d.ts +7 -1
  88. package/esm/util/tracks.js +72 -7
  89. package/esm/util/types/index.d.ts +12 -4
  90. package/esm/util/whenViewSettled.d.ts +9 -0
  91. package/esm/util/whenViewSettled.js +12 -4
  92. package/package.json +13 -3
  93. package/esm/configuration/promotableDefaults.d.ts +0 -234
  94. package/esm/configuration/promotableDefaults.js +0 -395
  95. package/esm/configuration/promotablePin.d.ts +0 -65
  96. package/esm/configuration/promotablePin.js +0 -1
  97. package/esm/configuration/promotableResolve.d.ts +0 -108
  98. package/esm/configuration/promotableResolve.js +0 -117
  99. package/esm/configuration/promotableSlots.d.ts +0 -12
  100. package/esm/configuration/promotableSlots.js +0 -45
  101. package/esm/configuration/slotShape.d.ts +0 -12
  102. package/esm/configuration/slotShape.js +0 -84
  103. package/esm/ui/PinAdornment.d.ts +0 -5
  104. package/esm/ui/PinAdornment.js +0 -67
  105. package/esm/ui/SvgGradientLegend.d.ts +0 -26
  106. package/esm/ui/SvgGradientLegend.js +0 -26
  107. package/esm/ui/promotablePinCoverage.d.ts +0 -58
  108. package/esm/ui/promotablePinCoverage.js +0 -124
  109. package/esm/util/openDisplays.d.ts +0 -23
  110. package/esm/util/openDisplays.js +0 -79
@@ -1,117 +0,0 @@
1
- /**
2
- * @module
3
- * The read-time resolver behind `promotable` config slots — a small CSS cascade
4
- * for one slot: the track's own set value -> the session-wide default for its
5
- * display type -> the slot's `promotedBase`. `resolveSlotIn` is the whole of it;
6
- * `resolveConf` and the control builders in `promotableDefaults.ts` each read a
7
- * field off the `SlotResolution` it returns.
8
- *
9
- * Mechanism: `agent-docs/reference/DISPLAY_TYPE_DEFAULTS.md`. Two decisions this
10
- * file rests on: resolution is named at its call sites rather than folded into
11
- * `getConf` (ADR-046), and unset is the only inherit sentinel — so declaring
12
- * `promotedBase` is what makes a slot promotable (ADR-047, enforced by
13
- * `ConfigSlot`).
14
- *
15
- * Every comparison against a promoted or base value is `deepEqual`, never `===`:
16
- * an object-valued slot (`maybeFrozen`, e.g. alignments `colorBy`) reconstructs a
17
- * fresh value out of MST that is never `===` its stored twin, so `===` would read
18
- * it as permanently differing and no pin would ever light up.
19
- *
20
- * The gate a candidate value passes to count at all is `isUsableValue`, in
21
- * `slotShape.ts` — shared with `ConfigSlot`, which applies it to `promotedBase`
22
- * at construction so the tier this file falls back to is usable by construction.
23
- */
24
- import { deepEqual } from '../util/deepEqual.js';
25
- import { getSession } from '../util/mstUtils.js';
26
- import { getSlotDefinition } from './slotFacade.js';
27
- import { isUsableValue } from './slotShape.js';
28
- export function cascadeContextFor(self) {
29
- return {
30
- config: self.configuration,
31
- displayType: self.type,
32
- defaults: getSession(self),
33
- };
34
- }
35
- /**
36
- * What the slot literally holds, unevaluated — a stray `jexl:` string yields the
37
- * raw string rather than running it. Both callers ask "is the slot set, and to
38
- * what kind of thing?", which has to be answerable with no feature context.
39
- *
40
- * Deliberately not `readConfObject`: for a promotable slot this is the whole
41
- * stored read, and both of that reader's extra behaviors are unwanted here — it
42
- * evaluates a `jexl:` string (which `isUsableValue` refuses anyway) and it
43
- * snapshots an MST-node value, which a `maybe*` slot never holds.
44
- */
45
- export function storedSlotValue(config, slot) {
46
- return config[slot];
47
- }
48
- /**
49
- * Whether `value` could actually become this slot's promoted default — the same
50
- * `isUsableValue` gate the cascade puts both tiers through, asked ahead of time
51
- * about a value a caller is about to offer as a pin's on-value.
52
- *
53
- * Exists because a pin built over a value the gate refuses is inert in a way
54
- * nothing reports: `applyAndOfferDefault` writes it to the session store happily,
55
- * `resolveSlotIn` then drops it and every track keeps resolving to the tier
56
- * below, and `isPromotableDefault` compares the *raw* stored value, so the pin
57
- * draws outline forever while a dead key sits in the user's localStorage. See
58
- * `makePin`, which is the one caller.
59
- */
60
- export function isPromotableValue(config, slot, value) {
61
- return isUsableValue(getSlotDefinition(config, slot), value);
62
- }
63
- // The whole three-tier cascade for one slot, in one place. `resolveConf` is the
64
- // public reader over it, and the control builders in `promotableDefaults.ts`
65
- // read a field off it.
66
- export function resolveSlot(self, slot) {
67
- return resolveSlotIn(cascadeContextFor(self), slot);
68
- }
69
- // The cascade over explicit inputs. `resolveSlot` is the display-state spelling
70
- // of this and the one nearly every consumer wants; the direct form exists for
71
- // the config-only path (a track that isn't open has no display state — see
72
- // `CascadeContext`).
73
- export function resolveSlotIn(ctx, slot) {
74
- const def = getSlotDefinition(ctx.config, slot);
75
- // A plain slot would otherwise resolve silently wrong here — with no
76
- // `promotedBase` every tier collapses to `undefined` — and, through a control
77
- // builder, write a promoted default nothing ever reads. `getSlotDefinition`
78
- // already throws on a slot name that doesn't exist at all.
79
- //
80
- // Declaring `promotedBase` is what makes a slot promotable, so its absence is
81
- // the whole test; there is no separate flag that could disagree with it.
82
- if (def.promotedBase === undefined) {
83
- throw new Error(`config slot "${slot}" is not promotable`);
84
- }
85
- // the slot's CSS `initial`
86
- const base = def.promotedBase;
87
- // a session-wide fact, and `isPromotableDefault` (the pin's filled/outline
88
- // state) reports on the session, not on one display's view of it
89
- const promoted = ctx.defaults.getDisplayTypeDefault(ctx.displayType, slot);
90
- const own = storedSlotValue(ctx.config, slot);
91
- // A track is customized exactly when it holds a *usable* value — being unset
92
- // is the inherit sentinel, so "set to something the slot could hold" is the
93
- // whole test. Routing `own` through the same gate as a promoted default is
94
- // what makes a malformed or stale own value (a saved `colorBy` naming a
95
- // since-removed scheme) read as not-customized and degrade in lockstep,
96
- // instead of reaching a consumer that trusts every value it sees.
97
- const customized = isUsableValue(def, own);
98
- const inheritedValue = isUsableValue(def, promoted) ? promoted : base;
99
- // A customized value wins unconditionally, which is what makes the share bake
100
- // sufficient on its own: a baked value lands in the track's config, so the
101
- // recipient reads it as customized and their own promoted default never gets
102
- // consulted. The one case a bake cannot cover is the sender sitting at `base`
103
- // while the recipient has promoted something — nothing is baked (the value
104
- // *is* base, and `stripDefault` drops it from the snapshot), so the recipient
105
- // resolves it from their own cascade. That is accepted: a promoted default is
106
- // personal and local, exactly like the theme a session is viewed in, and the
107
- // alternative was a per-display `ignorePromotedDefaults` flag that also
108
- // detached received tracks from the recipient's own pins for good.
109
- const value = customized ? own : inheritedValue;
110
- return {
111
- base,
112
- customized,
113
- promoted,
114
- inherited: !customized && !deepEqual(value, base),
115
- value,
116
- };
117
- }
@@ -1,12 +0,0 @@
1
- import type { AnyConfigurationModel } from './types.ts';
2
- import type { IAnyType } from '@jbrowse/mobx-state-tree';
3
- /**
4
- * Takes a live config node *or* the schema type itself, for the reason
5
- * `getConfigurationSchemaDefinition` does: every resolver here holds a node,
6
- * while a caller enumerating registered display types
7
- * (`displayTypesWithPromotableSlots`) holds only `DisplayType.configSchema` and
8
- * has nothing to create a node from. Caching under whichever handle it was
9
- * given is safe — a schema registers both its outer `stripDefault` wrapper and
10
- * its inner model against the same slot table, so the two entries agree.
11
- */
12
- export declare function promotableSlotNames(configOrType: AnyConfigurationModel | IAnyType): ReadonlySet<string>;
@@ -1,45 +0,0 @@
1
- import { getType, isType } from '@jbrowse/mobx-state-tree';
2
- import { getConfigurationSchemaDefinition } from './schemaRegistry.js';
3
- import { isSlotDefinitionEntry } from './schemaTypes.js';
4
- // The `promotable` slot names of one config schema (includes slots inherited via
5
- // baseConfiguration — merged into the table at construction), cached by MST type
6
- // since a schema's slot table is fixed.
7
- //
8
- // Only the functions that genuinely *enumerate* promotable slots call this — the
9
- // worker-payload resolver, the badge diff, "clear every default", and
10
- // `assertNoPromotableSlots`. It is deliberately NOT on any read path:
11
- // `getConf` used to consult it on all ~1300 config reads in the repo to decide
12
- // whether to cascade, which cost a `getType` per read (measured at ~60% overhead
13
- // over `readConfObject`) to serve the ~15 promotable ones. `resolveConf` names
14
- // the cascade at the call site instead, so there is nothing to look up.
15
- //
16
- // Its own module, and a leaf one, because `ui/promotablePinCoverage.ts` wants
17
- // the slot names and nothing else: reaching them through a module that also held
18
- // `readConfObject` pulled jexl and the whole read path into the ui graph. See the
19
- // note on that export in `ui/index.ts`.
20
- const promotableSlotsByType = new WeakMap();
21
- /**
22
- * Takes a live config node *or* the schema type itself, for the reason
23
- * `getConfigurationSchemaDefinition` does: every resolver here holds a node,
24
- * while a caller enumerating registered display types
25
- * (`displayTypesWithPromotableSlots`) holds only `DisplayType.configSchema` and
26
- * has nothing to create a node from. Caching under whichever handle it was
27
- * given is safe — a schema registers both its outer `stripDefault` wrapper and
28
- * its inner model against the same slot table, so the two entries agree.
29
- */
30
- export function promotableSlotNames(configOrType) {
31
- const type = isType(configOrType) ? configOrType : getType(configOrType);
32
- const cached = promotableSlotsByType.get(type);
33
- if (cached) {
34
- return cached;
35
- }
36
- const names = new Set();
37
- const table = getConfigurationSchemaDefinition(configOrType);
38
- for (const [name, def] of Object.entries(table ?? {})) {
39
- if (isSlotDefinitionEntry(def) && def.promotedBase !== undefined) {
40
- names.add(name);
41
- }
42
- }
43
- promotableSlotsByType.set(type, names);
44
- return names;
45
- }
@@ -1,12 +0,0 @@
1
- import type { ConfigSlotDefinition } from './configurationSlot.ts';
2
- /**
3
- * Whether a stored value could really be a value of this slot. An unusable value
4
- * is dropped by the cascade, so the getter, the pin and the badge all fall back
5
- * in lockstep.
6
- *
7
- * The `jexl:` check is the only place the subsystem handles a callback, and it
8
- * handles it by refusing it — a hand-edited config or default store is untyped,
9
- * and a callback string would otherwise reach a renderer as a literal value.
10
- * DISPLAY_TYPE_DEFAULTS.md §"No callbacks".
11
- */
12
- export declare function isUsableValue(def: ConfigSlotDefinition, value: unknown): boolean;
@@ -1,84 +0,0 @@
1
- /**
2
- * @module
3
- * The one gate a candidate value for a `promotable` slot has to pass. Shared,
4
- * deliberately, by the two places that ask the question:
5
- *
6
- * - `promotableResolve.ts` at read time, for both cascade tiers — a session-wide
7
- * promoted default and a track's own value out of an untyped saved snapshot;
8
- * - `configurationSlot.ts` at construction, for the slot's own `promotedBase`.
9
- *
10
- * One implementation is what makes "the base is always a usable value" true by
11
- * construction rather than assumed: the resolver falls back to `promotedBase`
12
- * whenever a tier fails this gate, so a base that would itself fail has nowhere
13
- * left to fall.
14
- */
15
- import { getEnumerationValues } from '../util/mst-reflection.js';
16
- import { isCallbackValue } from './slotValueUtils.js';
17
- // Per-slot-type JS shape checks, for the slot types whose value needs more than
18
- // a `typeof` against `promotedBase`. Only the `maybe*` types appear, because
19
- // `ConfigSlot` rejects a `promotedBase` on anything else and this table is only
20
- // ever consulted for a promotable slot. Any type absent here (`maybeBoolean`,
21
- // `maybeColor`, `maybeFrozen`) falls through to the `promotedBase`-derived check
22
- // in `matchesSlotShape`.
23
- const SHAPE_CHECKS = {
24
- // a bare `typeof value === 'number'` would admit `NaN`/`±Infinity`, which no
25
- // slot legitimately holds
26
- maybeNumber: value => Number.isFinite(value),
27
- // a `maybeStringEnum` with no `model` can't be membership-checked; reject
28
- // rather than admit any string. `def.model` is the author's plain enumeration
29
- // (ConfigSlot adds the nullability), so this reads the real vocabulary.
30
- maybeStringEnum: (value, def) => !!def.model &&
31
- typeof value === 'string' &&
32
- getEnumerationValues(def.model).includes(value),
33
- };
34
- /**
35
- * Whether `value` has a JS shape this slot could hold. Guards the untyped
36
- * session store / saved snapshot against garbage; not a full validation
37
- * (`validate` layers semantics on top).
38
- *
39
- * Module-private on purpose: `isUsableValue` is the gate, and this is only one
40
- * of its four checks. A caller reaching for the shape test alone would skip the
41
- * `jexl:` refusal and the slot's `validate` hook — the two checks that exist
42
- * precisely because a shape-valid value can still be unusable.
43
- *
44
- * Keyed off `promotedBase` because that is the only concrete specimen of the
45
- * slot's value space a promotable slot declares — `defaultValue` is always the
46
- * inherit sentinel, so keying off it would demand `typeof value === 'undefined'`.
47
- * A `promotedBase` checked against itself therefore only exercises the
48
- * `SHAPE_CHECKS` entries, which is exactly the half that can be authored wrong
49
- * (an enum member that isn't in the vocabulary, a non-finite number).
50
- *
51
- * Can't delegate to the slot's MST `model.is(value)`, which is too permissive
52
- * exactly where this guard matters — `slotShape.test.ts` pins the two cases.
53
- */
54
- function matchesSlotShape(def, value) {
55
- const { promotedBase } = def;
56
- const check = SHAPE_CHECKS[def.type];
57
- return check
58
- ? check(value, def)
59
- : typeof promotedBase === 'object' && promotedBase !== null
60
- ? // object/array slot (e.g. `colorBy`): match null-ness and array-ness —
61
- // a bare `typeof` compare would admit `null` (typeof null === 'object')
62
- // and an array against an object base
63
- typeof value === 'object' &&
64
- value !== null &&
65
- Array.isArray(value) === Array.isArray(promotedBase)
66
- : typeof value === typeof promotedBase;
67
- }
68
- /**
69
- * Whether a stored value could really be a value of this slot. An unusable value
70
- * is dropped by the cascade, so the getter, the pin and the badge all fall back
71
- * in lockstep.
72
- *
73
- * The `jexl:` check is the only place the subsystem handles a callback, and it
74
- * handles it by refusing it — a hand-edited config or default store is untyped,
75
- * and a callback string would otherwise reach a renderer as a literal value.
76
- * DISPLAY_TYPE_DEFAULTS.md §"No callbacks".
77
- */
78
- export function isUsableValue(def, value) {
79
- const { validate } = def;
80
- return (value !== undefined &&
81
- !isCallbackValue(value) &&
82
- matchesSlotShape(def, value) &&
83
- (!validate || validate(value)));
84
- }
@@ -1,5 +0,0 @@
1
- import type { MenuItemPin } from './MenuTypes.ts';
2
- export declare function PinAdornment({ pin, disabled, }: {
3
- pin: MenuItemPin;
4
- disabled?: boolean;
5
- }): import("react").JSX.Element;
@@ -1,67 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import PushPinIcon from '@mui/icons-material/PushPin';
3
- import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
4
- import { ToggleButton, Tooltip } from '@mui/material';
5
- import { makeStyles } from '../util/tss-react/index.js';
6
- const useStyles = makeStyles()(theme => ({
7
- // compact enough to sit inline with the value check: drop the default
8
- // ToggleButton border/padding so it's ~icon-sized, but keep the selected
9
- // background tint (an unmistakable on/off signal, unlike a hollow-vs-filled
10
- // icon alone)
11
- button: {
12
- border: 0,
13
- padding: theme.spacing(0.25),
14
- },
15
- // the Tooltip listens on this rather than on the button, which fires no
16
- // events once the row disables it
17
- wrapper: {
18
- display: 'inline-flex',
19
- },
20
- }));
21
- // A toggle pin sits on a checkbox row: the copy names the state the click
22
- // applies, and the fill mirrors the checkbox. A value pin's on-value IS what
23
- // the label says — a radio option ("Compact"), a size row whose caller folds
24
- // the value into the label ("Line width (2px)") — and there the fill means the
25
- // value is the promoted default, so a click clears it.
26
- //
27
- // The aria-label carries "of this type" wherever the tooltip does: it used to
28
- // stop at "for all open tracks", stating a wider blast radius than the control
29
- // has.
30
- function pinCopy(label, control) {
31
- const { kind, active } = control;
32
- return kind === 'toggle'
33
- ? {
34
- title: `Turn ${label} ${active ? 'off' : 'on'} for all open tracks of this type`,
35
- ariaLabel: `turn ${label} ${active ? 'off' : 'on'} for all open tracks of this type`,
36
- }
37
- : active
38
- ? {
39
- title: `${label} is the default for all tracks of this type (click to clear)`,
40
- ariaLabel: `clear the default for ${label} for all tracks of this type`,
41
- }
42
- : {
43
- title: `Apply ${label} to all open tracks of this type`,
44
- ariaLabel: `apply ${label} to all open tracks of this type`,
45
- };
46
- }
47
- // Trailing pin for a promotable setting, drawn beside the value check from the
48
- // row's `pin` declaration (`menuItemAdornment` builds this; a row never
49
- // constructs it, which is what keeps MUI out of the eager menu-builder graph). A
50
- // ToggleButton (native button a11y + a clear selected tint) with a pin —
51
- // distinct from the value checkbox.
52
- //
53
- // One click writes the value into every open track of this display type and
54
- // raises a snackbar offering to keep it as the display type's default, so a
55
- // default takes two deliberate clicks (ADR-048). Always shown so the capability
56
- // is discoverable. stopPropagation keeps the click off the row value / menu
57
- // dismissal. "of this type" because a promoted default is scoped to the display
58
- // type (e.g. every LinearAlignmentsDisplay), not literally all tracks.
59
- export function PinAdornment({ pin, disabled, }) {
60
- const { classes } = useStyles();
61
- const { label, control } = pin;
62
- const { title, ariaLabel } = pinCopy(label, control);
63
- return (_jsx(Tooltip, { title: title, children: _jsx("span", { className: classes.wrapper, children: _jsx(ToggleButton, { className: classes.button, value: "default", disabled: disabled, selected: control.active, color: "primary", size: "small", "aria-label": ariaLabel, onChange: e => {
64
- e.stopPropagation();
65
- control.toggle();
66
- }, children: control.active ? (_jsx(PushPinIcon, { fontSize: "small" })) : (_jsx(PushPinOutlinedIcon, { fontSize: "small" })) }) }) }));
67
- }
@@ -1,26 +0,0 @@
1
- export interface GradientStop {
2
- offset: string;
3
- color: string;
4
- opacity?: number;
5
- }
6
- export interface GradientLabel {
7
- text: string;
8
- position: 'start' | 'middle' | 'end';
9
- }
10
- export declare const GRADIENT_LEGEND_WIDTH = 120;
11
- export declare const GRADIENT_LEGEND_HEIGHT = 40;
12
- export declare const GRADIENT_LEGEND_SVG_AREA_WIDTH: number;
13
- export default function SvgGradientLegend({ gradientId, stops, labels, title, x, y, width, height, barWidth, barHeight, padding, fontSize, }: {
14
- gradientId: string;
15
- stops: GradientStop[];
16
- labels: GradientLabel[];
17
- title?: string;
18
- x?: number;
19
- y?: number;
20
- width?: number;
21
- height?: number;
22
- barWidth?: number;
23
- barHeight?: number;
24
- padding?: number;
25
- fontSize?: number;
26
- }): import("react").JSX.Element;
@@ -1,26 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /* eslint-disable react-refresh/only-export-components -- layout constants belong with this leaf SVG primitive; no component state to fast-refresh */
3
- import { svgSafeId } from '../svg/svgId.js';
4
- // Default box/bar geometry shared by the LD and Hi-C legends. Exported so
5
- // callers that size an outer container (an <svg> wrapper) or compute an offset
6
- // stay in lockstep with what this component draws.
7
- export const GRADIENT_LEGEND_WIDTH = 120;
8
- export const GRADIENT_LEGEND_HEIGHT = 40;
9
- // Horizontal space a display should reserve (via `svgLegendWidth()`) to park
10
- // this legend beside the plot in an SVG export: the bar itself plus a 10px
11
- // margin either side.
12
- export const GRADIENT_LEGEND_SVG_AREA_WIDTH = GRADIENT_LEGEND_WIDTH + 20;
13
- // Shared SVG color-ramp key: a rounded translucent box, a horizontal gradient
14
- // bar, and labels below it. Used by any display that colors by a continuous
15
- // scale (Hi-C contact counts, LD r²/D'). Renders a <g>; position via x/y.
16
- export default function SvgGradientLegend({ gradientId, stops, labels, title, x = 0, y = 0, width = GRADIENT_LEGEND_WIDTH, height = GRADIENT_LEGEND_HEIGHT, barWidth = 100, barHeight = 12, padding = 8, fontSize = 10, }) {
17
- const titleGap = title ? fontSize + 4 : 0;
18
- const barY = padding + titleGap;
19
- const labelY = barY + barHeight + fontSize + 2;
20
- const safeGradientId = svgSafeId(gradientId);
21
- return (_jsxs("g", { transform: `translate(${x}, ${y})`, children: [_jsx("defs", { children: _jsx("linearGradient", { id: safeGradientId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: stops.map(stop => (_jsx("stop", { offset: stop.offset, style: { stopColor: stop.color, stopOpacity: stop.opacity ?? 1 } }, stop.offset))) }) }), _jsx("rect", { x: 0, y: 0, width: width, height: height + titleGap, fill: "rgba(255,255,255,0.95)", stroke: "#ccc", strokeWidth: 1, rx: 4 }), title ? (_jsx("text", { x: padding, y: padding + fontSize, fontSize: fontSize, fill: "black", children: title })) : null, _jsx("rect", { x: padding, y: barY, width: barWidth, height: barHeight, fill: `url(#${safeGradientId})`, rx: 2 }), labels.map(label => (_jsx("text", { x: label.position === 'start'
22
- ? padding
23
- : label.position === 'middle'
24
- ? padding + barWidth / 2
25
- : padding + barWidth, y: labelY, fontSize: fontSize, fill: "black", textAnchor: label.position === 'start' ? undefined : label.position, children: label.text }, `${label.position}-${label.text}`)))] }));
26
- }
@@ -1,58 +0,0 @@
1
- import type { ResolvableDisplay } from '../configuration/promotableResolve.ts';
2
- import type { MenuItem } from './MenuTypes.ts';
3
- import type { IAnyType } from '@jbrowse/mobx-state-tree';
4
- /**
5
- * The registered display types whose config schema declares a promotable slot,
6
- * sorted — the set a pin-coverage check has to reach, answered off the
7
- * `DisplayType`s themselves before anything is opened.
8
- *
9
- * The other half of the same gap `promotableSlotsWithoutPin` covers, and the
10
- * half that has no symptom at all: that function reports on a display it was
11
- * *given*, so a display type no fixture ever opens is not checked and does not
12
- * say so. Six display types declaring `showLegend` sat outside the check's
13
- * reach with nothing recording it.
14
- *
15
- * Takes the display types rather than a `PluginManager`, so core doesn't have
16
- * to name a plugin registry to answer a question about config schemas.
17
- */
18
- export declare function displayTypesWithPromotableSlots(displayTypes: readonly {
19
- name: string;
20
- configSchema: IAnyType;
21
- }[]): string[];
22
- /**
23
- * Every promotable slot some row of `items` carries a pin for, submenus
24
- * included.
25
- *
26
- * Reads `pin.control.slot` and nothing else. A raw `endAdornment` is
27
- * deliberately not counted: it is an arbitrary element (synteny's colour
28
- * swatch), so it cannot say which slot it promotes, and a pin built by hand
29
- * rather than through `makePin`/`makeTogglePin` is a thing to find, not to
30
- * accept.
31
- */
32
- export declare function pinnedSlots(items: MenuItem[]): Set<string>;
33
- /**
34
- * Rows whose pin is the wrong kind for the row, submenus included: a checkbox
35
- * row carrying a value pin, or a radio row carrying a toggle pin. Empty is the
36
- * healthy answer.
37
- *
38
- * The row builders type the option so this cannot happen through them; this
39
- * is for a `pin` written by hand as a literal, which the type system lets
40
- * through as the union. A checkbox row's pin has to be the toggle kind because
41
- * that is what a click beside a checkbox is read as — "turn this on for all" —
42
- * and a value pin there applies the row's *current* state instead, which
43
- * beside an unchecked box is a visible no-op. That shipped once (2026-09-07)
44
- * and was caught in a live session, not by a test; this is the test.
45
- */
46
- export declare function misKindedPins(items: MenuItem[]): string[];
47
- /**
48
- * A display's promotable slots that its own track menu offers no pin for,
49
- * sorted. Empty is the healthy answer.
50
- *
51
- * Takes the built menu rather than calling `trackMenuItems()` itself, because
52
- * what a menu contains depends on the display's current state — a row can be
53
- * gated on a rendering type (wiggle line width), on a parent toggle (the sashimi
54
- * options), or on data having arrived. A caller that wants the whole surface has
55
- * to drive the display into the states that reveal it, and that is a decision
56
- * this function should not make silently.
57
- */
58
- export declare function promotableSlotsWithoutPin(display: ResolvableDisplay, menuItems: MenuItem[]): string[];
@@ -1,124 +0,0 @@
1
- /**
2
- * @module
3
- * The read side of the promotable pin: given a display, which of its promotable
4
- * slots its track menu actually offers a pin for.
5
- *
6
- * This exists because the two halves of the feature are declared in different
7
- * places and nothing joins them. Declaring `promotedBase` is a **schema** fact
8
- * that travels down `baseConfiguration` to every subclass; the pin is a **menu**
9
- * fact, built by whichever `trackMenuItems()` happens to construct a row for that
10
- * slot. A display that inherits the slot and curates its own menu therefore ends
11
- * up with a promotable slot that has no pin anywhere — and since a promoted default
12
- * is keyed by *display type*, no other display's pin can write its key either, so
13
- * the slot resolves to `promotedBase` forever unless a track customizes it.
14
- *
15
- * Nothing throws, nothing renders differently, and the config docs go on
16
- * advertising the setting as having a session-wide default. The state was tracked
17
- * by hand in `agent-docs/reference/DISPLAY_TYPE_DEFAULTS.md` and drifted twice
18
- * before this was written — once gaining a row for a slot that had been deleted,
19
- * once missing a slot that had been added.
20
- *
21
- * `products/jbrowse-web/src/tests/PromotablePinCoverage.test.ts` is the guard: it
22
- * opens one display of every type that declares a promotable slot and holds the
23
- * result against a baseline.
24
- */
25
- import { promotableSlotNames } from '../configuration/promotableSlots.js';
26
- import { resolveSubMenu } from './MenuTypes.js';
27
- /**
28
- * The registered display types whose config schema declares a promotable slot,
29
- * sorted — the set a pin-coverage check has to reach, answered off the
30
- * `DisplayType`s themselves before anything is opened.
31
- *
32
- * The other half of the same gap `promotableSlotsWithoutPin` covers, and the
33
- * half that has no symptom at all: that function reports on a display it was
34
- * *given*, so a display type no fixture ever opens is not checked and does not
35
- * say so. Six display types declaring `showLegend` sat outside the check's
36
- * reach with nothing recording it.
37
- *
38
- * Takes the display types rather than a `PluginManager`, so core doesn't have
39
- * to name a plugin registry to answer a question about config schemas.
40
- */
41
- export function displayTypesWithPromotableSlots(displayTypes) {
42
- return displayTypes
43
- .filter(d => promotableSlotNames(d.configSchema).size > 0)
44
- .map(d => d.name)
45
- .sort((a, b) => a.localeCompare(b));
46
- }
47
- /**
48
- * Every promotable slot some row of `items` carries a pin for, submenus
49
- * included.
50
- *
51
- * Reads `pin.control.slot` and nothing else. A raw `endAdornment` is
52
- * deliberately not counted: it is an arbitrary element (synteny's colour
53
- * swatch), so it cannot say which slot it promotes, and a pin built by hand
54
- * rather than through `makePin`/`makeTogglePin` is a thing to find, not to
55
- * accept.
56
- */
57
- export function pinnedSlots(items) {
58
- const found = new Set();
59
- const walk = (list) => {
60
- for (const item of list) {
61
- if ('pin' in item && item.pin) {
62
- found.add(item.pin.control.slot);
63
- }
64
- if ('subMenu' in item) {
65
- walk(resolveSubMenu(item));
66
- }
67
- }
68
- };
69
- walk(items);
70
- return found;
71
- }
72
- /**
73
- * Rows whose pin is the wrong kind for the row, submenus included: a checkbox
74
- * row carrying a value pin, or a radio row carrying a toggle pin. Empty is the
75
- * healthy answer.
76
- *
77
- * The row builders type the option so this cannot happen through them; this
78
- * is for a `pin` written by hand as a literal, which the type system lets
79
- * through as the union. A checkbox row's pin has to be the toggle kind because
80
- * that is what a click beside a checkbox is read as — "turn this on for all" —
81
- * and a value pin there applies the row's *current* state instead, which
82
- * beside an unchecked box is a visible no-op. That shipped once (2026-09-07)
83
- * and was caught in a live session, not by a test; this is the test.
84
- */
85
- export function misKindedPins(items) {
86
- const found = [];
87
- const walk = (list) => {
88
- for (const item of list) {
89
- if ('pin' in item && item.pin && 'type' in item) {
90
- const { kind } = item.pin.control;
91
- const wanted = item.type === 'checkbox'
92
- ? 'toggle'
93
- : item.type === 'radio'
94
- ? 'value'
95
- : undefined;
96
- if (wanted && kind !== wanted) {
97
- found.push(`${item.pin.label}: ${kind} pin on a ${item.type} row`);
98
- }
99
- }
100
- if ('subMenu' in item) {
101
- walk(resolveSubMenu(item));
102
- }
103
- }
104
- };
105
- walk(items);
106
- return found;
107
- }
108
- /**
109
- * A display's promotable slots that its own track menu offers no pin for,
110
- * sorted. Empty is the healthy answer.
111
- *
112
- * Takes the built menu rather than calling `trackMenuItems()` itself, because
113
- * what a menu contains depends on the display's current state — a row can be
114
- * gated on a rendering type (wiggle line width), on a parent toggle (the sashimi
115
- * options), or on data having arrived. A caller that wants the whole surface has
116
- * to drive the display into the states that reveal it, and that is a decision
117
- * this function should not make silently.
118
- */
119
- export function promotableSlotsWithoutPin(display, menuItems) {
120
- const pinned = pinnedSlots(menuItems);
121
- return [...promotableSlotNames(display.configuration)]
122
- .filter(slot => !pinned.has(slot))
123
- .sort((a, b) => a.localeCompare(b));
124
- }
@@ -1,23 +0,0 @@
1
- import type { ResolvableDisplay } from '../configuration/promotableResolve.ts';
2
- import type { AbstractSessionModel } from './types/index.ts';
3
- /**
4
- * #api core/util
5
- * Every display on an open track, across all open views — the reach of anything
6
- * that acts on "the tracks the user is looking at": a promoted default's "apply
7
- * to open tracks", and the share/export bake. One walk so those can't drift
8
- * apart.
9
- *
10
- * Recurses into composite views. A display nested in one resolves the cascade
11
- * like any other but was invisible to both callers, so the share/export bake
12
- * didn't bake its inherited values and a shared session containing a
13
- * breakpoint-split or synteny view rendered differently for the recipient.
14
- * `LGVSyntenyDisplay` is only ever reached through this branch, so don't flatten
15
- * the recursion away. `hasChildViews` names the one composite shape it does not
16
- * cover.
17
- *
18
- * A view holding neither (e.g. spreadsheet) drops out via the structural guards.
19
- * A view whose displays declare no promotable slot (e.g. dotplot, which does
20
- * hold tracks) is walked and contributes nothing — harmless, and cheaper than
21
- * asking each display whether it has anything to promote.
22
- */
23
- export declare function openPromotableDisplays(session: AbstractSessionModel): ResolvableDisplay[];