@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.
Files changed (49) hide show
  1. package/README.md +24 -14
  2. package/esm/AxisCaption.d.ts +24 -0
  3. package/esm/AxisCaption.js +57 -0
  4. package/esm/AxisGutter.d.ts +3 -3
  5. package/esm/AxisGutter.js +6 -16
  6. package/esm/FloatingLegend.d.ts +0 -1
  7. package/esm/FloatingLegend.js +16 -8
  8. package/esm/ScoreDomainCaption.d.ts +4 -3
  9. package/esm/ScoreDomainCaption.js +6 -5
  10. package/esm/ScoreRules.js +1 -1
  11. package/esm/YScaleBar.js +2 -1
  12. package/esm/YScaleBarOverlay.js +2 -3
  13. package/esm/axisPlacement.d.ts +23 -4
  14. package/esm/axisPlacement.js +42 -7
  15. package/esm/bottomRightCorner.d.ts +3 -4
  16. package/esm/bottomRightCorner.js +3 -4
  17. package/esm/chromeOverlayContext.d.ts +1 -1
  18. package/esm/chromeOverlayContext.js +4 -5
  19. package/esm/chromeOverlays.d.ts +4 -2
  20. package/esm/embed/index.d.ts +70 -0
  21. package/esm/embed/index.js +69 -0
  22. package/esm/embed/legend.d.ts +8 -0
  23. package/esm/embed/legend.js +35 -0
  24. package/esm/embed/location.d.ts +17 -0
  25. package/esm/embed/location.js +65 -0
  26. package/esm/embed/provider.d.ts +11 -0
  27. package/esm/embed/provider.js +6 -0
  28. package/esm/embed/regions.d.ts +53 -0
  29. package/esm/embed/regions.js +95 -0
  30. package/esm/floatingLegendTop.d.ts +1 -0
  31. package/esm/floatingLegendTop.js +2 -0
  32. package/esm/index.d.ts +8 -6
  33. package/esm/index.js +7 -5
  34. package/esm/plainChromeOverlays.d.ts +1 -1
  35. package/esm/plainChromeOverlays.js +5 -5
  36. package/esm/tooltip/Tooltip.d.ts +3 -3
  37. package/esm/tooltip/Tooltip.js +3 -3
  38. package/esm/trackControl/trackControlContext.d.ts +1 -1
  39. package/esm/trackControl/trackControlContext.js +6 -6
  40. package/esm/trackOverlay/TrackOverlaySlot.d.ts +2 -2
  41. package/esm/trackOverlay/TrackOverlaySlot.js +2 -2
  42. package/esm/valueScale.d.ts +22 -2
  43. package/esm/yAxisConstants.d.ts +6 -0
  44. package/esm/yAxisConstants.js +14 -0
  45. package/esm/yScaleTicks.d.ts +2 -1
  46. package/esm/yScaleTicks.js +2 -1
  47. package/package.json +7 -4
  48. package/esm/formatScore.d.ts +0 -1
  49. package/esm/formatScore.js +0 -14
