@jbrowse/display-ui 5.0.0-beta.8 → 5.0.0-beta.9
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 +24 -14
- package/esm/AxisCaption.d.ts +24 -0
- package/esm/AxisCaption.js +57 -0
- package/esm/AxisGutter.d.ts +3 -3
- package/esm/AxisGutter.js +6 -16
- package/esm/FloatingLegend.d.ts +0 -1
- package/esm/FloatingLegend.js +16 -8
- package/esm/ScoreDomainCaption.d.ts +4 -3
- package/esm/ScoreDomainCaption.js +6 -5
- package/esm/ScoreRules.js +1 -1
- package/esm/YScaleBar.js +2 -1
- package/esm/YScaleBarOverlay.js +2 -3
- package/esm/axisPlacement.d.ts +23 -4
- package/esm/axisPlacement.js +42 -7
- package/esm/bottomRightCorner.d.ts +3 -4
- package/esm/bottomRightCorner.js +3 -4
- package/esm/chromeOverlayContext.d.ts +1 -1
- package/esm/chromeOverlayContext.js +4 -5
- package/esm/chromeOverlays.d.ts +4 -2
- package/esm/embed/index.d.ts +70 -0
- package/esm/embed/index.js +69 -0
- package/esm/embed/legend.d.ts +8 -0
- package/esm/embed/legend.js +35 -0
- package/esm/embed/location.d.ts +17 -0
- package/esm/embed/location.js +65 -0
- package/esm/embed/provider.d.ts +11 -0
- package/esm/embed/provider.js +6 -0
- package/esm/embed/regions.d.ts +53 -0
- package/esm/embed/regions.js +95 -0
- package/esm/floatingLegendTop.d.ts +1 -0
- package/esm/floatingLegendTop.js +2 -0
- package/esm/index.d.ts +8 -6
- package/esm/index.js +7 -5
- package/esm/plainChromeOverlays.d.ts +1 -1
- package/esm/plainChromeOverlays.js +5 -5
- package/esm/tooltip/Tooltip.d.ts +3 -3
- package/esm/tooltip/Tooltip.js +3 -3
- package/esm/trackControl/trackControlContext.d.ts +1 -1
- package/esm/trackControl/trackControlContext.js +6 -6
- package/esm/trackOverlay/TrackOverlaySlot.d.ts +2 -2
- package/esm/trackOverlay/TrackOverlaySlot.js +2 -2
- package/esm/valueScale.d.ts +22 -2
- package/esm/yAxisConstants.d.ts +6 -0
- package/esm/yAxisConstants.js +14 -0
- package/esm/yScaleTicks.d.ts +2 -1
- package/esm/yScaleTicks.js +2 -1
- package/package.json +7 -4
- package/esm/formatScore.d.ts +0 -1
- package/esm/formatScore.js +0 -14
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ above them.
|
|
|
36
36
|
|
|
37
37
|
| | |
|
|
38
38
|
| ------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
39
|
-
| `DisplayChromeOverlays` | the
|
|
39
|
+
| `DisplayChromeOverlays` | the `displayPhase` states, as a five-component set, with the model shape each is handed |
|
|
40
40
|
| `DisplayChromeOverlayProvider` | redirects those states for JBrowse's own displays |
|
|
41
41
|
| `TrackControlComponent` | one shape for every ambient bottom-right control, icons named rather than passed |
|
|
42
42
|
| `TrackControlProvider` | redirects those |
|
|
@@ -89,10 +89,9 @@ and the status text disappearing under them.
|
|
|
89
89
|
It has never been reachable: the two displays that render the control row
|
|
90
90
|
(alignments, canvas) are not among the four that report a `ready`-phase status
|
|
91
91
|
(clustering, on multi-wiggle / multi-row features / maf / the multi-sample
|
|
92
|
-
variant pair).
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
never appears.
|
|
92
|
+
variant pair). The layout is structural anyway, because neither side checks for
|
|
93
|
+
the constraint, so the first display to want both would hit the bug as a status
|
|
94
|
+
message that never appears.
|
|
96
95
|
|
|
97
96
|
`BottomRightIndicators` already described itself as "the single anchor point for
|
|
98
97
|
every bottom-right overlay ... so they lay out as one row instead of each
|
|
@@ -208,9 +207,9 @@ question only exists for an MST node.
|
|
|
208
207
|
|
|
209
208
|
### plainChromeOverlays
|
|
210
209
|
|
|
211
|
-
The
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
The `displayPhase` states drawn with no UI toolkit: no theme provider to mount,
|
|
211
|
+
no emotion in the host page, and nothing that reads as a stray Material widget
|
|
212
|
+
inside someone else's design system.
|
|
214
213
|
|
|
215
214
|
`DisplayUIProvider` installs this by default, so mount that rather than naming
|
|
216
215
|
this — reach for it directly only to wrap a state or to build a context value by
|
|
@@ -288,9 +287,8 @@ toolkit, so the package's no-Material-UI guarantee holds.
|
|
|
288
287
|
Takes a single element child and clones it rather than wrapping it: the controls
|
|
289
288
|
that want a tooltip are absolutely positioned inside a legend or sit in a flex
|
|
290
289
|
row, where an extra `<span>` moves them. The child keeps its own handlers —
|
|
291
|
-
these compose on top of them.
|
|
292
|
-
|
|
293
|
-
toolkit is what differs here, not the shape.
|
|
290
|
+
these compose on top of them. MUI's `Tooltip`, which the rest of JBrowse's
|
|
291
|
+
chrome is built on, uses the same trigger pattern; only the toolkit differs.
|
|
294
292
|
|
|
295
293
|
```tsx
|
|
296
294
|
<Tooltip title="Hide legend">
|
|
@@ -366,8 +364,8 @@ under them with nothing to say so.
|
|
|
366
364
|
this component: JBrowse's own track container passes 100, which is positioned
|
|
367
365
|
above `PaddingBlocks` and below `TrackLabel` at 200, and means nothing to a host
|
|
368
366
|
whose masks sit at 2. A default would be a number that is right in one layout
|
|
369
|
-
and
|
|
370
|
-
|
|
367
|
+
and wrong in every other, and the failure — chrome painted under a mask — goes
|
|
368
|
+
unseen until someone looks at the right zoom.
|
|
371
369
|
|
|
372
370
|
The node takes no pointer events, so it does not eat the canvas's. Chrome that
|
|
373
371
|
wants them takes them back on its own positioned box; it also carries
|
|
@@ -451,7 +449,10 @@ the `[min, max]` caption and no axis, as a scale whose bands are too short for
|
|
|
451
449
|
one does. `side` and `left` are which of the band's edges the display's own
|
|
452
450
|
panels leave clear for a gutter: `right` where a group label chip takes the
|
|
453
451
|
left, `left: n` where a dendrogram takes the first `n` px. `caption` is what the
|
|
454
|
-
scale measures (`TLEN`), as a colour scale's `field` is
|
|
452
|
+
scale measures (`TLEN`), as a colour scale's `field` is; the chrome draws it
|
|
453
|
+
once for the scale, beside the bands on screen, however many it rules. `rules`
|
|
454
|
+
are the reference lines the scale declares, which the chrome draws across every
|
|
455
|
+
band.
|
|
455
456
|
|
|
456
457
|
A member describes the scale or the band it rules, never the axis — not an
|
|
457
458
|
orientation, a form, a gutter width or a font. The chrome reads a member to keep
|
|
@@ -460,4 +461,13 @@ off what the display put in the band, never to choose between two drawings of it
|
|
|
460
461
|
|
|
461
462
|
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/display-ui/src/valueScale.ts)
|
|
462
463
|
|
|
464
|
+
### ValueScaleRule
|
|
465
|
+
|
|
466
|
+
One reference line a value scale declares, at a value in the scale's own units.
|
|
467
|
+
`label` is free text and carries no meaning the chrome assigns: over a coverage
|
|
468
|
+
plot "2 copies" is a claim only the author can make, since no ploidy can be
|
|
469
|
+
assumed.
|
|
470
|
+
|
|
471
|
+
[Source code](https://github.com/GMOD/jbrowse-components/blob/main/packages/display-ui/src/valueScale.ts)
|
|
472
|
+
|
|
463
473
|
<!-- API_DOCS_END -->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { YAxis } from './valueScale.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A scale's caption, drawn once however many bands the scale rules: rotated
|
|
4
|
+
* along the outer edge of the gutter column, the origin at the column's top
|
|
5
|
+
* left, centred on the bands given as nearly as the display's `height` holds
|
|
6
|
+
* it whole, and cut to that height where it is longer. The on-screen overlay
|
|
7
|
+
* and the export both draw through this, so the two cannot place a caption
|
|
8
|
+
* differently.
|
|
9
|
+
*/
|
|
10
|
+
export default function AxisCaption({ axis, bandTops, height, }: {
|
|
11
|
+
axis: YAxis;
|
|
12
|
+
bandTops: number[];
|
|
13
|
+
height: number;
|
|
14
|
+
}): import("react").JSX.Element | null;
|
|
15
|
+
/**
|
|
16
|
+
* The on-screen caption of one scale: an `<svg>` over the gutter column, the
|
|
17
|
+
* height of the display, with `AxisCaption` inside.
|
|
18
|
+
*/
|
|
19
|
+
export declare function AxisCaptionOverlay({ axis, bandTops, width, height, }: {
|
|
20
|
+
axis: YAxis;
|
|
21
|
+
bandTops: number[];
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
3
|
+
import { VERTICAL_SCROLLBAR_CLEARANCE } from '@jbrowse/core/ui/VerticalScrollbar';
|
|
4
|
+
import { measureText } from '@jbrowse/core/util/measureText';
|
|
5
|
+
import { axisCaptionY, axisGutterLeft, axisGutterWidth, } from './axisPlacement.js';
|
|
6
|
+
import { AXIS_FONT_PX, CAPTION_INSET_PX } from './yAxisConstants.js';
|
|
7
|
+
// The caption and its length, cut to an ellipsis where it is longer than
|
|
8
|
+
// `room`, as a Vega-Lite axis title is cut at its `titleLimit`. The ellipsis
|
|
9
|
+
// is one em.
|
|
10
|
+
function fitCaption(caption, room) {
|
|
11
|
+
const whole = measureText(caption, AXIS_FONT_PX);
|
|
12
|
+
if (whole <= room) {
|
|
13
|
+
return { text: caption, length: whole };
|
|
14
|
+
}
|
|
15
|
+
for (let end = caption.length - 1; end > 0; end--) {
|
|
16
|
+
const length = measureText(caption.slice(0, end), AXIS_FONT_PX) + AXIS_FONT_PX;
|
|
17
|
+
if (length <= room) {
|
|
18
|
+
return { text: `${caption.slice(0, end)}…`, length };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A scale's caption, drawn once however many bands the scale rules: rotated
|
|
25
|
+
* along the outer edge of the gutter column, the origin at the column's top
|
|
26
|
+
* left, centred on the bands given as nearly as the display's `height` holds
|
|
27
|
+
* it whole, and cut to that height where it is longer. The on-screen overlay
|
|
28
|
+
* and the export both draw through this, so the two cannot place a caption
|
|
29
|
+
* differently.
|
|
30
|
+
*/
|
|
31
|
+
export default function AxisCaption({ axis, bandTops, height, }) {
|
|
32
|
+
const palette = usePalette();
|
|
33
|
+
const x = axis.side === 'right'
|
|
34
|
+
? axisGutterWidth(axis) - CAPTION_INSET_PX
|
|
35
|
+
: CAPTION_INSET_PX;
|
|
36
|
+
const caption = fitCaption(axis.caption ?? '', height);
|
|
37
|
+
if (!caption) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const half = caption.length / 2;
|
|
41
|
+
const y = Math.min(Math.max(axisCaptionY(axis, bandTops), half), height - half);
|
|
42
|
+
return (_jsx("text", { x: x, y: y, fontSize: AXIS_FONT_PX, textAnchor: "middle", fill: palette.text.primary, stroke: palette.background.default, strokeWidth: 2.5, paintOrder: "stroke", transform: `rotate(-90, ${x}, ${y})`, children: caption.text }));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The on-screen caption of one scale: an `<svg>` over the gutter column, the
|
|
46
|
+
* height of the display, with `AxisCaption` inside.
|
|
47
|
+
*/
|
|
48
|
+
export function AxisCaptionOverlay({ axis, bandTops, width, height, }) {
|
|
49
|
+
return (_jsx("svg", { style: {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
top: 0,
|
|
52
|
+
left: axisGutterLeft(axis, width, VERTICAL_SCROLLBAR_CLEARANCE),
|
|
53
|
+
pointerEvents: 'none',
|
|
54
|
+
height,
|
|
55
|
+
width: axisGutterWidth(axis),
|
|
56
|
+
}, children: _jsx(AxisCaption, { axis: axis, bandTops: bandTops, height: height }) }));
|
|
57
|
+
}
|
package/esm/AxisGutter.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import type { YAxis } from './valueScale.ts';
|
|
|
2
2
|
/**
|
|
3
3
|
* One band's axis drawn inside its gutter, the origin at the gutter's left
|
|
4
4
|
* edge: the spine on the gutter's inner edge — its right for a left-side
|
|
5
|
-
* axis, its left for a right-side one — with the numbers growing outward
|
|
6
|
-
* the caption, where the scale has one, rotated along the outer edge. The
|
|
5
|
+
* axis, its left for a right-side one — with the numbers growing outward. The
|
|
7
6
|
* on-screen overlay and the export both draw through this, so the two cannot
|
|
8
|
-
* place a spine differently.
|
|
7
|
+
* place a spine differently. The scale's caption is `AxisCaption`'s, drawn once
|
|
8
|
+
* for the scale and not per band.
|
|
9
9
|
*/
|
|
10
10
|
export default function AxisGutter({ axis }: {
|
|
11
11
|
axis: YAxis;
|
package/esm/AxisGutter.js
CHANGED
|
@@ -1,25 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import YScaleBar from './YScaleBar.js';
|
|
4
|
-
import {
|
|
5
|
-
// Half the rotated caption's own 10px height plus a px of margin: how far in
|
|
6
|
-
// from the gutter's outer edge it sits with its glyphs still inside.
|
|
7
|
-
const CAPTION_INSET_PX = 8;
|
|
3
|
+
import { axisGutterWidth } from './axisPlacement.js';
|
|
8
4
|
/**
|
|
9
5
|
* One band's axis drawn inside its gutter, the origin at the gutter's left
|
|
10
6
|
* edge: the spine on the gutter's inner edge — its right for a left-side
|
|
11
|
-
* axis, its left for a right-side one — with the numbers growing outward
|
|
12
|
-
* the caption, where the scale has one, rotated along the outer edge. The
|
|
7
|
+
* axis, its left for a right-side one — with the numbers growing outward. The
|
|
13
8
|
* on-screen overlay and the export both draw through this, so the two cannot
|
|
14
|
-
* place a spine differently.
|
|
9
|
+
* place a spine differently. The scale's caption is `AxisCaption`'s, drawn once
|
|
10
|
+
* for the scale and not per band.
|
|
15
11
|
*/
|
|
16
12
|
export default function AxisGutter({ axis }) {
|
|
17
|
-
const palette = usePalette();
|
|
18
13
|
const right = axis.side === 'right';
|
|
19
|
-
|
|
20
|
-
const captionX = right
|
|
21
|
-
? AXIS_GUTTER_WIDTH_PX - CAPTION_INSET_PX
|
|
22
|
-
: CAPTION_INSET_PX;
|
|
23
|
-
const midY = (ticks.yTop + ticks.yBottom) / 2;
|
|
24
|
-
return (_jsxs(_Fragment, { children: [_jsx("g", { transform: `translate(${right ? 0 : AXIS_GUTTER_WIDTH_PX} 0)`, children: _jsx(YScaleBar, { ticks: ticks, orientation: right ? 'right' : 'left', bandHeight: height }) }), caption ? (_jsx("text", { x: captionX, y: midY, fontSize: 10, textAnchor: "middle", fill: palette.text.primary, stroke: palette.background.default, strokeWidth: 2.5, paintOrder: "stroke", transform: `rotate(-90, ${captionX}, ${midY})`, children: caption })) : null] }));
|
|
14
|
+
return (_jsx("g", { transform: `translate(${right ? 0 : axisGutterWidth(axis)} 0)`, children: _jsx(YScaleBar, { ticks: axis.ticks, orientation: right ? 'right' : 'left', bandHeight: axis.height }) }));
|
|
25
15
|
}
|
package/esm/FloatingLegend.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { LegendItem, LegendSection } from '@jbrowse/core/ui/legendSpec';
|
|
2
|
-
export declare const FLOATING_LEGEND_TOP_PX = 10;
|
|
3
2
|
export type { LegendGradient, LegendItem, LegendSection, LegendSpec, LegendSwatch, } from '@jbrowse/core/ui/legendSpec';
|
|
4
3
|
declare const FloatingLegend: ({ items, sections, title, onDismiss, onDismissSection, onItemClick, maxItems, maxWidth, top, }: {
|
|
5
4
|
items?: LegendItem[];
|
package/esm/FloatingLegend.js
CHANGED
|
@@ -8,10 +8,9 @@ import { LegendSwatchGlyph } from '@jbrowse/core/ui/LegendSwatchGlyph';
|
|
|
8
8
|
import { legendSwatches, nonEmptyLegendSections, } from '@jbrowse/core/ui/legendSpec';
|
|
9
9
|
import { cx, makeStyles } from '@jbrowse/core/util/tss-react';
|
|
10
10
|
import { observer } from 'mobx-react';
|
|
11
|
+
import { FLOATING_LEGEND_TOP_PX } from './floatingLegendTop.js';
|
|
11
12
|
import Tooltip from './tooltip/Tooltip.js';
|
|
12
13
|
import { TrackOverlayPortal } from './trackOverlay/TrackOverlayPortal.js';
|
|
13
|
-
// The key's own inset from the display's top-right corner.
|
|
14
|
-
export const FLOATING_LEGEND_TOP_PX = 10;
|
|
15
14
|
const useStyles = makeStyles()(theme => ({
|
|
16
15
|
legend: {
|
|
17
16
|
position: 'absolute',
|
|
@@ -27,6 +26,9 @@ const useStyles = makeStyles()(theme => ({
|
|
|
27
26
|
border: `1px solid ${theme.palette.divider}`,
|
|
28
27
|
borderRadius: 3,
|
|
29
28
|
padding: 3,
|
|
29
|
+
// Two keys on one short display ran past its bottom edge and were cut
|
|
30
|
+
// there; the box scrolls inside the display instead.
|
|
31
|
+
overflowY: 'auto',
|
|
30
32
|
fontSize: 10,
|
|
31
33
|
zIndex: 100,
|
|
32
34
|
// the default; `maxWidth` overrides it per display
|
|
@@ -89,6 +91,13 @@ const useStyles = makeStyles()(theme => ({
|
|
|
89
91
|
overflow: 'hidden',
|
|
90
92
|
textOverflow: 'ellipsis',
|
|
91
93
|
},
|
|
94
|
+
// Wraps where every other line ellipsizes: a note is a sentence, and half of
|
|
95
|
+
// one says nothing.
|
|
96
|
+
sectionNote: {
|
|
97
|
+
color: theme.palette.text.secondary,
|
|
98
|
+
whiteSpace: 'normal',
|
|
99
|
+
marginBottom: 2,
|
|
100
|
+
},
|
|
92
101
|
sectionClose: {
|
|
93
102
|
font: 'inherit',
|
|
94
103
|
lineHeight: 1,
|
|
@@ -236,10 +245,9 @@ const LegendItemList = observer(function LegendItemList({ items, maxItems, onIte
|
|
|
236
245
|
// is more than one section, so a single-scheme legend looks unchanged. `title`
|
|
237
246
|
// is a heading for the whole box, shown regardless of section count.
|
|
238
247
|
//
|
|
239
|
-
// `onItemClick` makes the rows
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
// leave the others inert.
|
|
248
|
+
// `onItemClick` makes the rows of a section declaring `focusesRows` act —
|
|
249
|
+
// clicking an entry focuses the rows it names. A section coloring features or
|
|
250
|
+
// cells stays inert text beside it.
|
|
243
251
|
const FloatingLegend = observer(function FloatingLegend({ items, sections, title, onDismiss, onDismissSection, onItemClick, maxItems = DEFAULT_MAX_ITEMS, maxWidth = DEFAULT_MAX_WIDTH, top = FLOATING_LEGEND_TOP_PX, }) {
|
|
244
252
|
const { classes } = useStyles();
|
|
245
253
|
const nonEmpty = nonEmptyLegendSections({ items, sections });
|
|
@@ -249,11 +257,11 @@ const FloatingLegend = observer(function FloatingLegend({ items, sections, title
|
|
|
249
257
|
const multiSection = nonEmpty.length > 1;
|
|
250
258
|
// portal above the inter-region padding masks so the key isn't buried at
|
|
251
259
|
// whole-genome / multi-region scale (see TrackOverlayPortal)
|
|
252
|
-
return (_jsx(TrackOverlayPortal, { children: _jsxs("div", { className: cx(classes.legend, onDismiss && classes.withClose), style: { top, maxWidth }, "data-testid": "floating-legend", "data-gesture-owner": "true", children: [onDismiss ? (_jsx(Tooltip, { title: "Hide legend", children: _jsx("button", { type: "button", className: classes.closeButton, "aria-label": "Hide legend", onClick: () => {
|
|
260
|
+
return (_jsx(TrackOverlayPortal, { children: _jsxs("div", { className: cx(classes.legend, onDismiss && classes.withClose), style: { top, maxWidth, maxHeight: `calc(100% - ${top + 4}px)` }, "data-testid": "floating-legend", "data-gesture-owner": "true", children: [onDismiss ? (_jsx(Tooltip, { title: "Hide legend", children: _jsx("button", { type: "button", className: classes.closeButton, "aria-label": "Hide legend", onClick: () => {
|
|
253
261
|
onDismiss();
|
|
254
262
|
}, children: "\u00D7" }) })) : null, title ? _jsx("div", { className: classes.topTitle, children: title }) : null, nonEmpty.map(section => (_jsxs("div", { className: classes.section, children: [multiSection && section.title ? (_jsxs("div", { className: classes.sectionHeader, children: [_jsx("span", { className: classes.sectionTitle, children: section.title }), onDismissSection ? (_jsx(Tooltip, { title: `Hide ${section.title}`, children: _jsx("button", { type: "button", className: classes.sectionClose, "aria-label": `Hide ${section.title}`, onClick: () => {
|
|
255
263
|
onDismissSection(section.id);
|
|
256
|
-
}, children: "\u00D7" }) })) : null] })) : null, _jsx(LegendItemList, { items: section.items, maxItems: maxItems, onItemClick: onItemClick
|
|
264
|
+
}, children: "\u00D7" }) })) : null] })) : null, section.note ? (_jsx("div", { className: classes.sectionNote, children: section.note })) : null, _jsx(LegendItemList, { items: section.items, maxItems: maxItems, onItemClick: onItemClick && section.focusesRows
|
|
257
265
|
? item => {
|
|
258
266
|
onItemClick(item, section);
|
|
259
267
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
export declare const SCORE_CAPTION_HEIGHT = 16;
|
|
2
1
|
/**
|
|
3
2
|
* The one-line `[min, max]` caption that stands in for a y axis where none can
|
|
4
3
|
* be drawn — a scale whose bands are too short for tick labels, and a density
|
|
5
4
|
* track whose rows each carry their own colour, so no single ramp describes
|
|
6
5
|
* them. An axis stand-in rather than a key, which is why it is not a colour
|
|
7
6
|
* scale and draws whether or not the legend does. Right-aligned to
|
|
8
|
-
* `canvasWidth` at the top of the display
|
|
7
|
+
* `canvasWidth` at the top of the display, led by the scale's `caption` where
|
|
8
|
+
* it has one, which has no gutter to sit in here.
|
|
9
9
|
*/
|
|
10
|
-
export default function ScoreDomainCaption({ domain, scaleType, canvasWidth, }: {
|
|
10
|
+
export default function ScoreDomainCaption({ domain, scaleType, canvasWidth, caption, }: {
|
|
11
11
|
domain: [number, number];
|
|
12
12
|
scaleType: string;
|
|
13
13
|
canvasWidth: number;
|
|
14
|
+
caption?: string;
|
|
14
15
|
}): import("react").JSX.Element;
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
3
3
|
import { measureLegendText } from '@jbrowse/core/ui/measureLegendText';
|
|
4
|
+
import { formatScore } from '@jbrowse/core/util/numericUtils';
|
|
4
5
|
import { stripAlpha } from '@jbrowse/core/util/svgColorProps';
|
|
5
|
-
import {
|
|
6
|
-
export const SCORE_CAPTION_HEIGHT = 16;
|
|
6
|
+
import { SCORE_CAPTION_HEIGHT } from './yAxisConstants.js';
|
|
7
7
|
/**
|
|
8
8
|
* The one-line `[min, max]` caption that stands in for a y axis where none can
|
|
9
9
|
* be drawn — a scale whose bands are too short for tick labels, and a density
|
|
10
10
|
* track whose rows each carry their own colour, so no single ramp describes
|
|
11
11
|
* them. An axis stand-in rather than a key, which is why it is not a colour
|
|
12
12
|
* scale and draws whether or not the legend does. Right-aligned to
|
|
13
|
-
* `canvasWidth` at the top of the display
|
|
13
|
+
* `canvasWidth` at the top of the display, led by the scale's `caption` where
|
|
14
|
+
* it has one, which has no gutter to sit in here.
|
|
14
15
|
*/
|
|
15
|
-
export default function ScoreDomainCaption({ domain, scaleType, canvasWidth, }) {
|
|
16
|
+
export default function ScoreDomainCaption({ domain, scaleType, canvasWidth, caption, }) {
|
|
16
17
|
const palette = usePalette();
|
|
17
18
|
const suffix = scaleType === 'log' ? ' (log)' : scaleType === 'symlog' ? ' (symlog)' : '';
|
|
18
|
-
const text = `[${formatScore(domain[0])}, ${formatScore(domain[1])}]${suffix}`;
|
|
19
|
+
const text = `${caption ? `${caption} ` : ''}[${formatScore(domain[0])}, ${formatScore(domain[1])}]${suffix}`;
|
|
19
20
|
const len = measureLegendText(text, 12);
|
|
20
21
|
const xpos = Math.max(0, canvasWidth - len - 10);
|
|
21
22
|
return (_jsxs("g", { children: [_jsx("rect", { x: xpos - 3, y: 0, width: len + 6, height: SCORE_CAPTION_HEIGHT, fill: stripAlpha(palette.background.paper), fillOpacity: 0.8 }), _jsx("text", { y: 12, x: xpos, fontSize: 12, fill: palette.text.primary, children: text })] }));
|
package/esm/ScoreRules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
3
|
-
|
|
3
|
+
import { DEFAULT_RULE_COLOR } from './yAxisConstants.js';
|
|
4
4
|
// Bare marks, no wrapping <svg>, so the on-screen overlay and the SVG export
|
|
5
5
|
// draw the same elements from the same y. Same split as CrossHatchLines.
|
|
6
6
|
export function ScoreRuleLines({ marks, width, offsetY = 0, }) {
|
package/esm/YScaleBar.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
3
|
+
import { AXIS_FONT_PX, TICK_LABEL_X_PX } from './yAxisConstants.js';
|
|
3
4
|
import { clampStrokeInsideAxis } from './yScaleTicks.js';
|
|
4
5
|
// Not an `observer`, and deliberately: there is nothing here to observe. Every
|
|
5
6
|
// caller already reads `model.ticks` inside its own observer and hands the
|
|
@@ -42,5 +43,5 @@ export default function YScaleBar({ ticks, orientation, bandHeight, }) {
|
|
|
42
43
|
const inset = Math.min(Math.max(sy, LABEL_HALF_HEIGHT_PX), bandHeight - LABEL_HALF_HEIGHT_PX);
|
|
43
44
|
return inset === sy ? undefined : inset - sy;
|
|
44
45
|
};
|
|
45
|
-
return (_jsxs("g", { fontSize:
|
|
46
|
+
return (_jsxs("g", { fontSize: AXIS_FONT_PX, textAnchor: isLeft ? 'end' : 'start', stroke: fg, strokeWidth: 1, children: [_jsx("path", { fill: "none", d: `M${k * tickLength} ${strokeY(yTop)}H0.5V${strokeY(yBottom)}H${k * tickLength}` }), items.map(({ value, y, label }) => (_jsxs("g", { transform: `translate(0,${strokeY(y)})`, children: [_jsx("line", { x2: k * tickLength }), _jsx("text", { stroke: bg, strokeWidth: 2.5, paintOrder: "stroke", fill: fg, y: labelDy(strokeY(y)), dy: "0.32em", x: k * TICK_LABEL_X_PX, children: label ?? value })] }, `${value}-${y}`)))] }));
|
|
46
47
|
}
|
package/esm/YScaleBarOverlay.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { VERTICAL_SCROLLBAR_CLEARANCE } from '@jbrowse/core/ui/VerticalScrollbar';
|
|
3
3
|
import AxisGutter from './AxisGutter.js';
|
|
4
|
-
import { axisGutterLeft } from './axisPlacement.js';
|
|
5
|
-
import { AXIS_GUTTER_WIDTH_PX } from './yScaleTicks.js';
|
|
4
|
+
import { axisGutterLeft, axisGutterWidth } from './axisPlacement.js';
|
|
6
5
|
/**
|
|
7
6
|
* The on-screen axis of one band: an `<svg>` the width of the gutter,
|
|
8
7
|
* positioned absolutely over the band at `top`, with `AxisGutter` inside. A
|
|
@@ -15,7 +14,7 @@ export default function YScaleBarOverlay({ axis, top, width, }) {
|
|
|
15
14
|
left: axisGutterLeft(axis, width, VERTICAL_SCROLLBAR_CLEARANCE),
|
|
16
15
|
pointerEvents: 'none',
|
|
17
16
|
height: axis.height,
|
|
18
|
-
width:
|
|
17
|
+
width: axisGutterWidth(axis),
|
|
19
18
|
overflow: 'visible',
|
|
20
19
|
}, children: _jsx(AxisGutter, { axis: axis }) }));
|
|
21
20
|
}
|
package/esm/axisPlacement.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ValueScale } from './valueScale.ts';
|
|
1
|
+
import type { ValueScale, YAxis } from './valueScale.ts';
|
|
2
2
|
/**
|
|
3
3
|
* Below this band height a full `YScaleBar`'s tick labels overlap each other,
|
|
4
4
|
* so the chrome draws one `ScoreDomainCaption` for the scale instead of an
|
|
@@ -6,11 +6,29 @@ import type { ValueScale } from './valueScale.ts';
|
|
|
6
6
|
* makes 30 the number.
|
|
7
7
|
*/
|
|
8
8
|
export declare const COMPACT_AXIS_HEIGHT = 30;
|
|
9
|
+
/**
|
|
10
|
+
* Whether a scale places anything by y, which is whether its rules draw: it
|
|
11
|
+
* rules at least one band, where `[]` is a scale mapped to colour instead.
|
|
12
|
+
*/
|
|
13
|
+
export declare function rulesABand(scale: Pick<ValueScale, 'bandTops'>): boolean;
|
|
9
14
|
/**
|
|
10
15
|
* Whether a scale gets an axis at all: it rules at least one band, and the
|
|
11
16
|
* band has room for tick labels. A scale that fails either is captioned.
|
|
12
17
|
*/
|
|
13
18
|
export declare function axisDrawn(scale: Pick<ValueScale, 'height' | 'bandTops'>): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The y a scale's one caption centres on: halfway down the extent of the bands
|
|
21
|
+
* given, from the first's top tick to the last's bottom. One band gives the
|
|
22
|
+
* middle of its own axis.
|
|
23
|
+
*/
|
|
24
|
+
export declare function axisCaptionY(axis: Pick<YAxis, 'ticks'>, bandTops: number[]): number;
|
|
25
|
+
/**
|
|
26
|
+
* How wide an axis's gutter is: the ordinary width, grown inward over the plot
|
|
27
|
+
* where a caption and the widest tick label cannot share it, so neither is
|
|
28
|
+
* drawn over the other. The caption keeps the outer edge and the labels stay
|
|
29
|
+
* beside the spine.
|
|
30
|
+
*/
|
|
31
|
+
export declare function axisGutterWidth(axis: Pick<YAxis, 'ticks' | 'caption'>): number;
|
|
14
32
|
/** How far in from the plot's right edge a right-side gutter ends in an export. */
|
|
15
33
|
export declare const AXIS_RIGHT_INSET_PX = 4;
|
|
16
34
|
/**
|
|
@@ -19,7 +37,8 @@ export declare const AXIS_RIGHT_INSET_PX = 4;
|
|
|
19
37
|
* scrollbar a display may mount on screen; a margin in an export). A left-side
|
|
20
38
|
* axis takes the gutter at the display's left edge, past whatever panel the
|
|
21
39
|
* scale's `left` reserves — except in an export, whose margin is the gutter,
|
|
22
|
-
* so an axis nothing pushes right sits in the margin with its
|
|
23
|
-
*
|
|
40
|
+
* so an axis nothing pushes right sits in the margin with its numbers outside
|
|
41
|
+
* the plot and its spine on the content edge, or past it by what
|
|
42
|
+
* `axisGutterWidth` grew.
|
|
24
43
|
*/
|
|
25
|
-
export declare function axisGutterLeft(
|
|
44
|
+
export declare function axisGutterLeft(axis: Pick<YAxis, 'side' | 'left' | 'ticks' | 'caption'>, width: number, rightInset: number, exportContentLeft?: number): number;
|
package/esm/axisPlacement.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { measureText } from '@jbrowse/core/util/measureText';
|
|
2
|
+
import { AXIS_FONT_PX, CAPTION_BAND_PX, TICK_LABEL_X_PX, } from './yAxisConstants.js';
|
|
1
3
|
import { AXIS_GUTTER_WIDTH_PX } from './yScaleTicks.js';
|
|
2
4
|
/**
|
|
3
5
|
* Below this band height a full `YScaleBar`'s tick labels overlap each other,
|
|
@@ -6,12 +8,44 @@ import { AXIS_GUTTER_WIDTH_PX } from './yScaleTicks.js';
|
|
|
6
8
|
* makes 30 the number.
|
|
7
9
|
*/
|
|
8
10
|
export const COMPACT_AXIS_HEIGHT = 30;
|
|
11
|
+
/**
|
|
12
|
+
* Whether a scale places anything by y, which is whether its rules draw: it
|
|
13
|
+
* rules at least one band, where `[]` is a scale mapped to colour instead.
|
|
14
|
+
*/
|
|
15
|
+
export function rulesABand(scale) {
|
|
16
|
+
return (scale.bandTops?.length ?? 1) > 0;
|
|
17
|
+
}
|
|
9
18
|
/**
|
|
10
19
|
* Whether a scale gets an axis at all: it rules at least one band, and the
|
|
11
20
|
* band has room for tick labels. A scale that fails either is captioned.
|
|
12
21
|
*/
|
|
13
22
|
export function axisDrawn(scale) {
|
|
14
|
-
return (
|
|
23
|
+
return rulesABand(scale) && scale.height >= COMPACT_AXIS_HEIGHT;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The y a scale's one caption centres on: halfway down the extent of the bands
|
|
27
|
+
* given, from the first's top tick to the last's bottom. One band gives the
|
|
28
|
+
* middle of its own axis.
|
|
29
|
+
*/
|
|
30
|
+
export function axisCaptionY(axis, bandTops) {
|
|
31
|
+
const { yTop, yBottom } = axis.ticks;
|
|
32
|
+
return (Math.min(...bandTops) + yTop + Math.max(...bandTops) + yBottom) / 2;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* How wide an axis's gutter is: the ordinary width, grown inward over the plot
|
|
36
|
+
* where a caption and the widest tick label cannot share it, so neither is
|
|
37
|
+
* drawn over the other. The caption keeps the outer edge and the labels stay
|
|
38
|
+
* beside the spine.
|
|
39
|
+
*/
|
|
40
|
+
export function axisGutterWidth(axis) {
|
|
41
|
+
if (!axis.caption) {
|
|
42
|
+
return AXIS_GUTTER_WIDTH_PX;
|
|
43
|
+
}
|
|
44
|
+
let widest = 0;
|
|
45
|
+
for (const { value, label } of axis.ticks.items) {
|
|
46
|
+
widest = Math.max(widest, measureText(label ?? value, AXIS_FONT_PX));
|
|
47
|
+
}
|
|
48
|
+
return Math.max(AXIS_GUTTER_WIDTH_PX, Math.ceil(CAPTION_BAND_PX + widest + TICK_LABEL_X_PX));
|
|
15
49
|
}
|
|
16
50
|
/** How far in from the plot's right edge a right-side gutter ends in an export. */
|
|
17
51
|
export const AXIS_RIGHT_INSET_PX = 4;
|
|
@@ -21,14 +55,15 @@ export const AXIS_RIGHT_INSET_PX = 4;
|
|
|
21
55
|
* scrollbar a display may mount on screen; a margin in an export). A left-side
|
|
22
56
|
* axis takes the gutter at the display's left edge, past whatever panel the
|
|
23
57
|
* scale's `left` reserves — except in an export, whose margin is the gutter,
|
|
24
|
-
* so an axis nothing pushes right sits in the margin with its
|
|
25
|
-
*
|
|
58
|
+
* so an axis nothing pushes right sits in the margin with its numbers outside
|
|
59
|
+
* the plot and its spine on the content edge, or past it by what
|
|
60
|
+
* `axisGutterWidth` grew.
|
|
26
61
|
*/
|
|
27
|
-
export function axisGutterLeft(
|
|
28
|
-
if (
|
|
29
|
-
return width - rightInset -
|
|
62
|
+
export function axisGutterLeft(axis, width, rightInset, exportContentLeft) {
|
|
63
|
+
if (axis.side === 'right') {
|
|
64
|
+
return width - rightInset - axisGutterWidth(axis);
|
|
30
65
|
}
|
|
31
|
-
const left =
|
|
66
|
+
const left = axis.left ?? 0;
|
|
32
67
|
return exportContentLeft !== undefined && left === 0
|
|
33
68
|
? exportContentLeft - AXIS_GUTTER_WIDTH_PX
|
|
34
69
|
: left;
|
|
@@ -22,10 +22,9 @@
|
|
|
22
22
|
* It has never been reachable: the two displays that render the control row
|
|
23
23
|
* (alignments, canvas) are not among the four that report a `ready`-phase
|
|
24
24
|
* status (clustering, on multi-wiggle / multi-row features / maf / the
|
|
25
|
-
* multi-sample variant pair).
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* message that silently never appears.
|
|
25
|
+
* multi-sample variant pair). The layout is structural anyway, because neither
|
|
26
|
+
* side checks for the constraint, so the first display to want both would hit
|
|
27
|
+
* the bug as a status message that never appears.
|
|
29
28
|
*
|
|
30
29
|
* `BottomRightIndicators` already described itself as "the single anchor point
|
|
31
30
|
* for every bottom-right overlay ... so they lay out as one row instead of each
|
package/esm/bottomRightCorner.js
CHANGED
|
@@ -23,10 +23,9 @@ import { createContext } from 'react';
|
|
|
23
23
|
* It has never been reachable: the two displays that render the control row
|
|
24
24
|
* (alignments, canvas) are not among the four that report a `ready`-phase
|
|
25
25
|
* status (clustering, on multi-wiggle / multi-row features / maf / the
|
|
26
|
-
* multi-sample variant pair).
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* message that silently never appears.
|
|
26
|
+
* multi-sample variant pair). The layout is structural anyway, because neither
|
|
27
|
+
* side checks for the constraint, so the first display to want both would hit
|
|
28
|
+
* the bug as a status message that never appears.
|
|
30
29
|
*
|
|
31
30
|
* `BottomRightIndicators` already described itself as "the single anchor point
|
|
32
31
|
* for every bottom-right overlay ... so they lay out as one row instead of each
|
|
@@ -2,6 +2,6 @@ import type { DisplayChromeOverlays } from './chromeOverlays.ts';
|
|
|
2
2
|
export declare const DisplayChromeOverlayProvider: import("react").Provider<DisplayChromeOverlays | undefined>;
|
|
3
3
|
/**
|
|
4
4
|
* The set a provider installed, or `undefined` for "nobody asked". The caller
|
|
5
|
-
* supplies the default,
|
|
5
|
+
* supplies the default, so this module binds none.
|
|
6
6
|
*/
|
|
7
7
|
export declare function useChromeOverlayOverride(): DisplayChromeOverlays | undefined;
|
|
@@ -18,10 +18,9 @@ import { createContext, use } from 'react';
|
|
|
18
18
|
// `muiFreeSeam.test.ts` fails naming the import trail if that stops being true.
|
|
19
19
|
/**
|
|
20
20
|
* Runtime override for the overlay set. **Defaults to undefined, not to a
|
|
21
|
-
* component set
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* display still works, it just stops looking like JBrowse. Undefined means
|
|
21
|
+
* component set**. A plain default would degrade every display that renders
|
|
22
|
+
* outside a provider (unit tests, SVG export, breakpoint-split-view's
|
|
23
|
+
* overlayUtils): the display would still work, but stop looking like JBrowse. Undefined means
|
|
25
24
|
* "nobody asked", so `DisplayChrome` keeps its MUI set and nothing changes.
|
|
26
25
|
*
|
|
27
26
|
* This is the escape hatch for embedders who want JBrowse's *own* displays
|
|
@@ -37,7 +36,7 @@ const DisplayChromeOverlayContext = createContext(undefined);
|
|
|
37
36
|
export const DisplayChromeOverlayProvider = DisplayChromeOverlayContext.Provider;
|
|
38
37
|
/**
|
|
39
38
|
* The set a provider installed, or `undefined` for "nobody asked". The caller
|
|
40
|
-
* supplies the default,
|
|
39
|
+
* supplies the default, so this module binds none.
|
|
41
40
|
*/
|
|
42
41
|
export function useChromeOverlayOverride() {
|
|
43
42
|
return use(DisplayChromeOverlayContext);
|
package/esm/chromeOverlays.d.ts
CHANGED
|
@@ -54,8 +54,10 @@ export interface DisplayChromeOverlays {
|
|
|
54
54
|
visible: boolean;
|
|
55
55
|
}>;
|
|
56
56
|
/**
|
|
57
|
-
* The loading scrim. `visible` is `displayPhase
|
|
58
|
-
*
|
|
57
|
+
* The loading scrim. `visible` is `displayPhase` `loading` or `canceled`,
|
|
58
|
+
* the second drawn from `model.fetchCanceled` with Retry in place of Cancel;
|
|
59
|
+
* `immediate` asks it to skip its anti-flash delay because nothing is painted
|
|
60
|
+
* yet.
|
|
59
61
|
*
|
|
60
62
|
* Mounted unconditionally, so it must handle `visible === false` itself — and
|
|
61
63
|
* that is load-bearing rather than a style choice: the anti-flash delay is
|