@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.
Files changed (41) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +433 -0
  3. package/esm/DisplayUIProvider.d.ts +70 -0
  4. package/esm/DisplayUIProvider.js +96 -0
  5. package/esm/FloatingLegend.d.ts +16 -0
  6. package/esm/FloatingLegend.js +213 -0
  7. package/esm/PointerLayer.d.ts +26 -0
  8. package/esm/PointerLayer.js +27 -0
  9. package/esm/bottomRightCorner.d.ts +58 -0
  10. package/esm/bottomRightCorner.js +59 -0
  11. package/esm/chromeOverlayContext.d.ts +7 -0
  12. package/esm/chromeOverlayContext.js +44 -0
  13. package/esm/chromeOverlays.d.ts +88 -0
  14. package/esm/chromeOverlays.js +1 -0
  15. package/esm/index.d.ts +22 -0
  16. package/esm/index.js +51 -0
  17. package/esm/isLiveModel.d.ts +21 -0
  18. package/esm/isLiveModel.js +24 -0
  19. package/esm/plainChromeOverlays.d.ts +18 -0
  20. package/esm/plainChromeOverlays.js +189 -0
  21. package/esm/tooLargeBannerText.d.ts +18 -0
  22. package/esm/tooLargeBannerText.js +34 -0
  23. package/esm/tooltip/Tooltip.d.ts +52 -0
  24. package/esm/tooltip/Tooltip.js +67 -0
  25. package/esm/tooltip/useTooltip.d.ts +50 -0
  26. package/esm/tooltip/useTooltip.js +116 -0
  27. package/esm/trackControl/plainTrackControl.d.ts +17 -0
  28. package/esm/trackControl/plainTrackControl.js +133 -0
  29. package/esm/trackControl/trackControlContext.d.ts +7 -0
  30. package/esm/trackControl/trackControlContext.js +30 -0
  31. package/esm/trackControl/types.d.ts +58 -0
  32. package/esm/trackControl/types.js +1 -0
  33. package/esm/trackControl/useTrackControlMenu.d.ts +51 -0
  34. package/esm/trackControl/useTrackControlMenu.js +163 -0
  35. package/esm/trackOverlay/TrackOverlayContext.d.ts +1 -0
  36. package/esm/trackOverlay/TrackOverlayContext.js +18 -0
  37. package/esm/trackOverlay/TrackOverlayPortal.d.ts +18 -0
  38. package/esm/trackOverlay/TrackOverlayPortal.js +66 -0
  39. package/esm/trackOverlay/TrackOverlaySlot.d.ts +54 -0
  40. package/esm/trackOverlay/TrackOverlaySlot.js +60 -0
  41. 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
+ }