@@ -0,0 +1,70 @@
1
+ import type { PanZoomView } from '@jbrowse/core/util/usePanZoom';
2
+ import type { ViewStatus as ViewStatusValue } from '@jbrowse/core/util/viewStatus';
3
+ import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
4
+ import type React from 'react';
5
+ export { Legend } from './legend.tsx';
6
+ export { LocationBox, useLocationBox } from './location.tsx';
7
+ export { EmbedProvider } from './provider.tsx';
8
+ export { Highlights, RegionSeams, Scalebar } from './regions.tsx';
9
+ export interface EmbedDisplay {
10
+ height: number;
11
+ RenderingComponent: React.ComponentType<{
12
+ model: EmbedDisplay;
13
+ onHorizontalScroll?: (distance: number) => void;
14
+ }>;
15
+ }
16
+ export interface EmbedView extends PanZoomView, IStateTreeNode {
17
+ status: ViewStatusValue;
18
+ tracks: {
19
+ configuration: {
20
+ trackId: string;
21
+ };
22
+ }[];
23
+ getTrack: (trackId: string) => {
24
+ activeDisplay: EmbedDisplay;
25
+ } | undefined;
26
+ setWidth: (width: number) => void;
27
+ }
28
+ export declare const Track: ({ view, trackId, }: {
29
+ view: EmbedView;
30
+ trackId: string;
31
+ }) => React.JSX.Element | null;
32
+ export interface ResizableDisplay {
33
+ setResizing: (resizing: boolean) => void;
34
+ resizeHeight: (distance: number) => unknown;
35
+ }
36
+ export declare const ResizeHandle: ({ view, trackId, style, }: {
37
+ view: {
38
+ getTrack: (trackId: string) => {
39
+ activeDisplay: ResizableDisplay;
40
+ } | undefined;
41
+ };
42
+ trackId: string;
43
+ style?: React.CSSProperties;
44
+ }) => React.JSX.Element;
45
+ export declare const TrackToggle: ({ view, trackId, style, children, }: {
46
+ view: {
47
+ tracks: {
48
+ configuration: {
49
+ trackId: string;
50
+ };
51
+ }[];
52
+ launchToggleTrack: (trackId: string) => Promise<unknown>;
53
+ };
54
+ trackId: string;
55
+ style?: React.CSSProperties;
56
+ children?: React.ReactNode;
57
+ }) => React.JSX.Element;
58
+ export declare const ViewStatus: ({ view, style, }: {
59
+ view: {
60
+ status: ViewStatusValue;
61
+ };
62
+ style?: React.CSSProperties;
63
+ }) => React.JSX.Element | null;
64
+ export declare const TrackStack: ({ view, trackIds, renderTrack, style, children, }: {
65
+ view: EmbedView;
66
+ trackIds?: string[];
67
+ renderTrack?: (trackId: string) => React.ReactNode;
68
+ style?: React.CSSProperties;
69
+ children?: React.ReactNode;
70
+ }) => React.JSX.Element;
@@ -0,0 +1,69 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment, Suspense } from 'react';
3
+ import { useWidthSetter } from '@jbrowse/core/util/hooks';
4
+ import { usePanZoom } from '@jbrowse/core/util/usePanZoom';
5
+ import { useResizeDrag } from '@jbrowse/core/util/useResizeDrag';
6
+ import { observer } from 'mobx-react';
7
+ import { TrackOverlaySlot } from '../trackOverlay/TrackOverlaySlot.js';
8
+ export { Legend } from './legend.js';
9
+ export { LocationBox, useLocationBox } from './location.js';
10
+ export { EmbedProvider } from './provider.js';
11
+ export { Highlights, RegionSeams, Scalebar } from './regions.js';
12
+ export const Track = observer(function Track({ view, trackId, }) {
13
+ const display = view.getTrack(trackId)?.activeDisplay;
14
+ if (!display) {
15
+ return null;
16
+ }
17
+ const { RenderingComponent } = display;
18
+ return (_jsx(TrackOverlaySlot, { zIndex: 3, style: { height: display.height }, children: _jsx("div", { style: { position: 'absolute', inset: 0, contain: 'strict' }, children: _jsx(Suspense, { fallback: null, children: _jsx(RenderingComponent, { model: display, onHorizontalScroll: view.horizontalScroll }) }) }) }));
19
+ });
20
+ export const ResizeHandle = observer(function ResizeHandle({ view, trackId, style, }) {
21
+ const display = view.getTrack(trackId)?.activeDisplay;
22
+ const props = useResizeDrag({
23
+ onDragStart: () => {
24
+ display?.setResizing(true);
25
+ },
26
+ onDrag: distance => {
27
+ display?.resizeHeight(distance);
28
+ },
29
+ onDragEnd: () => {
30
+ display?.setResizing(false);
31
+ },
32
+ });
33
+ return (_jsx("div", { ...props, "data-gesture-owner": "true", "aria-label": `Resize ${trackId}`, style: {
34
+ height: 4,
35
+ cursor: 'row-resize',
36
+ touchAction: 'none',
37
+ background: 'color-mix(in srgb, currentColor 20%, transparent)',
38
+ ...style,
39
+ } }));
40
+ });
41
+ export const TrackToggle = observer(function TrackToggle({ view, trackId, style, children, }) {
42
+ return (_jsxs("label", { style: style, children: [_jsx("input", { type: "checkbox", checked: view.tracks.some(t => t.configuration.trackId === trackId), onChange: () => {
43
+ void view.launchToggleTrack(trackId);
44
+ } }), children] }));
45
+ });
46
+ export const ViewStatus = observer(function ViewStatus({ view, style, }) {
47
+ const { status } = view;
48
+ return status.type === 'ready' ? null : (_jsx("div", { role: status.type === 'error' ? 'alert' : 'status', style: {
49
+ padding: '10px 12px',
50
+ fontSize: '0.85rem',
51
+ opacity: 0.75,
52
+ ...style,
53
+ }, children: status.type === 'error'
54
+ ? `Could not load: ${status.error instanceof Error ? status.error.message : String(status.error)}`
55
+ : status.type === 'loading'
56
+ ? status.message
57
+ : 'Nothing to show yet' }));
58
+ });
59
+ export const TrackStack = observer(function TrackStack({ view, trackIds, renderTrack, style, children, }) {
60
+ const ref = useWidthSetter(view);
61
+ const { containerProps } = usePanZoom(ref, view);
62
+ const ids = trackIds ?? view.tracks.map(t => t.configuration.trackId);
63
+ return (_jsx("div", { ref: ref, ...containerProps, style: {
64
+ position: 'relative',
65
+ overflow: 'hidden',
66
+ cursor: 'grab',
67
+ ...style,
68
+ }, children: view.status.type === 'ready' ? (_jsxs(_Fragment, { children: [children, ids.map(id => renderTrack ? (_jsx(Fragment, { children: renderTrack(id) }, id)) : (_jsx(Track, { view: view, trackId: id }, id)))] })) : (_jsx(ViewStatus, { view: view })) }));
69
+ });
@@ -0,0 +1,8 @@
1
+ import type { LegendSpec } from '@jbrowse/core/ui/legendSpec';
2
+ import type React from 'react';
3
+ export declare const Legend: ({ display, style, }: {
4
+ display: {
5
+ legendSpec: LegendSpec;
6
+ };
7
+ style?: React.CSSProperties;
8
+ }) => React.JSX.Element | null;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { LegendSwatchGlyph } from '@jbrowse/core/ui/LegendSwatchGlyph';
3
+ import { legendSwatches, nonEmptyLegendSections, } from '@jbrowse/core/ui/legendSpec';
4
+ import { observer } from 'mobx-react';
5
+ const SWATCH = 12;
6
+ function gradientCss({ stops }) {
7
+ return `linear-gradient(to right, ${stops
8
+ .map(({ color, opacity = 1, offset }) => `color-mix(in srgb, ${color} ${Math.round(opacity * 100)}%, transparent) ${offset * 100}%`)
9
+ .join(', ')})`;
10
+ }
11
+ function Row({ item }) {
12
+ const { gradient, label, hidden } = item;
13
+ return gradient ? (_jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: 4 }, children: [label, _jsx("span", { children: gradient.minLabel }), _jsx("span", { style: {
14
+ width: 80,
15
+ height: SWATCH,
16
+ backgroundImage: gradientCss(gradient),
17
+ } }), _jsx("span", { children: gradient.maxLabel })] })) : (_jsxs("span", { style: {
18
+ display: 'inline-flex',
19
+ alignItems: 'center',
20
+ gap: 4,
21
+ opacity: hidden ? 0.5 : undefined,
22
+ textDecoration: hidden ? 'line-through' : undefined,
23
+ }, children: [legendSwatches(item).map((swatch, i) => (_jsx("svg", { "aria-hidden": true, width: SWATCH, height: SWATCH, children: _jsx(LegendSwatchGlyph, { swatch: swatch, size: SWATCH }) }, i))), label] }));
24
+ }
25
+ export const Legend = observer(function Legend({ display, style, }) {
26
+ const { legendSpec } = display;
27
+ const sections = nonEmptyLegendSections(legendSpec);
28
+ return sections.length ? (_jsxs("div", { "data-testid": "embed-legend", style: {
29
+ display: 'flex',
30
+ flexWrap: 'wrap',
31
+ gap: '2px 12px',
32
+ fontSize: '0.75rem',
33
+ ...style,
34
+ }, children: [legendSpec.title ? _jsx("strong", { children: legendSpec.title }) : null, sections.map(section => (_jsxs("span", { style: { display: 'contents' }, children: [sections.length > 1 && section.title ? (_jsx("strong", { children: section.title })) : null, section.note ? (_jsx("span", { style: { opacity: 0.7 }, children: section.note })) : null, section.items.map((item, i) => (_jsx(Row, { item: item }, i)))] }, section.id)))] })) : null;
35
+ });
@@ -0,0 +1,17 @@
1
+ import type React from 'react';
2
+ export interface LocationView {
3
+ coarseVisibleLocStrings: string;
4
+ navToLocString: (input: string) => Promise<unknown>;
5
+ }
6
+ export declare function useLocationBox(view: LocationView): {
7
+ value: string;
8
+ pending: boolean;
9
+ error: unknown;
10
+ edit(text: string): void;
11
+ reset(): void;
12
+ go(input?: string): void;
13
+ };
14
+ export declare const LocationBox: ({ view, style, }: {
15
+ view: LocationView;
16
+ style?: React.CSSProperties;
17
+ }) => React.JSX.Element;
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import { observer } from 'mobx-react';
4
+ export function useLocationBox(view) {
5
+ const [draft, setDraft] = useState();
6
+ const [pending, setPending] = useState(false);
7
+ const [error, setError] = useState();
8
+ const requests = useRef(0);
9
+ const value = draft ?? view.coarseVisibleLocStrings;
10
+ return {
11
+ value,
12
+ pending,
13
+ error,
14
+ edit(text) {
15
+ setDraft(text);
16
+ },
17
+ reset() {
18
+ setDraft(undefined);
19
+ setError(undefined);
20
+ },
21
+ go(input = value) {
22
+ const request = ++requests.current;
23
+ const isLatest = () => request === requests.current;
24
+ setDraft(input);
25
+ setError(undefined);
26
+ setPending(true);
27
+ view
28
+ .navToLocString(input)
29
+ .then(() => {
30
+ if (isLatest()) {
31
+ setDraft(undefined);
32
+ }
33
+ })
34
+ .catch((e) => {
35
+ if (isLatest()) {
36
+ setError(e);
37
+ }
38
+ })
39
+ .finally(() => {
40
+ if (isLatest()) {
41
+ setPending(false);
42
+ }
43
+ });
44
+ },
45
+ };
46
+ }
47
+ export const LocationBox = observer(function LocationBox({ view, style, }) {
48
+ const box = useLocationBox(view);
49
+ return (_jsxs("form", { style: {
50
+ display: 'flex',
51
+ flexWrap: 'wrap',
52
+ alignItems: 'center',
53
+ gap: 4,
54
+ ...style,
55
+ }, onSubmit: event => {
56
+ event.preventDefault();
57
+ box.go();
58
+ }, children: [_jsx("input", { "aria-label": "Location", value: box.value, size: 38, style: { font: 'inherit', padding: '2px 4px' }, onChange: event => {
59
+ box.edit(event.target.value);
60
+ }, onKeyDown: event => {
61
+ if (event.key === 'Escape') {
62
+ box.reset();
63
+ }
64
+ } }), _jsx("button", { type: "submit", disabled: box.pending, style: { font: 'inherit' }, children: "Go" }), box.error ? (_jsx("span", { role: "alert", children: box.error instanceof Error ? box.error.message : String(box.error) })) : null] }));
65
+ });
@@ -0,0 +1,11 @@
1
+ import type { DisplayChromeOverlays } from '../chromeOverlays.ts';
2
+ import type { TrackControlComponent } from '../trackControl/types.ts';
3
+ import type { ThemeModeSession } from '@jbrowse/core/ui/PaletteContext';
4
+ import type React from 'react';
5
+ export declare function EmbedProvider({ session, mode, overlays, trackControl, children, }: {
6
+ session: ThemeModeSession;
7
+ mode?: 'light' | 'dark';
8
+ overlays?: Partial<DisplayChromeOverlays>;
9
+ trackControl?: TrackControlComponent;
10
+ children: React.ReactNode;
11
+ }): React.JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { SessionPaletteProvider } from '@jbrowse/core/ui/PaletteContext';
3
+ import DisplayUIProvider from '../DisplayUIProvider.js';
4
+ export function EmbedProvider({ session, mode, overlays, trackControl, children, }) {
5
+ return (_jsx(SessionPaletteProvider, { session: session, mode: mode, children: _jsx(DisplayUIProvider, { overlays: overlays, trackControl: trackControl, children: children }) }));
6
+ }
@@ -0,0 +1,53 @@
1
+ import type React from 'react';
2
+ export interface RegionSeamsView {
3
+ staticBlocksTranslateX: number;
4
+ paddingSpans: readonly {
5
+ key: string;
6
+ x: number;
7
+ width: number;
8
+ kind: 'seam' | 'elided' | 'boundary';
9
+ }[];
10
+ }
11
+ export interface ScalebarView {
12
+ staticBlocks: {
13
+ totalWidthPx: number;
14
+ };
15
+ staticBlocksTranslateX: number;
16
+ scalebarLabels: readonly {
17
+ x: number;
18
+ label: string;
19
+ }[];
20
+ scalebarRefNameLabels: {
21
+ labels: readonly {
22
+ key: string;
23
+ text: string;
24
+ transform: number;
25
+ maxWidth: number;
26
+ paddingLeft: number;
27
+ }[];
28
+ caption?: string;
29
+ };
30
+ }
31
+ export interface HighlightsView {
32
+ highlights: readonly {
33
+ assemblyName?: string;
34
+ refName: string;
35
+ start: number;
36
+ end: number;
37
+ color?: string;
38
+ label?: string;
39
+ }[];
40
+ getHighlightCoords: (highlight: HighlightsView['highlights'][number]) => {
41
+ left: number;
42
+ width: number;
43
+ } | undefined;
44
+ }
45
+ export declare const RegionSeams: ({ view, }: {
46
+ view: RegionSeamsView;
47
+ }) => React.JSX.Element;
48
+ export declare const Highlights: ({ view, }: {
49
+ view: HighlightsView;
50
+ }) => (React.JSX.Element | null)[];
51
+ export declare const Scalebar: ({ view, style, ...props }: {
52
+ view: ScalebarView;
53
+ } & React.ComponentProps<'div'>) => React.JSX.Element;
@@ -0,0 +1,95 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { usePalette } from '@jbrowse/core/ui/PaletteContext';
3
+ import { highlightKey } from '@jbrowse/core/util/highlights';
4
+ import { observer } from 'mobx-react';
5
+ const spanFill = {
6
+ seam: 'color-mix(in srgb, CanvasText 45%, Canvas)',
7
+ boundary: 'color-mix(in srgb, CanvasText 12%, Canvas)',
8
+ elided: 'color-mix(in srgb, CanvasText 30%, Canvas)',
9
+ };
10
+ export const RegionSeams = observer(function RegionSeams({ view, }) {
11
+ return (_jsx("div", { "aria-hidden": true, "data-region-seams": true, style: {
12
+ position: 'absolute',
13
+ top: 0,
14
+ bottom: 0,
15
+ left: 0,
16
+ zIndex: 2,
17
+ pointerEvents: 'none',
18
+ transform: `translateX(${view.staticBlocksTranslateX}px)`,
19
+ }, children: view.paddingSpans.map(({ key, x, width, kind }) => (_jsx("div", { "data-span": kind, style: {
20
+ position: 'absolute',
21
+ top: 0,
22
+ bottom: 0,
23
+ left: x,
24
+ width,
25
+ background: spanFill[kind],
26
+ } }, key))) }));
27
+ });
28
+ export const Highlights = observer(function Highlights({ view, }) {
29
+ const palette = usePalette();
30
+ return view.highlights.map((highlight, i) => {
31
+ const coords = view.getHighlightCoords(highlight);
32
+ return coords ? (_jsx("div", { "data-testid": "highlight-band", style: {
33
+ position: 'absolute',
34
+ top: 0,
35
+ bottom: 0,
36
+ left: 0,
37
+ width: coords.width,
38
+ transform: `translateX(${coords.left}px)`,
39
+ zIndex: 3,
40
+ pointerEvents: 'none',
41
+ background: highlight.color ??
42
+ `color-mix(in srgb, ${palette.highlight.main} 20%, transparent)`,
43
+ }, children: highlight.label ? (_jsx("span", { style: {
44
+ position: 'absolute',
45
+ top: 0,
46
+ left: 3,
47
+ fontSize: '0.7rem',
48
+ whiteSpace: 'nowrap',
49
+ color: palette.text.primary,
50
+ }, children: highlight.label })) : null }, highlightKey(highlight, i))) : null;
51
+ });
52
+ });
53
+ const SCALEBAR_HEIGHT = 20;
54
+ export const Scalebar = observer(function Scalebar({ view, style, ...props }) {
55
+ const palette = usePalette();
56
+ const { scalebarLabels, scalebarRefNameLabels, staticBlocks, staticBlocksTranslateX, } = view;
57
+ const chip = {
58
+ position: 'absolute',
59
+ top: 0,
60
+ whiteSpace: 'nowrap',
61
+ background: palette.background.paper,
62
+ color: palette.text.primary,
63
+ };
64
+ return (_jsxs("div", { "data-gesture-owner": "true", ...props, style: {
65
+ position: 'relative',
66
+ height: SCALEBAR_HEIGHT,
67
+ lineHeight: `${SCALEBAR_HEIGHT}px`,
68
+ fontSize: '0.7rem',
69
+ overflow: 'clip',
70
+ userSelect: 'none',
71
+ borderBottom: `1px solid ${palette.divider}`,
72
+ ...style,
73
+ }, children: [_jsx("div", { style: {
74
+ position: 'absolute',
75
+ top: 0,
76
+ left: 0,
77
+ height: '100%',
78
+ width: staticBlocks.totalWidthPx,
79
+ transform: `translateX(${Math.round(staticBlocksTranslateX)}px)`,
80
+ }, children: scalebarLabels.map(({ x, label }, i) => (_jsx("span", { style: {
81
+ ...chip,
82
+ left: x,
83
+ transform: 'translateX(-50%)',
84
+ padding: '0 3px',
85
+ }, children: label }, i))) }), scalebarRefNameLabels.labels.map(({ key, text, transform, maxWidth, paddingLeft }) => (_jsx("span", { style: {
86
+ ...chip,
87
+ left: 0,
88
+ transform: `translateX(${transform}px)`,
89
+ maxWidth,
90
+ paddingLeft,
91
+ boxSizing: 'border-box',
92
+ fontWeight: 'bold',
93
+ overflow: 'clip',
94
+ }, children: text }, key))), scalebarRefNameLabels.caption === undefined ? null : (_jsx("span", { style: { ...chip, left: 0, fontWeight: 'bold' }, children: scalebarRefNameLabels.caption }))] }));
95
+ });
@@ -0,0 +1 @@
1
+ export declare const FLOATING_LEGEND_TOP_PX = 10;
@@ -0,0 +1,2 @@
1
+ // The key's own inset from the display's top-right corner.
2
+ export const FLOATING_LEGEND_TOP_PX = 10;
package/esm/index.d.ts CHANGED
@@ -7,7 +7,8 @@ export { default as plainTrackControl } from './trackControl/plainTrackControl.t
7
7
  export { useTrackControlMenu } from './trackControl/useTrackControlMenu.tsx';
