@jbrowse/display-ui 4.3.0
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/LICENSE +201 -0
- package/README.md +433 -0
- package/esm/DisplayUIProvider.d.ts +70 -0
- package/esm/DisplayUIProvider.js +96 -0
- package/esm/FloatingLegend.d.ts +16 -0
- package/esm/FloatingLegend.js +213 -0
- package/esm/PointerLayer.d.ts +26 -0
- package/esm/PointerLayer.js +27 -0
- package/esm/bottomRightCorner.d.ts +58 -0
- package/esm/bottomRightCorner.js +59 -0
- package/esm/chromeOverlayContext.d.ts +7 -0
- package/esm/chromeOverlayContext.js +44 -0
- package/esm/chromeOverlays.d.ts +88 -0
- package/esm/chromeOverlays.js +1 -0
- package/esm/index.d.ts +22 -0
- package/esm/index.js +51 -0
- package/esm/isLiveModel.d.ts +21 -0
- package/esm/isLiveModel.js +24 -0
- package/esm/plainChromeOverlays.d.ts +18 -0
- package/esm/plainChromeOverlays.js +189 -0
- package/esm/tooLargeBannerText.d.ts +18 -0
- package/esm/tooLargeBannerText.js +34 -0
- package/esm/tooltip/Tooltip.d.ts +52 -0
- package/esm/tooltip/Tooltip.js +67 -0
- package/esm/tooltip/useTooltip.d.ts +50 -0
- package/esm/tooltip/useTooltip.js +116 -0
- package/esm/trackControl/plainTrackControl.d.ts +17 -0
- package/esm/trackControl/plainTrackControl.js +133 -0
- package/esm/trackControl/trackControlContext.d.ts +7 -0
- package/esm/trackControl/trackControlContext.js +30 -0
- package/esm/trackControl/types.d.ts +58 -0
- package/esm/trackControl/types.js +1 -0
- package/esm/trackControl/useTrackControlMenu.d.ts +51 -0
- package/esm/trackControl/useTrackControlMenu.js +163 -0
- package/esm/trackOverlay/TrackOverlayContext.d.ts +1 -0
- package/esm/trackOverlay/TrackOverlayContext.js +18 -0
- package/esm/trackOverlay/TrackOverlayPortal.d.ts +18 -0
- package/esm/trackOverlay/TrackOverlayPortal.js +66 -0
- package/esm/trackOverlay/TrackOverlaySlot.d.ts +54 -0
- package/esm/trackOverlay/TrackOverlaySlot.js +60 -0
- package/package.json +48 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { use } from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { TrackOverlayContext } from './TrackOverlayContext.js';
|
|
5
|
+
// Portals floating track chrome (legends, badges, on-canvas overlays) into the
|
|
6
|
+
// TrackContainer's overlay node, which is mounted *above* the inter-region
|
|
7
|
+
// padding masks (elided/boundary/region-separator blocks). A display's own
|
|
8
|
+
// React tree is sealed in a `contain:strict` sandbox those masks paint over, so
|
|
9
|
+
// anything rendered inline there gets buried at whole-genome / multi-region
|
|
10
|
+
// scale. Wrapping it here lifts it above the masks without the display knowing
|
|
11
|
+
// how the LGV lays them out — the single reusable answer to that recurring
|
|
12
|
+
// problem (previously re-solved per-display: the multi-wiggle color key,
|
|
13
|
+
// FloatingLegend).
|
|
14
|
+
//
|
|
15
|
+
// The sandbox is `contain: strict`, and the obvious simplification — drop the
|
|
16
|
+
// containment so chrome can just out-z-index the masks and none of this is
|
|
17
|
+
// needed — is measured and rejected (ADR-058): it costs 2.4-4.8x paint time
|
|
18
|
+
// under DOM load, because the stacking context that blocks the z-index is the
|
|
19
|
+
// same thing that isolates the paint. See `browser-tests/probe-containment.ts`
|
|
20
|
+
// in jbrowse-web and the comment on `trackRenderingContainer`. The portal buys
|
|
21
|
+
// both.
|
|
22
|
+
//
|
|
23
|
+
// The overlay node is `pointer-events:none` (so it doesn't eat canvas
|
|
24
|
+
// events), and that does NOT change here — children that should capture hover
|
|
25
|
+
// (any interactive legend/panel) must set `pointer-events:auto` on their own
|
|
26
|
+
// positioned box, or hovering them falls through to feature tooltips / click
|
|
27
|
+
// actions on the canvas below. It can't be defaulted here: the value must live
|
|
28
|
+
// on the positioned element itself, and full-span SVG overlays deliberately
|
|
29
|
+
// stay `none` except on their sub-content (see FloatingLegend).
|
|
30
|
+
//
|
|
31
|
+
// Taking events back is all a panel has to do: the overlay node carries
|
|
32
|
+
// `data-gesture-owner`, so the LGV's click-drag pan already skips anything in
|
|
33
|
+
// this layer that can be a press target (TrackContainer). Without that, every
|
|
34
|
+
// panel here would separately have to know that dragging its own text pans the
|
|
35
|
+
// view under it — which is how it shipped, on the legend and the Hi-C panel.
|
|
36
|
+
// The `fallbackInline` path renders outside that node, so chrome that is
|
|
37
|
+
// interactive AND meaningful inline still declares its own marker.
|
|
38
|
+
//
|
|
39
|
+
// `TrackOverlayContext` is null outside a TrackContainer (some tests, or a
|
|
40
|
+
// display used standalone). `fallbackInline` (default) then renders the children
|
|
41
|
+
// in place, preserving behavior for chrome that is still meaningful without the
|
|
42
|
+
// escape (e.g. FloatingLegend). Pass `fallbackInline={false}` for chrome that
|
|
43
|
+
// only makes sense above the masks and has its own non-masked path elsewhere
|
|
44
|
+
// (e.g. the multi-wiggle overlay legend, whose SVG-export path draws its own).
|
|
45
|
+
/**
|
|
46
|
+
* #api
|
|
47
|
+
* Lift floating track chrome out of the display's `contain: strict` sandbox and
|
|
48
|
+
* into the host's overlay node, so the LGV's inter-region masks cannot bury it
|
|
49
|
+
* at multi-region scale. `TrackOverlaySlot` is the other end.
|
|
50
|
+
*
|
|
51
|
+
* The overlay node takes no pointer events, so anything of yours the user
|
|
52
|
+
* hovers or clicks sets `pointer-events: auto` on its own positioned box.
|
|
53
|
+
*
|
|
54
|
+
* With no slot above it this renders the children in place (`fallbackInline`,
|
|
55
|
+
* the default), which preserves chrome that is still meaningful unescaped. Pass
|
|
56
|
+
* `fallbackInline={false}` for chrome that only makes sense above the masks and
|
|
57
|
+
* draws itself some other way elsewhere.
|
|
58
|
+
*/
|
|
59
|
+
export const TrackOverlayPortal = observer(function TrackOverlayPortal({ children, fallbackInline = true, }) {
|
|
60
|
+
const overlayEl = use(TrackOverlayContext);
|
|
61
|
+
return overlayEl
|
|
62
|
+
? createPortal(children, overlayEl)
|
|
63
|
+
: fallbackInline
|
|
64
|
+
? children
|
|
65
|
+
: null;
|
|
66
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #api
|
|
3
|
+
* The box a display is mounted in, plus the overlay node its floating chrome
|
|
4
|
+
* escapes into. Pair to `TrackOverlayPortal`, which is the other end.
|
|
5
|
+
*
|
|
6
|
+
* A display's React tree is sealed in a `contain: strict` sandbox — that is what
|
|
7
|
+
* isolates its paint, and dropping it is measured and rejected (ADR-058). A
|
|
8
|
+
* stacking context comes with the isolation, so floating chrome a display draws
|
|
9
|
+
* (a colour key, hi-c's overlay panel, maf's row labels) cannot out-z-index
|
|
10
|
+
* anything painted over the track stack from outside. The escape is a node
|
|
11
|
+
* mounted *beside* the sandbox rather than inside it, published through
|
|
12
|
+
* `TrackOverlayContext`; this component is that node, its context and the paint
|
|
13
|
+
* order between them, in the one place they have to agree.
|
|
14
|
+
*
|
|
15
|
+
* `TrackContainer` uses it, so JBrowse's own layout and an embedder's go through
|
|
16
|
+
* the same code rather than two copies of one rule. **An embedder mounting
|
|
17
|
+
* `RenderingComponent` directly needs it too**, and that is the case it was
|
|
18
|
+
* added for: with no provider the context is null, the portal falls back to
|
|
19
|
+
* rendering inline, and a host that paints region seams over its column buries
|
|
20
|
+
* the chrome under them with nothing to say so.
|
|
21
|
+
*
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <TrackOverlaySlot zIndex={3} style={{ height: display.height }}>
|
|
24
|
+
* <div style={{ position: 'absolute', inset: 0, contain: 'strict' }}>
|
|
25
|
+
* <RenderingComponent model={display} />
|
|
26
|
+
* </div>
|
|
27
|
+
* </TrackOverlaySlot>
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* **`zIndex` is required, and deliberately has no default.** It is the answer to
|
|
31
|
+
* "above what?", and that is a fact about the caller's layout rather than about
|
|
32
|
+
* this component: JBrowse's own track container passes 100, which is positioned
|
|
33
|
+
* above `PaddingBlocks` and below `TrackLabel` at 200, and means nothing to a
|
|
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.
|
|
37
|
+
*
|
|
38
|
+
* The node takes no pointer events, so it does not eat the canvas's. Chrome that
|
|
39
|
+
* wants them takes them back on its own positioned box; it also carries
|
|
40
|
+
* `data-gesture-owner`, so anything that does is already exempt from the LGV's
|
|
41
|
+
* click-drag pan.
|
|
42
|
+
*/
|
|
43
|
+
export declare function TrackOverlaySlot({ children, zIndex, style, overlayStyle, }: {
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
/** Paint order for the overlay node, against whatever the caller draws over
|
|
46
|
+
* the track stack. No default on purpose — see above. */
|
|
47
|
+
zIndex: number;
|
|
48
|
+
/** The box the display is mounted in. Its height, usually. */
|
|
49
|
+
style?: React.CSSProperties;
|
|
50
|
+
/** Placement escape for the overlay node. `TrackContainer` passes a negative
|
|
51
|
+
* `left` to cancel its Paper border, so the node covers the canvas
|
|
52
|
+
* edge-to-edge rather than leaving a sliver. */
|
|
53
|
+
overlayStyle?: React.CSSProperties;
|
|
54
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { TrackOverlayContext } from './TrackOverlayContext.js';
|
|
4
|
+
/**
|
|
5
|
+
* #api
|
|
6
|
+
* The box a display is mounted in, plus the overlay node its floating chrome
|
|
7
|
+
* escapes into. Pair to `TrackOverlayPortal`, which is the other end.
|
|
8
|
+
*
|
|
9
|
+
* A display's React tree is sealed in a `contain: strict` sandbox — that is what
|
|
10
|
+
* isolates its paint, and dropping it is measured and rejected (ADR-058). A
|
|
11
|
+
* stacking context comes with the isolation, so floating chrome a display draws
|
|
12
|
+
* (a colour key, hi-c's overlay panel, maf's row labels) cannot out-z-index
|
|
13
|
+
* anything painted over the track stack from outside. The escape is a node
|
|
14
|
+
* mounted *beside* the sandbox rather than inside it, published through
|
|
15
|
+
* `TrackOverlayContext`; this component is that node, its context and the paint
|
|
16
|
+
* order between them, in the one place they have to agree.
|
|
17
|
+
*
|
|
18
|
+
* `TrackContainer` uses it, so JBrowse's own layout and an embedder's go through
|
|
19
|
+
* the same code rather than two copies of one rule. **An embedder mounting
|
|
20
|
+
* `RenderingComponent` directly needs it too**, and that is the case it was
|
|
21
|
+
* added for: with no provider the context is null, the portal falls back to
|
|
22
|
+
* rendering inline, and a host that paints region seams over its column buries
|
|
23
|
+
* the chrome under them with nothing to say so.
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <TrackOverlaySlot zIndex={3} style={{ height: display.height }}>
|
|
27
|
+
* <div style={{ position: 'absolute', inset: 0, contain: 'strict' }}>
|
|
28
|
+
* <RenderingComponent model={display} />
|
|
29
|
+
* </div>
|
|
30
|
+
* </TrackOverlaySlot>
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* **`zIndex` is required, and deliberately has no default.** It is the answer to
|
|
34
|
+
* "above what?", and that is a fact about the caller's layout rather than about
|
|
35
|
+
* this component: JBrowse's own track container passes 100, which is positioned
|
|
36
|
+
* above `PaddingBlocks` and below `TrackLabel` at 200, and means nothing to a
|
|
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.
|
|
40
|
+
*
|
|
41
|
+
* The node takes no pointer events, so it does not eat the canvas's. Chrome that
|
|
42
|
+
* wants them takes them back on its own positioned box; it also carries
|
|
43
|
+
* `data-gesture-owner`, so anything that does is already exempt from the LGV's
|
|
44
|
+
* click-drag pan.
|
|
45
|
+
*/
|
|
46
|
+
export function TrackOverlaySlot({ children, zIndex, style, overlayStyle, }) {
|
|
47
|
+
// element state rather than a ref, so consumers re-render once the portal
|
|
48
|
+
// target mounts and the context value flips from null to the node
|
|
49
|
+
const [overlayEl, setOverlayEl] = useState(null);
|
|
50
|
+
return (_jsxs("div", { "data-track-overlay-slot": "true", style: { position: 'relative', ...style }, children: [_jsx(TrackOverlayContext, { value: overlayEl, children: children }), _jsx("div", { ref: setOverlayEl, "data-gesture-owner": "true", "data-track-overlay-node": "true", style: {
|
|
51
|
+
position: 'absolute',
|
|
52
|
+
top: 0,
|
|
53
|
+
bottom: 0,
|
|
54
|
+
left: 0,
|
|
55
|
+
width: '100%',
|
|
56
|
+
pointerEvents: 'none',
|
|
57
|
+
zIndex,
|
|
58
|
+
...overlayStyle,
|
|
59
|
+
} })] }));
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jbrowse/display-ui",
|
|
3
|
+
"version": "4.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The UI a display draws that is not data: the swappable chrome contract, its toolkit-free implementations, and the track overlay layer",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"jbrowse",
|
|
8
|
+
"jbrowse2",
|
|
9
|
+
"embedding"
|
|
10
|
+
],
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"homepage": "https://jbrowse.org",
|
|
13
|
+
"bugs": "https://github.com/GMOD/jbrowse-components/issues",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/GMOD/jbrowse-components.git",
|
|
17
|
+
"directory": "packages/display-ui"
|
|
18
|
+
},
|
|
19
|
+
"author": "JBrowse Team",
|
|
20
|
+
"main": "esm/index.js",
|
|
21
|
+
"files": [
|
|
22
|
+
"esm"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@jbrowse/mobx-state-tree": "^6.5.1",
|
|
26
|
+
"mobx-react": "^10.0.2",
|
|
27
|
+
"@jbrowse/core": "^4.3.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=19.0.0",
|
|
31
|
+
"react-dom": ">=19.0.0"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"sideEffects": false,
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build:esm": "node ../../node_modules/typescript7/bin/tsc --build tsconfig.build.esm.json",
|
|
39
|
+
"build": "pnpm build:esm",
|
|
40
|
+
"test": "cd ../..; jest --passWithNoTests packages/display-ui",
|
|
41
|
+
"clean": "rimraf esm *.tsbuildinfo",
|
|
42
|
+
"prebuild": "pnpm clean"
|
|
43
|
+
},
|
|
44
|
+
"types": "esm/index.d.ts",
|
|
45
|
+
"exports": {
|
|
46
|
+
".": "./esm/index.js"
|
|
47
|
+
}
|
|
48
|
+
}
|