@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
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { stringToJexlExpression } from './jexlStrings.js';
|
|
2
|
+
import SimpleFeature, { buildJexlContext } from './simpleFeature.js';
|
|
3
|
+
export const DEFAULT_BIN_AS = ['start', 'end'];
|
|
4
|
+
export const DEFAULT_COVERAGE_AS = 'coverage';
|
|
5
|
+
export const DEFAULT_STACK_AS = 'row';
|
|
6
|
+
export const DEFAULT_STACK_FIELDS = ['start', 'end'];
|
|
7
|
+
/**
|
|
8
|
+
* A feature with fields written over another's: what `formula` and `bin`
|
|
9
|
+
* produce, without copying the base feature's data per step.
|
|
10
|
+
*/
|
|
11
|
+
class DerivedFeature {
|
|
12
|
+
base;
|
|
13
|
+
fields;
|
|
14
|
+
constructor(base, fields) {
|
|
15
|
+
this.base = base;
|
|
16
|
+
this.fields = fields;
|
|
17
|
+
}
|
|
18
|
+
get = ((name) => name in this.fields
|
|
19
|
+
? this.fields[name]
|
|
20
|
+
: this.base.get(name));
|
|
21
|
+
id() {
|
|
22
|
+
return this.base.id();
|
|
23
|
+
}
|
|
24
|
+
parent() {
|
|
25
|
+
return this.base.parent?.();
|
|
26
|
+
}
|
|
27
|
+
children() {
|
|
28
|
+
return this.base.children?.();
|
|
29
|
+
}
|
|
30
|
+
toJSON() {
|
|
31
|
+
return { ...this.base.toJSON(), ...this.fields };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A feature a step made from nothing: what `aggregate` and `coverage` answer,
|
|
36
|
+
* carrying only the fields they wrote. `SimpleFeature`'s constructor costs
|
|
37
|
+
* more than the encode of what it builds, and nothing here needs its checks.
|
|
38
|
+
*/
|
|
39
|
+
class MadeFeature {
|
|
40
|
+
data;
|
|
41
|
+
tag;
|
|
42
|
+
constructor(data, tag) {
|
|
43
|
+
this.data = data;
|
|
44
|
+
this.tag = tag;
|
|
45
|
+
}
|
|
46
|
+
get = ((name) => name === 'uniqueId' ? this.id() : this.data[name]);
|
|
47
|
+
id() {
|
|
48
|
+
const { refName, start, end } = this.data;
|
|
49
|
+
return `${String(refName)}:${String(start)}-${String(end)}${this.tag}`;
|
|
50
|
+
}
|
|
51
|
+
toJSON() {
|
|
52
|
+
return { ...this.data, uniqueId: this.id() };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function expression(expr, jexl) {
|
|
56
|
+
if (!jexl) {
|
|
57
|
+
throw new Error(`a jexl transform needs a jexl instance (${expr})`);
|
|
58
|
+
}
|
|
59
|
+
return stringToJexlExpression(expr, jexl);
|
|
60
|
+
}
|
|
61
|
+
function filter(features, expr, jexl) {
|
|
62
|
+
const compiled = expression(expr, jexl);
|
|
63
|
+
return features.filter(f => compiled.eval(buildJexlContext({ feature: f })));
|
|
64
|
+
}
|
|
65
|
+
function formula(features, expr, as, jexl) {
|
|
66
|
+
const compiled = expression(expr, jexl);
|
|
67
|
+
return features.map(f => new DerivedFeature(f, {
|
|
68
|
+
[as]: compiled.eval(buildJexlContext({ feature: f })),
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* One element of a fanned-out array field, reading its own fields over the
|
|
73
|
+
* feature it came from: an exon that still knows its gene's name and strand.
|
|
74
|
+
*/
|
|
75
|
+
class FlattenedFeature {
|
|
76
|
+
container;
|
|
77
|
+
item;
|
|
78
|
+
constructor(container, item) {
|
|
79
|
+
this.container = container;
|
|
80
|
+
this.item = item;
|
|
81
|
+
}
|
|
82
|
+
get = ((name) => {
|
|
83
|
+
const v = this.item.get(name);
|
|
84
|
+
return v === undefined ? this.container.get(name) : v;
|
|
85
|
+
});
|
|
86
|
+
id() {
|
|
87
|
+
return this.item.id();
|
|
88
|
+
}
|
|
89
|
+
parent() {
|
|
90
|
+
return this.container;
|
|
91
|
+
}
|
|
92
|
+
children() {
|
|
93
|
+
return this.item.children?.();
|
|
94
|
+
}
|
|
95
|
+
toJSON() {
|
|
96
|
+
return { ...this.container.toJSON(), ...this.item.toJSON() };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function flatten(features, step) {
|
|
100
|
+
const { field = 'subfeatures', index, keepEmpty } = step;
|
|
101
|
+
const out = [];
|
|
102
|
+
for (const f of features) {
|
|
103
|
+
const items = f.get(field);
|
|
104
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
105
|
+
if (keepEmpty) {
|
|
106
|
+
out.push(f);
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
for (const [i, item] of items.entries()) {
|
|
111
|
+
const child = typeof item?.get === 'function'
|
|
112
|
+
? item
|
|
113
|
+
: new SimpleFeature({
|
|
114
|
+
...item,
|
|
115
|
+
uniqueId: `${f.id()}#${i}`,
|
|
116
|
+
});
|
|
117
|
+
const flat = new FlattenedFeature(f, child);
|
|
118
|
+
out.push(index ? new DerivedFeature(flat, { [index]: i }) : flat);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
function bin(features, step) {
|
|
124
|
+
const { field = 'start', step: size } = step;
|
|
125
|
+
if (!(size > 0)) {
|
|
126
|
+
throw new Error(`a bin step needs a positive size (${size})`);
|
|
127
|
+
}
|
|
128
|
+
const [asStart, asEnd] = step.as ?? DEFAULT_BIN_AS;
|
|
129
|
+
return features.map(f => {
|
|
130
|
+
const v = Number(f.get(field));
|
|
131
|
+
const start = Math.floor(v / size) * size;
|
|
132
|
+
return new DerivedFeature(f, { [asStart]: start, [asEnd]: start + size });
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
export function aggregateFieldName({ op, field, as }) {
|
|
136
|
+
return as ?? (op === 'count' || field === undefined ? op : `${op}_${field}`);
|
|
137
|
+
}
|
|
138
|
+
function groupMembers(features, groupby) {
|
|
139
|
+
if (groupby.length === 0) {
|
|
140
|
+
return features.length > 0 ? [[...features]] : [];
|
|
141
|
+
}
|
|
142
|
+
const last = groupby.length - 1;
|
|
143
|
+
const root = new Map();
|
|
144
|
+
const groups = [];
|
|
145
|
+
for (const f of features) {
|
|
146
|
+
let node = root;
|
|
147
|
+
for (let i = 0; i < last; i++) {
|
|
148
|
+
const v = f.get(groupby[i]);
|
|
149
|
+
let next = node.get(v);
|
|
150
|
+
if (!next) {
|
|
151
|
+
next = new Map();
|
|
152
|
+
node.set(v, next);
|
|
153
|
+
}
|
|
154
|
+
node = next;
|
|
155
|
+
}
|
|
156
|
+
const v = f.get(groupby[last]);
|
|
157
|
+
let members = node.get(v);
|
|
158
|
+
if (!members) {
|
|
159
|
+
members = [];
|
|
160
|
+
node.set(v, members);
|
|
161
|
+
groups.push(members);
|
|
162
|
+
}
|
|
163
|
+
members.push(f);
|
|
164
|
+
}
|
|
165
|
+
return groups;
|
|
166
|
+
}
|
|
167
|
+
function aggregate(features, step) {
|
|
168
|
+
const { groupby = [], ops } = step;
|
|
169
|
+
const out = [];
|
|
170
|
+
let serial = 0;
|
|
171
|
+
for (const members of groupMembers(features, groupby)) {
|
|
172
|
+
const first = members[0];
|
|
173
|
+
const refName = first.get('refName');
|
|
174
|
+
let start = Infinity;
|
|
175
|
+
let end = -Infinity;
|
|
176
|
+
for (const m of members) {
|
|
177
|
+
start = Math.min(start, m.get('start'));
|
|
178
|
+
end = Math.max(end, m.get('end'));
|
|
179
|
+
}
|
|
180
|
+
const data = {};
|
|
181
|
+
for (const field of groupby) {
|
|
182
|
+
data[field] = first.get(field);
|
|
183
|
+
}
|
|
184
|
+
for (const agg of ops) {
|
|
185
|
+
data[aggregateFieldName(agg)] = aggregateValue(members, agg);
|
|
186
|
+
}
|
|
187
|
+
out.push(new MadeFeature({ ...data, refName, start, end }, `#${serial++}`));
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
function aggregateValue(members, { op, field }) {
|
|
192
|
+
if (op === 'count') {
|
|
193
|
+
return members.length;
|
|
194
|
+
}
|
|
195
|
+
if (field === undefined) {
|
|
196
|
+
throw new Error(`an aggregate ${op} needs a field`);
|
|
197
|
+
}
|
|
198
|
+
let sum = 0;
|
|
199
|
+
let n = 0;
|
|
200
|
+
let min = Infinity;
|
|
201
|
+
let max = -Infinity;
|
|
202
|
+
for (const m of members) {
|
|
203
|
+
const v = Number(m.get(field));
|
|
204
|
+
if (!Number.isFinite(v)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
sum += v;
|
|
208
|
+
n++;
|
|
209
|
+
min = Math.min(min, v);
|
|
210
|
+
max = Math.max(max, v);
|
|
211
|
+
}
|
|
212
|
+
switch (op) {
|
|
213
|
+
case 'sum': {
|
|
214
|
+
return sum;
|
|
215
|
+
}
|
|
216
|
+
case 'mean': {
|
|
217
|
+
return n > 0 ? sum / n : undefined;
|
|
218
|
+
}
|
|
219
|
+
case 'min': {
|
|
220
|
+
return n > 0 ? min : undefined;
|
|
221
|
+
}
|
|
222
|
+
case 'max': {
|
|
223
|
+
return n > 0 ? max : undefined;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The lowest row each feature fits on, greedy first fit in start order: the
|
|
229
|
+
* layout a pileup is, as a step in front of the encoder rather than a packer
|
|
230
|
+
* behind it. `rowEnds[r]` is where row `r` is free again, so the scan is over
|
|
231
|
+
* rows rather than over features.
|
|
232
|
+
*/
|
|
233
|
+
function stack(features, step) {
|
|
234
|
+
const as = step.as ?? DEFAULT_STACK_AS;
|
|
235
|
+
const [startField, endField] = step.fields ?? DEFAULT_STACK_FIELDS;
|
|
236
|
+
const padding = step.padding ?? 0;
|
|
237
|
+
const out = [];
|
|
238
|
+
for (const members of groupMembers(features, step.groupby ?? [])) {
|
|
239
|
+
members.sort((a, b) => Number(a.get(startField)) - Number(b.get(startField)));
|
|
240
|
+
const rowEnds = [];
|
|
241
|
+
for (const f of members) {
|
|
242
|
+
const start = Number(f.get(startField));
|
|
243
|
+
const end = Number(f.get(endField));
|
|
244
|
+
let row = 0;
|
|
245
|
+
while (row < rowEnds.length && rowEnds[row] > start) {
|
|
246
|
+
row++;
|
|
247
|
+
}
|
|
248
|
+
rowEnds[row] = (end > start ? end : start) + padding;
|
|
249
|
+
out.push(new DerivedFeature(f, { [as]: row }));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Piecewise-constant depth over the features' spans: one feature per run of
|
|
256
|
+
* equal depth, where the depth is not zero. A sweep over the sorted edges,
|
|
257
|
+
* so the input need not be sorted.
|
|
258
|
+
*/
|
|
259
|
+
function coverage(features, step) {
|
|
260
|
+
const as = step.as ?? DEFAULT_COVERAGE_AS;
|
|
261
|
+
const n = features.length;
|
|
262
|
+
if (n === 0) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
const starts = new Float64Array(n);
|
|
266
|
+
const ends = new Float64Array(n);
|
|
267
|
+
for (let i = 0; i < n; i++) {
|
|
268
|
+
starts[i] = features[i].get('start');
|
|
269
|
+
ends[i] = features[i].get('end');
|
|
270
|
+
}
|
|
271
|
+
starts.sort();
|
|
272
|
+
ends.sort();
|
|
273
|
+
const refName = features[0].get('refName');
|
|
274
|
+
const out = [];
|
|
275
|
+
let depth = 0;
|
|
276
|
+
let runStart = starts[0];
|
|
277
|
+
let si = 0;
|
|
278
|
+
let ei = 0;
|
|
279
|
+
const emit = (to) => {
|
|
280
|
+
if (depth > 0 && to > runStart) {
|
|
281
|
+
out.push(new MadeFeature({ refName, start: runStart, end: to, [as]: depth }, ''));
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
while (si < n || ei < n) {
|
|
285
|
+
const nextStart = si < n ? starts[si] : Infinity;
|
|
286
|
+
const nextEnd = ei < n ? ends[ei] : Infinity;
|
|
287
|
+
const at = Math.min(nextStart, nextEnd);
|
|
288
|
+
emit(at);
|
|
289
|
+
runStart = at;
|
|
290
|
+
while (si < n && starts[si] === at) {
|
|
291
|
+
depth++;
|
|
292
|
+
si++;
|
|
293
|
+
}
|
|
294
|
+
while (ei < n && ends[ei] === at) {
|
|
295
|
+
depth--;
|
|
296
|
+
ei++;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* #api
|
|
303
|
+
* Run the transform steps over a feature list, in order, in the worker. The
|
|
304
|
+
* list a step answers is what the next one reads, and the last one is what
|
|
305
|
+
* the encoder walks.
|
|
306
|
+
*/
|
|
307
|
+
export function runTransforms(features, steps, jexl) {
|
|
308
|
+
let current = features;
|
|
309
|
+
for (const step of steps) {
|
|
310
|
+
switch (step.type) {
|
|
311
|
+
case 'filter': {
|
|
312
|
+
current = filter(current, step.expr, jexl);
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
case 'formula': {
|
|
316
|
+
current = formula(current, step.expr, step.as, jexl);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case 'flatten': {
|
|
320
|
+
current = flatten(current, step);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
case 'bin': {
|
|
324
|
+
current = bin(current, step);
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
case 'aggregate': {
|
|
328
|
+
current = aggregate(current, step);
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case 'coverage': {
|
|
332
|
+
current = coverage(current, step);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 'stack': {
|
|
336
|
+
current = stack(current, step);
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return current;
|
|
342
|
+
}
|
package/esm/util/freezeDeep.d.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Freeze `value` and everything reachable from it, in place, and return it.
|
|
3
3
|
*
|
|
4
|
-
* For a value handed to consumers **by reference on purpose**: a
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* following display). Sharing the reference is load-bearing in both cases —
|
|
8
|
-
* `===` stability is what lets a display's cached MobX computed re-resolve
|
|
4
|
+
* For a value handed to consumers **by reference on purpose**: a stored user
|
|
5
|
+
* preference, read by everything that follows it. Sharing the reference is
|
|
6
|
+
* load-bearing — `===` stability is what lets a cached MobX computed re-derive
|
|
9
7
|
* without waking anything downstream — so handing out a defensive copy per read
|
|
10
8
|
* would trade a silent hazard for a real re-render on every read. Freezing keeps
|
|
11
9
|
* the identity and makes "nothing mutates this" throw rather than quietly
|
|
12
|
-
* rewriting a value every other
|
|
10
|
+
* rewriting a value every other reader sees.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* The caller freezes once, at the store write, over a value that has to survive
|
|
13
|
+
* `postMessage` anyway — so the recursion is over small JSON and costs nothing
|
|
14
|
+
* per read. Cyclic input terminates: each object is frozen before its children
|
|
15
|
+
* are visited.
|
|
18
16
|
*
|
|
19
17
|
* **Unconditional, deliberately unlike MST's own `deepFreeze`** (whose body this
|
|
20
18
|
* otherwise mirrors), which is gated on `devMode()`. MST gates because it freezes
|
package/esm/util/freezeDeep.js
CHANGED
|
@@ -6,20 +6,18 @@ function isObjectLike(value) {
|
|
|
6
6
|
/**
|
|
7
7
|
* Freeze `value` and everything reachable from it, in place, and return it.
|
|
8
8
|
*
|
|
9
|
-
* For a value handed to consumers **by reference on purpose**: a
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* following display). Sharing the reference is load-bearing in both cases —
|
|
13
|
-
* `===` stability is what lets a display's cached MobX computed re-resolve
|
|
9
|
+
* For a value handed to consumers **by reference on purpose**: a stored user
|
|
10
|
+
* preference, read by everything that follows it. Sharing the reference is
|
|
11
|
+
* load-bearing — `===` stability is what lets a cached MobX computed re-derive
|
|
14
12
|
* without waking anything downstream — so handing out a defensive copy per read
|
|
15
13
|
* would trade a silent hazard for a real re-render on every read. Freezing keeps
|
|
16
14
|
* the identity and makes "nothing mutates this" throw rather than quietly
|
|
17
|
-
* rewriting a value every other
|
|
15
|
+
* rewriting a value every other reader sees.
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* The caller freezes once, at the store write, over a value that has to survive
|
|
18
|
+
* `postMessage` anyway — so the recursion is over small JSON and costs nothing
|
|
19
|
+
* per read. Cyclic input terminates: each object is frozen before its children
|
|
20
|
+
* are visited.
|
|
23
21
|
*
|
|
24
22
|
* **Unconditional, deliberately unlike MST's own `deepFreeze`** (whose body this
|
|
25
23
|
* otherwise mirrors), which is gated on `devMode()`. MST gates because it freezes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GlyphName } from './markEncodingTypes.ts';
|
|
2
|
+
/**
|
|
3
|
+
* #api
|
|
4
|
+
* The `point` shape's glyph code for each name an encoding can say.
|
|
5
|
+
*/
|
|
6
|
+
export declare const GLYPH_CODES: Record<GlyphName, number>;
|
|
7
|
+
/** The glyph names in range order: what an unlisted `range` hands out. */
|
|
8
|
+
export declare const GLYPH_NAMES: GlyphName[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GLYPH_DIAMOND, GLYPH_DISC, GLYPH_TRIANGLE, } from '@jbrowse/render-core/shaders/pointMarkConsts';
|
|
2
|
+
/**
|
|
3
|
+
* #api
|
|
4
|
+
* The `point` shape's glyph code for each name an encoding can say.
|
|
5
|
+
*/
|
|
6
|
+
export const GLYPH_CODES = {
|
|
7
|
+
disc: GLYPH_DISC,
|
|
8
|
+
triangle: GLYPH_TRIANGLE,
|
|
9
|
+
diamond: GLYPH_DIAMOND,
|
|
10
|
+
};
|
|
11
|
+
/** The glyph names in range order: what an unlisted `range` hands out. */
|
|
12
|
+
export const GLYPH_NAMES = Object.keys(GLYPH_CODES);
|
package/esm/util/index.d.ts
CHANGED
|
@@ -37,7 +37,6 @@ export { indexedDBAvailable, isElectron, isNode, rIC } from './environment.ts';
|
|
|
37
37
|
export { isWebWorker } from './isWebWorker.ts';
|
|
38
38
|
export { sessionStorageAvailable, sessionStorageGetItem, sessionStorageRemoveItem, sessionStorageSetItem, } from './sessionStorage.ts';
|
|
39
39
|
export { isObject } from './objectUtils.ts';
|
|
40
|
-
export { openPromotableDisplays } from './openDisplays.ts';
|
|
41
40
|
export { type ReorderDirection, applyOrderWithin, reorder, reorderWithin, } from './reorder.ts';
|
|
42
41
|
export { getStr } from './getStr.ts';
|
|
43
42
|
export { downloadPhase, getLocationUri, resolveUri } from './getLocationUri.ts';
|
package/esm/util/index.js
CHANGED
|
@@ -44,7 +44,6 @@ export { indexedDBAvailable, isElectron, isNode, rIC } from './environment.js';
|
|
|
44
44
|
export { isWebWorker } from './isWebWorker.js';
|
|
45
45
|
export { sessionStorageAvailable, sessionStorageGetItem, sessionStorageRemoveItem, sessionStorageSetItem, } from './sessionStorage.js';
|
|
46
46
|
export { isObject } from './objectUtils.js';
|
|
47
|
-
export { openPromotableDisplays } from './openDisplays.js';
|
|
48
47
|
export { applyOrderWithin, reorder, reorderWithin, } from './reorder.js';
|
|
49
48
|
export { getStr } from './getStr.js';
|
|
50
49
|
export { downloadPhase, getLocationUri, resolveUri } from './getLocationUri.js';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { JexlInstance } from './jexlStrings.ts';
|
|
2
|
+
import type { ColorEncoding, Encoded, EncodedChannels, FieldRef, GlyphEncoding, LaneName, ValueEncoding } from './markEncodingTypes.ts';
|
|
3
|
+
import type { ProgressReporter } from './progress.ts';
|
|
4
|
+
import type { Feature } from './simpleFeature.ts';
|
|
5
|
+
export type { AggregateOp, AggregateStep, BinStep, CategoricalRef, ColorEncoding, CoverageStep, ColorScaleTable, CoreEncodeFeaturesArgs, Encoded, EncodedChannels, EncodedFeaturesResult, FieldRef, FilterStep, FormulaStep, GlyphEncoding, GlyphName, GlyphScaleTable, LaneName, LayerRequest, MarkEncoding, RampRef, ScaleTable, TransformStep, ValueEncoding, } from './markEncodingTypes.ts';
|
|
6
|
+
export declare const DEFAULT_MARK_COLOR = "#0068d1";
|
|
7
|
+
/**
|
|
8
|
+
* #api
|
|
9
|
+
* The key row a feature with nothing in a categorical field lands on, so the
|
|
10
|
+
* legend says why a mark is grey, or a disc, rather than listing a blank
|
|
11
|
+
* value.
|
|
12
|
+
*/
|
|
13
|
+
export declare const NO_VALUE_LABEL = "(no value)";
|
|
14
|
+
/**
|
|
15
|
+
* #api
|
|
16
|
+
* A channel read per feature: the compiled form of a {@link FieldRef}, and
|
|
17
|
+
* what a display's own worker method hands the encoder for a channel no
|
|
18
|
+
* field name can say — a join against a second adapter, a lookup table, a
|
|
19
|
+
* rule over two fields.
|
|
20
|
+
*/
|
|
21
|
+
export type ChannelReader<T = unknown> = (feature: Feature) => T;
|
|
22
|
+
/**
|
|
23
|
+
* #api
|
|
24
|
+
* What `encodeFeatures` takes: a {@link MarkEncoding}, any channel of which
|
|
25
|
+
* may be a {@link ChannelReader} in place of its declared form. The declared
|
|
26
|
+
* form is what crosses the wire; a reader is built in the worker.
|
|
27
|
+
*/
|
|
28
|
+
export interface MarkEncodingInput {
|
|
29
|
+
x?: FieldRef | ChannelReader;
|
|
30
|
+
x2?: FieldRef | ChannelReader;
|
|
31
|
+
y?: ValueEncoding | ChannelReader;
|
|
32
|
+
row?: FieldRef | ChannelReader;
|
|
33
|
+
color?: ColorEncoding | ChannelReader<number>;
|
|
34
|
+
glyph?: GlyphEncoding | ChannelReader<number>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* #api
|
|
38
|
+
* What surrounds an encode: the jexl instance a `jexl:` channel compiles
|
|
39
|
+
* against — a caller whose channels are all readers or field names passes
|
|
40
|
+
* none — and a progress reporter.
|
|
41
|
+
*/
|
|
42
|
+
export interface EncodeContext {
|
|
43
|
+
jexl?: JexlInstance;
|
|
44
|
+
report?: ProgressReporter;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* #api
|
|
48
|
+
* The field a `y` declaration reads, whichever of its two forms it is
|
|
49
|
+
* written in.
|
|
50
|
+
*/
|
|
51
|
+
export declare function valueField(y: ValueEncoding): FieldRef;
|
|
52
|
+
/**
|
|
53
|
+
* #api
|
|
54
|
+
* Evaluate one encoding over a feature list into dense channel arrays for
|
|
55
|
+
* the lanes named, the scale table each scaled channel came from, the `y`
|
|
56
|
+
* extremes and — when `index` is among the lanes — a hit index.
|
|
57
|
+
*
|
|
58
|
+
* A feature whose `x`, `x2` or (declared and asked-for) `y` is not finite is
|
|
59
|
+
* skipped and counted in `skipped`, so every array stays index-aligned with
|
|
60
|
+
* the Flatbush. Pure: the RPC around it owns the adapter, the filters and the
|
|
61
|
+
* transferables.
|
|
62
|
+
*/
|
|
63
|
+
export declare function encodeFeatures<L extends LaneName>(features: readonly Feature[], encoding: MarkEncodingInput, lanes: readonly L[], ctx?: EncodeContext): Encoded<L>;
|
|
64
|
+
/**
|
|
65
|
+
* #api
|
|
66
|
+
* A CSS colour or `jexl:` colour expression as a per-feature packed ABGR —
|
|
67
|
+
* the unscaled arm of {@link ColorEncoding}, on its own for a display that
|
|
68
|
+
* carries a plain `color` slot.
|
|
69
|
+
*/
|
|
70
|
+
export declare function colorEvaluator(color: string, jexl: JexlInstance | undefined): (feature: Feature) => number;
|
|
71
|
+
/**
|
|
72
|
+
* #api
|
|
73
|
+
* The buffers an {@link EncodedChannels} owns, for `rpcResult`'s transfer
|
|
74
|
+
* list.
|
|
75
|
+
*/
|
|
76
|
+
export declare function encodedChannelTransferables(c: EncodedChannels): ArrayBufferLike[];
|