8
8
  export type { TrackControlMenu } from './trackControl/useTrackControlMenu.tsx';
9
9
  export type { TrackControlComponent, TrackControlIcon, TrackControlOption, TrackControlProps, } from './trackControl/types.ts';
10
- export { default as FloatingLegend, FLOATING_LEGEND_TOP_PX, } from './FloatingLegend.tsx';
10
+ export { default as FloatingLegend } from './FloatingLegend.tsx';
11
+ export { FLOATING_LEGEND_TOP_PX } from './floatingLegendTop.ts';
11
12
  export type { LegendItem, LegendSection } from './FloatingLegend.tsx';
12
13
  export { FloatingSvgOverlay } from './FloatingSvgOverlay.tsx';
13
14
  export { default as Tooltip } from './tooltip/Tooltip.tsx';
@@ -21,16 +22,17 @@ export { TrackOverlaySlot } from './trackOverlay/TrackOverlaySlot.tsx';
21
22
  export { tooLargeBannerText } from './tooLargeBannerText.ts';
22
23
  export { isLiveModel } from './isLiveModel.ts';
23
24
  export { BOTTOM_RIGHT_CONTROLS_ORDER, BottomRightCornerContext, } from './bottomRightCorner.ts';
24
- export type { AxisSide, ValueScale, YAxis } from './valueScale.ts';
25
+ export type { AxisSide, ValueScale, ValueScaleRule, YAxis, } from './valueScale.ts';
25
26
  export { AXIS_GUTTER_WIDTH_PX, axisPlotBox, clampStrokeInsideAxis, scoreToAxisY, } from './yScaleTicks.ts';
