@jbrowse/core 5.0.0-beta.4 → 5.0.0-beta.7
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 +278 -250
- package/esm/PluginManager.js +1 -1
- package/esm/ReExports/publicUi.d.ts +1 -1
- package/esm/ReExports/publicUi.js +1 -1
- package/esm/ReExports/publicUtil.d.ts +1 -1
- package/esm/ReExports/publicUtil.js +1 -1
- package/esm/ReExports/workerNamespaceNames.js +3 -9
- package/esm/assemblyManager/assemblyConfigSchema.d.ts +2 -1
- package/esm/assemblyManager/assemblyConfigSchema.js +2 -1
- package/esm/assemblyManager/expandAssemblyConfigShorthand.d.ts +15 -15
- package/esm/assemblyManager/expandAssemblyConfigShorthand.js +34 -18
- package/esm/configuration/configurationSchema.d.ts +1 -2
- package/esm/configuration/configurationSchema.js +3 -8
- package/esm/configuration/configurationSlot.d.ts +0 -47
- package/esm/configuration/configurationSlot.js +4 -54
- package/esm/configuration/fillLocations.d.ts +14 -0
- package/esm/configuration/fillLocations.js +14 -0
- package/esm/configuration/fullConfSnapshot.d.ts +0 -15
- package/esm/configuration/fullConfSnapshot.js +0 -28
- package/esm/configuration/getConf.d.ts +4 -34
- package/esm/configuration/getConf.js +3 -38
- package/esm/configuration/index.d.ts +3 -4
- package/esm/configuration/index.js +3 -2
- package/esm/configuration/schemaRegistry.d.ts +1 -2
- package/esm/configuration/schemaRegistry.js +1 -2
- package/esm/configuration/slotFacade.d.ts +1 -16
- package/esm/configuration/slotFacade.js +1 -2
- package/esm/configuration/tabixShorthand.js +3 -3
- package/esm/configuration/types.d.ts +4 -16
- package/esm/data_adapters/CytobandAdapter/configSchema.js +3 -4
- package/esm/pluggableElementTypes/DisplayType.d.ts +5 -3
- package/esm/pluggableElementTypes/DisplayType.js +3 -1
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +30 -0
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +45 -17
- package/esm/rpc/RpcRegistry.d.ts +7 -1
- package/esm/rpc/coreRpcMethods.d.ts +1 -0
- package/esm/rpc/coreRpcMethods.js +1 -0
- package/esm/rpc/methods/CoreEncodeFeatures.d.ts +15 -0
- package/esm/rpc/methods/CoreEncodeFeatures.js +62 -0
- package/esm/svg/svgId.d.ts +1 -1
- package/esm/svg/svgId.js +1 -1
- package/esm/ui/LegendSwatchGlyph.js +26 -21
- package/esm/ui/MenuTypes.d.ts +5 -52
- package/esm/ui/SizeSliderRow.d.ts +1 -3
- package/esm/ui/SizeSliderRow.js +4 -6
- package/esm/ui/SvgColorLegend.d.ts +9 -3
- package/esm/ui/SvgColorLegend.js +74 -33
- package/esm/ui/colorScale.d.ts +64 -0
- package/esm/ui/colorScale.js +41 -0
- package/esm/ui/colors.d.ts +11 -0
- package/esm/ui/colors.js +7 -0
- package/esm/ui/index.d.ts +5 -7
- package/esm/ui/index.js +3 -13
- package/esm/ui/legendMenuItem.d.ts +5 -16
- package/esm/ui/legendMenuItem.js +3 -14
- package/esm/ui/legendSpec.d.ts +11 -5
- package/esm/ui/legendSpec.js +8 -11
- package/esm/ui/makeSizeMenu.d.ts +0 -6
- package/esm/ui/makeSizeMenu.js +6 -30
- package/esm/ui/measureLegendText.js +1 -1
- package/esm/ui/menuItemAdornment.d.ts +9 -19
- package/esm/ui/menuItemAdornment.js +10 -31
- package/esm/ui/menuItems.d.ts +2 -6
- package/esm/ui/menuItems.js +0 -4
- package/esm/ui/palette.js +2 -1
- package/esm/ui/toggleMenuItems.d.ts +6 -13
- package/esm/ui/toggleMenuItems.js +13 -29
- package/esm/util/colorRamp.d.ts +3 -3
- package/esm/util/colorRamp.js +2 -2
- package/esm/util/featureTransforms.d.ts +15 -0
- package/esm/util/featureTransforms.js +342 -0
- package/esm/util/freezeDeep.d.ts +8 -10
- package/esm/util/freezeDeep.js +8 -10
- package/esm/util/glyphNames.d.ts +8 -0
- package/esm/util/glyphNames.js +12 -0
- package/esm/util/index.d.ts +0 -1
- package/esm/util/index.js +0 -1
- package/esm/util/markEncoding.d.ts +76 -0
- package/esm/util/markEncoding.js +436 -0
- package/esm/util/markEncodingTypes.d.ts +331 -0
- package/esm/util/markEncodingTypes.js +8 -0
- package/esm/util/renderToStaticMarkup.js +1 -1
- package/esm/util/starFusion.d.ts +26 -0
- package/esm/util/starFusion.js +37 -0
- package/esm/util/trackConfigDelta.d.ts +4 -8
- package/esm/util/trackConfigDelta.js +2 -4
- package/esm/util/tracks.d.ts +7 -1
- package/esm/util/tracks.js +72 -7
- package/esm/util/types/index.d.ts +12 -4
- package/esm/util/whenViewSettled.d.ts +9 -0
- package/esm/util/whenViewSettled.js +12 -4
- package/package.json +13 -3
- package/esm/configuration/promotableDefaults.d.ts +0 -234
- package/esm/configuration/promotableDefaults.js +0 -395
- package/esm/configuration/promotablePin.d.ts +0 -65
- package/esm/configuration/promotablePin.js +0 -1
- package/esm/configuration/promotableResolve.d.ts +0 -108
- package/esm/configuration/promotableResolve.js +0 -117
- package/esm/configuration/promotableSlots.d.ts +0 -12
- package/esm/configuration/promotableSlots.js +0 -45
- package/esm/configuration/slotShape.d.ts +0 -12
- package/esm/configuration/slotShape.js +0 -84
- package/esm/ui/PinAdornment.d.ts +0 -5
- package/esm/ui/PinAdornment.js +0 -67
- package/esm/ui/SvgGradientLegend.d.ts +0 -26
- package/esm/ui/SvgGradientLegend.js +0 -26
- package/esm/ui/promotablePinCoverage.d.ts +0 -58
- package/esm/ui/promotablePinCoverage.js +0 -124
- package/esm/util/openDisplays.d.ts +0 -23
- 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
|
-
}
|
package/esm/ui/PinAdornment.d.ts
DELETED
package/esm/ui/PinAdornment.js
DELETED
|
@@ -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[];
|