@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
|
@@ -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,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;
|
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
|
|
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
|
|
33
|
-
export { formatScore } from '
|
|
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
|
|
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
|
|
65
|
-
export { formatScore } from '
|
|
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
|
|
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-
|
|
23
|
-
// `loading-overlay-retry`, `progress-chip`,
|
|
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":
|
|
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
|
|
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
|
*
|
package/esm/tooltip/Tooltip.d.ts
CHANGED
|
@@ -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.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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">
|
package/esm/tooltip/Tooltip.js
CHANGED
|
@@ -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.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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,
|
|
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
|
|
10
|
-
*
|
|
11
|
-
* breakpoint-split-view's overlayUtils)
|
|
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.
|
|
20
|
-
*
|
|
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,
|
|
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
|
|
36
|
-
*
|
|
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
|
|
39
|
-
*
|
|
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
|