26
27
  export type { YScaleTicks } from './yScaleTicks.ts';
27
- export { YSCALEBAR_LABEL_OFFSET } from './yAxisConstants.ts';
28
- export { AXIS_RIGHT_INSET_PX, COMPACT_AXIS_HEIGHT, axisDrawn, axisGutterLeft, } from './axisPlacement.ts';
28
+ export { DEFAULT_RULE_COLOR, SCORE_CAPTION_HEIGHT, YSCALEBAR_LABEL_OFFSET, } from './yAxisConstants.ts';
29
+ export { AXIS_RIGHT_INSET_PX, COMPACT_AXIS_HEIGHT, axisDrawn, axisGutterLeft, axisGutterWidth, } from './axisPlacement.ts';
29
30
  export { default as AxisGutter } from './AxisGutter.tsx';
31
+ export { AxisCaptionOverlay, default as AxisCaption } from './AxisCaption.tsx';
30
32
  export { default as YScaleBar } from './YScaleBar.tsx';
31
33
  export { default as YScaleBarOverlay } from './YScaleBarOverlay.tsx';
32
- export { default as ScoreDomainCaption, SCORE_CAPTION_HEIGHT, } from './ScoreDomainCaption.tsx';
33
- export { formatScore } from './formatScore.ts';
34
+ export { default as ScoreDomainCaption } from './ScoreDomainCaption.tsx';
35
+ export { formatScore } from '@jbrowse/core/util/numericUtils';
34
36
  export { CrossHatchLines, default as CrossHatches } from './CrossHatches.tsx';
35
37
  export { ScoreRuleLines, default as ScoreRules } from './ScoreRules.tsx';
36
38
  export type { ScoreRuleMark } from './ScoreRules.tsx';
package/esm/index.js CHANGED
@@ -22,7 +22,8 @@ export { useTrackControlMenu } from './trackControl/useTrackControlMenu.js';
22
22
  // no UI toolkit, and it is chrome by the definition this package uses — a thing
23
23
  // a display draws that is not its data. `@jbrowse/plugin-linear-genome-view`
24
24
  // re-exports it, so its published name is unchanged.
25
- export { default as FloatingLegend, FLOATING_LEGEND_TOP_PX, } from './FloatingLegend.js';
25
+ export { default as FloatingLegend } from './FloatingLegend.js';
26
+ export { FLOATING_LEGEND_TOP_PX } from './floatingLegendTop.js';
26
27
  // The <svg> counterpart of that legend, for a display whose key is drawn by an
27
28
  // SVG-export path as well as on screen. Beside it for the same reasons — it
28
29
  // reaches no UI toolkit, and portaling chrome above the region masks is what
@@ -56,12 +57,13 @@ export { isLiveModel } from './isLiveModel.js';
56
57
  // with the chrome that happens to mount it.
57
58
  export { BOTTOM_RIGHT_CONTROLS_ORDER, BottomRightCornerContext, } from './bottomRightCorner.js';
58
59
  export { AXIS_GUTTER_WIDTH_PX, axisPlotBox, clampStrokeInsideAxis, scoreToAxisY, } from './yScaleTicks.js';
59
- export { YSCALEBAR_LABEL_OFFSET } from './yAxisConstants.js';
60
- export { AXIS_RIGHT_INSET_PX, COMPACT_AXIS_HEIGHT, axisDrawn, axisGutterLeft, } from './axisPlacement.js';
60
+ export { DEFAULT_RULE_COLOR, SCORE_CAPTION_HEIGHT, YSCALEBAR_LABEL_OFFSET, } from './yAxisConstants.js';
61
+ export { AXIS_RIGHT_INSET_PX, COMPACT_AXIS_HEIGHT, axisDrawn, axisGutterLeft, axisGutterWidth, } from './axisPlacement.js';
61
62
  export { default as AxisGutter } from './AxisGutter.js';
63
+ export { AxisCaptionOverlay, default as AxisCaption } from './AxisCaption.js';
62
64
  export { default as YScaleBar } from './YScaleBar.js';
63
65
  export { default as YScaleBarOverlay } from './YScaleBarOverlay.js';
64
- export { default as ScoreDomainCaption, SCORE_CAPTION_HEIGHT, } from './ScoreDomainCaption.js';
65
- export { formatScore } from './formatScore.js';
66
+ export { default as ScoreDomainCaption } from './ScoreDomainCaption.js';
67
+ export { formatScore } from '@jbrowse/core/util/numericUtils';
66
68
  export { CrossHatchLines, default as CrossHatches } from './CrossHatches.js';
67
69
  export { ScoreRuleLines, default as ScoreRules } from './ScoreRules.js';
@@ -1,7 +1,7 @@
1
1
  import type { DisplayChromeOverlays } from './chromeOverlays.ts';
2
2
  /**
3
3
  * #api
4
- * The five `displayPhase` states drawn with no UI toolkit: no theme provider to
4
+ * The `displayPhase` states drawn with no UI toolkit: no theme provider to
5
5
  * mount, no emotion in the host page, and nothing that reads as a stray Material
6
6
  * widget inside someone else's design system.
7
7
  *
@@ -19,9 +19,9 @@ import Tooltip from './tooltip/Tooltip.js';
19
19
  // host's own cascade drives them. Restyle by wrapping these, or write your own
20
20
  // set against the same interface.
21
21
  //
22
- // The testids are NOT cosmetic. `loading-overlay`, `loading-overlay-cancel`,
23
- // `loading-overlay-retry`, `progress-chip`, `reload_button` and
24
- // `use_canvas2d_button` are a contract across four test systems (jest, browser
22
+ // The testids are NOT cosmetic. `loading-overlay`, `loading-overlay-canceled`,
23
+ // `loading-overlay-cancel`, `loading-overlay-retry`, `progress-chip`,
24
+ // `reload_button` and `use_canvas2d_button` are a contract across four test systems (jest, browser
25
25
  // tests, screenshot harness, jbrowse-img), so a replacement set that wants to
26
26
  // run the existing suites has to keep them.
27
27
  // The scrim and the too-large/error banners all need to sit over the canvas
@@ -149,7 +149,7 @@ const PlainLoading = observer(function PlainLoading({ model, visible, }) {
149
149
  return null;
150
150
  }
151
151
  const { statusMessage, statusProgress, fetchCanceled } = model;
152
- return (_jsx("div", { style: overlayBox, "data-testid": "loading-overlay", children: _jsx("div", { style: { ...chip, display: 'flex', alignItems: 'center' }, role: "status", children: fetchCanceled ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "Loading canceled" }), model.reload ? (_jsx("button", { type: "button", style: button, "data-testid": "loading-overlay-retry", onClick: () => {
152
+ return (_jsx("div", { style: overlayBox, "data-testid": fetchCanceled ? 'loading-overlay-canceled' : 'loading-overlay', children: _jsx("div", { style: { ...chip, display: 'flex', alignItems: 'center' }, role: "status", children: fetchCanceled ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "Loading canceled" }), model.reload ? (_jsx("button", { type: "button", style: button, "data-testid": "loading-overlay-retry", onClick: () => {
153
153
  model.reload?.();
154
154
  }, children: "Retry" })) : null] })) : (_jsxs(_Fragment, { children: [_jsx("span", { children: statusProgress === undefined
155
155
  ? `${statusMessage || 'Loading'}...`
@@ -166,7 +166,7 @@ const PlainBackgroundProgress = observer(function PlainBackgroundProgress({ mode
166
166
  });
167
167
  /**
168
168
  * #api
169
- * The five `displayPhase` states drawn with no UI toolkit: no theme provider to
169
+ * The `displayPhase` states drawn with no UI toolkit: no theme provider to
170
170
  * mount, no emotion in the host page, and nothing that reads as a stray Material
171
171
  * widget inside someone else's design system.
172
172
  *
@@ -25,9 +25,9 @@ interface TriggerProps {
25
25
  * Takes a single element child and clones it rather than wrapping it: the
26
26
  * controls that want a tooltip are absolutely positioned inside a legend or sit
27
27
  * in a flex row, where an extra `<span>` moves them. The child keeps its own
28
- * handlers — these compose on top of them. This is the same trigger pattern
29
- * MUI's own `Tooltip` uses, which is what the rest of JBrowse's chrome is built
30
- * on; the toolkit is what differs here, not the shape.
28
+ * handlers — these compose on top of them. MUI's `Tooltip`, which the rest of
29
+ * JBrowse's chrome is built on, uses the same trigger pattern; only the toolkit
30
+ * differs.
31
31
  *
32
32
  * ```tsx
33
33
  * <Tooltip title="Hide legend">
@@ -24,9 +24,9 @@ function compose(theirs, ours) {
24
24
  * Takes a single element child and clones it rather than wrapping it: the
25
25
  * controls that want a tooltip are absolutely positioned inside a legend or sit
26
26
  * in a flex row, where an extra `<span>` moves them. The child keeps its own
27
- * handlers — these compose on top of them. This is the same trigger pattern
28
- * MUI's own `Tooltip` uses, which is what the rest of JBrowse's chrome is built
29
- * on; the toolkit is what differs here, not the shape.
27
+ * handlers — these compose on top of them. MUI's `Tooltip`, which the rest of
28
+ * JBrowse's chrome is built on, uses the same trigger pattern; only the toolkit
29
+ * differs.
30
30
  *
31
31
  * ```tsx
32
32
  * <Tooltip title="Hide legend">
@@ -2,6 +2,6 @@ import type { TrackControlComponent } from './types.ts';
2
2
  export declare const TrackControlProvider: import("react").Provider<TrackControlComponent | undefined>;
3
3
  /**
4
4
  * The component a provider installed, or `undefined` for "nobody asked". The
5
- * caller supplies the default, which is what keeps this module free of one.
5
+ * caller supplies the default, so this module binds none.
6
6
  */
7
7
  export declare function useTrackControlOverride(): TrackControlComponent | undefined;
@@ -6,9 +6,9 @@ import { createContext, use } from 'react';
6
6
  /**
7
7
  * Runtime override for how ambient track controls are drawn. **Defaults to
8
8
  * undefined, not to a component** — the same choice the overlay context makes,
9
- * for the same reason: a plain default would silently change the look of every
10
- * display that renders outside a provider (unit tests, SVG export,
11
- * breakpoint-split-view's overlayUtils), and that degradation is invisible.
9
+ * for the same reason: a plain default would change the look of every display
10
+ * that renders outside a provider (unit tests, SVG export,
11
+ * breakpoint-split-view's overlayUtils) while the display keeps working.
12
12
  * Undefined means "nobody asked", so JBrowse keeps its own look.
13
13
  *
14
14
  * This is the corner-control half of the bring-your-own-UI story; the status
@@ -16,14 +16,14 @@ import { createContext, use } from 'react';
16
16
  * context. Two contexts because two different things render them — the chrome
17
17
  * renders the status states, the display's own component renders the controls —
18
18
  * and folding them together would put entries in `DisplayChromeOverlays` that
19
- * `DisplayChromeBase` never uses. `DisplayUIProvider` mounts both, which is what
20
- * an embedder actually wants.
19
+ * `DisplayChromeBase` never uses. An embedder usually wants both, and
20
+ * `DisplayUIProvider` mounts both.
21
21
  */
22
22
  const TrackControlContext = createContext(undefined);
23
23
  export const TrackControlProvider = TrackControlContext.Provider;
24
24
  /**
25
25
  * The component a provider installed, or `undefined` for "nobody asked". The
26
- * caller supplies the default, which is what keeps this module free of one.
26
+ * caller supplies the default, so this module binds none.
27
27
  */
28
28
  export function useTrackControlOverride() {
29
29
  return use(TrackControlContext);
@@ -32,8 +32,8 @@
32
32
  * this component: JBrowse's own track container passes 100, which is positioned
33
33
  * above `PaddingBlocks` and below `TrackLabel` at 200, and means nothing to a
34
34
  * host whose masks sit at 2. A default would be a number that is right in one
35
- * layout and silently wrong in every other, and the failure — chrome painted
36
- * under a mask — is invisible until someone looks at the right zoom.
35
+ * layout and wrong in every other, and the failure — chrome painted under a
36
+ * mask — goes unseen until someone looks at the right zoom.
37
37
  *
38
38
  * The node takes no pointer events, so it does not eat the canvas's. Chrome that
39
39
  * wants them takes them back on its own positioned box; it also carries
@@ -35,8 +35,8 @@ import { TrackOverlayContext } from './TrackOverlayContext.js';
35
35
  * this component: JBrowse's own track container passes 100, which is positioned
36
36
  * above `PaddingBlocks` and below `TrackLabel` at 200, and means nothing to a
37
37
  * host whose masks sit at 2. A default would be a number that is right in one
38
- * layout and silently wrong in every other, and the failure — chrome painted
39
- * under a mask — is invisible until someone looks at the right zoom.
38
+ * layout and wrong in every other, and the failure — chrome painted under a
39
+ * mask — goes unseen until someone looks at the right zoom.
40
40
  *
41
41
  * The node takes no pointer events, so it does not eat the canvas's. Chrome that
42
42
  * wants them takes them back on its own positioned box; it also carries