@genome-spy/core 0.76.0 → 0.78.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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bundle/browser-KWU9rWZT.js +123 -0
  3. package/dist/bundle/esm-CT3ygiMq.js +1084 -0
  4. package/dist/bundle/{esm-D_euN86T.js → esm-DAnOffpD.js} +2 -2
  5. package/dist/bundle/{esm-BimDEpBb.js → esm-DNtC3H80.js} +2 -2
  6. package/dist/bundle/{esm-CngqBe45.js → esm-DVOHLB1e.js} +2 -2
  7. package/dist/bundle/esm-NIYEaYkc.js +1221 -0
  8. package/dist/bundle/index.es.js +3007 -2787
  9. package/dist/bundle/index.js +110 -99
  10. package/dist/schema.json +825 -112
  11. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  12. package/dist/src/data/sources/lazy/bigWigSource.js +31 -11
  13. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +19 -3
  14. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  15. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +34 -13
  16. package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
  17. package/dist/src/data/transforms/filterScoredLabels.js +3 -2
  18. package/dist/src/embedFactory.d.ts.map +1 -1
  19. package/dist/src/embedFactory.js +2 -0
  20. package/dist/src/genomeSpy/headlessBootstrap.d.ts.map +1 -1
  21. package/dist/src/genomeSpy/headlessBootstrap.js +2 -0
  22. package/dist/src/genomeSpy/interactionController.d.ts +4 -1
  23. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  24. package/dist/src/genomeSpy/interactionController.js +57 -13
  25. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -1
  26. package/dist/src/genomeSpy/viewContextFactory.js +1 -0
  27. package/dist/src/genomeSpyBase.d.ts +6 -0
  28. package/dist/src/genomeSpyBase.d.ts.map +1 -1
  29. package/dist/src/genomeSpyBase.js +16 -1
  30. package/dist/src/index.d.ts +1 -0
  31. package/dist/src/index.d.ts.map +1 -1
  32. package/dist/src/index.js +1 -0
  33. package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +2 -2
  34. package/dist/src/marks/mark.d.ts.map +1 -1
  35. package/dist/src/marks/mark.js +0 -6
  36. package/dist/src/marks/rect.vertex.glsl.js +1 -1
  37. package/dist/src/minimal.d.ts +1 -0
  38. package/dist/src/minimal.d.ts.map +1 -1
  39. package/dist/src/minimal.js +1 -0
  40. package/dist/src/paramRuntime/embedParamApi.d.ts +26 -0
  41. package/dist/src/paramRuntime/embedParamApi.d.ts.map +1 -0
  42. package/dist/src/paramRuntime/embedParamApi.js +133 -0
  43. package/dist/src/scales/domainExpressions.d.ts +21 -0
  44. package/dist/src/scales/domainExpressions.d.ts.map +1 -0
  45. package/dist/src/scales/domainExpressions.js +43 -0
  46. package/dist/src/scales/domainPlanner.d.ts +12 -1
  47. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  48. package/dist/src/scales/domainPlanner.js +55 -36
  49. package/dist/src/scales/scaleInstanceManager.d.ts +1 -0
  50. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  51. package/dist/src/scales/scaleInstanceManager.js +5 -0
  52. package/dist/src/scales/scaleInteractionController.d.ts +8 -4
  53. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  54. package/dist/src/scales/scaleInteractionController.js +55 -7
  55. package/dist/src/scales/scalePropsResolver.d.ts +6 -1
  56. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
  57. package/dist/src/scales/scalePropsResolver.js +35 -10
  58. package/dist/src/scales/scaleResolution.d.ts +19 -3
  59. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  60. package/dist/src/scales/scaleResolution.js +141 -20
  61. package/dist/src/scales/scaleRules.d.ts +10 -0
  62. package/dist/src/scales/scaleRules.d.ts.map +1 -1
  63. package/dist/src/scales/scaleRules.js +38 -1
  64. package/dist/src/scales/viewLevelScaleConfig.d.ts +45 -0
  65. package/dist/src/scales/viewLevelScaleConfig.d.ts.map +1 -0
  66. package/dist/src/scales/viewLevelScaleConfig.js +138 -0
  67. package/dist/src/selection/index.d.ts +8 -0
  68. package/dist/src/selection/index.d.ts.map +1 -0
  69. package/dist/src/selection/index.js +12 -0
  70. package/dist/src/spec/scale.d.ts +19 -6
  71. package/dist/src/spec/view.d.ts +11 -0
  72. package/dist/src/styles/genome-spy.css +12 -1
  73. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  74. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  75. package/dist/src/styles/genome-spy.css.js +12 -1
  76. package/dist/src/types/embedApi.d.ts +54 -0
  77. package/dist/src/types/scaleResolutionApi.d.ts +28 -1
  78. package/dist/src/types/viewContext.d.ts +11 -0
  79. package/dist/src/utils/ui/tooltip.d.ts +4 -0
  80. package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
  81. package/dist/src/utils/ui/tooltip.js +57 -10
  82. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  83. package/dist/src/view/containerMutationHelper.js +11 -3
  84. package/package.json +3 -3
  85. package/dist/bundle/browser-BTgw5ieH.js +0 -126
  86. package/dist/bundle/esm-Bvlm1uVk.js +0 -1015
  87. package/dist/bundle/esm-C49STiCR.js +0 -1248
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @typedef {import("../spec/channel.js").ChannelWithScale} ChannelWithScale
3
+ * @typedef {import("../spec/scale.js").Scale} Scale
4
+ * @typedef {import("../view/view.js").default} View
5
+ * @typedef {import("./scaleResolution.js").default} ScaleResolution
6
+ *
7
+ * @typedef {object} ViewLevelScaleConfigMapping
8
+ * @prop {View} view
9
+ * @prop {ChannelWithScale} channel
10
+ * @prop {Scale} config
11
+ * @prop {ScaleResolution | undefined} resolution
12
+ */
13
+
14
+ /**
15
+ * Maps view-level scale configs to the unique scale resolution visible from
16
+ * each configured subtree. Configs with no matching resolution stay pending.
17
+ *
18
+ * @param {View} root
19
+ * @returns {ViewLevelScaleConfigMapping[]}
20
+ */
21
+ export function mapViewLevelScaleConfigs(root) {
22
+ /** @type {ViewLevelScaleConfigMapping[]} */
23
+ const mappings = [];
24
+
25
+ for (const view of root.getDescendants()) {
26
+ const scales = view.spec.scales;
27
+ if (!scales) {
28
+ continue;
29
+ }
30
+
31
+ for (const [channel, config] of Object.entries(scales)) {
32
+ mappings.push(
33
+ mapViewLevelScaleConfig(
34
+ view,
35
+ /** @type {ChannelWithScale} */ (channel),
36
+ config
37
+ )
38
+ );
39
+ }
40
+ }
41
+
42
+ return mappings;
43
+ }
44
+
45
+ /**
46
+ * Maps view-level scale configs and attaches non-pending configs to their
47
+ * target resolutions.
48
+ *
49
+ * @param {View} root
50
+ * @returns {ViewLevelScaleConfigMapping[]}
51
+ */
52
+ export function attachViewLevelScaleConfigs(root) {
53
+ clearViewLevelScaleConfigs(root);
54
+ const mappings = mapViewLevelScaleConfigs(root);
55
+ for (const mapping of mappings) {
56
+ if (mapping.resolution) {
57
+ mapping.resolution.attachViewLevelScaleConfig(
58
+ mapping.view,
59
+ mapping.config
60
+ );
61
+ }
62
+ }
63
+ return mappings;
64
+ }
65
+
66
+ /**
67
+ * Clears view-level scale configs owned by views in the subtree.
68
+ *
69
+ * @param {View} root
70
+ */
71
+ export function clearViewLevelScaleConfigs(root) {
72
+ const views = new Set(root.getDescendants());
73
+ const resolutions = collectAllScaleResolutions(root);
74
+
75
+ for (const resolution of resolutions) {
76
+ const config = resolution.getViewLevelScaleConfig();
77
+ if (config && views.has(config.view)) {
78
+ resolution.clearViewLevelScaleConfig(config.view);
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @param {View} view
85
+ * @param {ChannelWithScale} channel
86
+ * @param {Scale} config
87
+ * @returns {ViewLevelScaleConfigMapping}
88
+ */
89
+ function mapViewLevelScaleConfig(view, channel, config) {
90
+ const resolutions = collectVisibleScaleResolutions(view, channel);
91
+
92
+ if (resolutions.size > 1) {
93
+ throw new Error(
94
+ `View-level scales.${channel} maps to multiple scale resolutions. ` +
95
+ `Move scales.${channel} closer to the intended subtree or configure scale resolution explicitly.`
96
+ );
97
+ }
98
+
99
+ const resolution = resolutions.values().next().value;
100
+ return {
101
+ view,
102
+ channel,
103
+ config,
104
+ resolution,
105
+ };
106
+ }
107
+
108
+ /**
109
+ * @param {View} view
110
+ * @param {ChannelWithScale} channel
111
+ * @returns {Set<ScaleResolution>}
112
+ */
113
+ function collectVisibleScaleResolutions(view, channel) {
114
+ /** @type {Set<ScaleResolution>} */
115
+ const resolutions = new Set();
116
+ for (const descendant of view.getDescendants()) {
117
+ const resolution = descendant.getScaleResolution(channel);
118
+ if (resolution) {
119
+ resolutions.add(resolution);
120
+ }
121
+ }
122
+ return resolutions;
123
+ }
124
+
125
+ /**
126
+ * @param {View} view
127
+ * @returns {Set<ScaleResolution>}
128
+ */
129
+ function collectAllScaleResolutions(view) {
130
+ /** @type {Set<ScaleResolution>} */
131
+ const resolutions = new Set();
132
+ for (const descendant of view.getDescendants()) {
133
+ for (const resolution of Object.values(descendant.resolutions.scale)) {
134
+ resolutions.add(resolution);
135
+ }
136
+ }
137
+ return resolutions;
138
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Creates a runtime interval selection value for use with parameter APIs.
3
+ *
4
+ * @param {Partial<Record<import("../spec/channel.js").PositionalChannel, [number, number] | null>>} intervals
5
+ * @returns {import("../types/selectionTypes.js").IntervalSelection}
6
+ */
7
+ export function intervalSelection(intervals: Partial<Record<import("../spec/channel.js").PositionalChannel, [number, number] | null>>): import("../types/selectionTypes.js").IntervalSelection;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/selection/index.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,6CAHW,OAAO,CAAC,MAAM,CAAC,OAAO,oBAAoB,EAAE,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GACtF,OAAO,4BAA4B,EAAE,iBAAiB,CAOlE"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Creates a runtime interval selection value for use with parameter APIs.
3
+ *
4
+ * @param {Partial<Record<import("../spec/channel.js").PositionalChannel, [number, number] | null>>} intervals
5
+ * @returns {import("../types/selectionTypes.js").IntervalSelection}
6
+ */
7
+ export function intervalSelection(intervals) {
8
+ return {
9
+ type: "interval",
10
+ intervals: { ...intervals },
11
+ };
12
+ }
@@ -67,8 +67,17 @@ export interface Scale {
67
67
  * For _temporal_ fields, `domain` can be a two-element array minimum and maximum values, in the form of either timestamps or the [DateTime definition objects](https://vega.github.io/vega-lite/docs/types.html#datetime).
68
68
  *
69
69
  * For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input values.
70
+
71
+ * The domain can also be defined by an expression reference that evaluates to the domain array.
72
+ * Array elements may also be expression references.
73
+ *
70
74
  */
71
- domain?: ScalarDomain | ComplexDomain | SelectionDomainRef | ExprRef;
75
+ domain?:
76
+ | ScalarDomain
77
+ | ComplexDomain
78
+ | SelectionDomainRef
79
+ | ExprRef
80
+ | DomainValueArray;
72
81
 
73
82
  /**
74
83
  * Inserts a single mid-point value into a two-element domain. The mid-point value must lie between the domain minimum and maximum values. This property can be useful for setting a midpoint for [diverging color scales](https://vega.github.io/vega-lite/docs/scale.html#piecewise). The domainMid property is only intended for use with scales supporting continuous, piecewise domains.
@@ -97,10 +106,10 @@ export interface Scale {
97
106
  * transition.
98
107
  *
99
108
  * Set this to `false` to apply domain updates immediately. The default is
100
- * `true`, except for domains defined by an `ExprRef`, which default to
109
+ * `true`, except for domains that include `ExprRef`s, which default to
101
110
  * `false` unless overridden.
102
111
  *
103
- * __Default value:__ `true`, except `false` for ExprRef-driven domains.
112
+ * __Default value:__ `true`, except `false` for `ExprRef`-driven domains.
104
113
  */
105
114
  domainTransition?: boolean | Record<string, unknown>;
106
115
 
@@ -109,9 +118,9 @@ export interface Scale {
109
118
  *
110
119
  * - A string indicating a [pre-defined named scale range](https://vega.github.io/vega-lite/docs/scale.html#range-config) (e.g., example, `"symbol"`, or `"diverging"`).
111
120
  *
112
- * - For [continuous scales](https://vega.github.io/vega-lite/docs/scale.html#continuous), two-element array indicating minimum and maximum values, or an array with more than two entries for specifying a [piecewise scale](https://vega.github.io/vega-lite/docs/scale.html#piecewise).
121
+ * - For [continuous scales](https://vega.github.io/vega-lite/docs/scale.html#continuous), two-element array indicating minimum and maximum values, or an array with more than two entries for specifying a [piecewise scale](https://vega.github.io/vega-lite/docs/scale.html#piecewise). Array elements may also be expression references.
113
122
  *
114
- * - For [discrete](https://vega.github.io/vega-lite/docs/scale.html#discrete) and [discretizing](https://vega.github.io/vega-lite/docs/scale.html#discretizing) scales, an array of desired output values.
123
+ * - For [discrete](https://vega.github.io/vega-lite/docs/scale.html#discrete) and [discretizing](https://vega.github.io/vega-lite/docs/scale.html#discretizing) scales, an array of desired output values. Array elements may also be expression references.
115
124
  *
116
125
  * __Notes:__
117
126
  *
@@ -119,7 +128,7 @@ export interface Scale {
119
128
  *
120
129
  * 2) Any directly specified `range` for `x` and `y` channels will be ignored. Range can be customized via the view's corresponding [size](https://vega.github.io/vega-lite/docs/size.html) (`width` and `height`).
121
130
  */
122
- range?: number[] | string[] | string | ExprRef[];
131
+ range?: (number | string | ExprRef)[] | string;
123
132
 
124
133
  // ordinal
125
134
 
@@ -257,6 +266,10 @@ export interface Scale {
257
266
 
258
267
  export type InlineLocusAssembly = GenomeDefinition;
259
268
 
269
+ export type DomainValue = number | string | boolean | ExprRef;
270
+
271
+ export type DomainValueArray = DomainValue[];
272
+
260
273
  export interface SelectionDomainRef {
261
274
  /**
262
275
  * Name of an interval selection parameter that provides the domain.
@@ -2,6 +2,7 @@ import { Data } from "./data.js";
2
2
  import { TransformParams } from "./transform.js";
3
3
  import {
4
4
  Channel,
5
+ ChannelWithScale,
5
6
  Encoding,
6
7
  FacetFieldDef,
7
8
  PrimaryPositionalChannel,
@@ -12,6 +13,7 @@ import { Title } from "./title.js";
12
13
  import { Parameter } from "./parameter.js";
13
14
  import { GenomeSpyConfig } from "./config.js";
14
15
  import { ViewBackgroundProps, ZIndexProps } from "./decoration.js";
16
+ import { Scale } from "./scale.js";
15
17
 
16
18
  export interface SizeDef {
17
19
  /**
@@ -211,6 +213,15 @@ export interface ViewSpecBase extends ResolveSpec {
211
213
  */
212
214
  config?: GenomeSpyConfig;
213
215
 
216
+ /**
217
+ * Configures scale resolutions used by this view subtree.
218
+ *
219
+ * Use this when a composed view shares a scale across child views and the
220
+ * scale settings, such as the visible domain, belong to the composed view
221
+ * rather than an individual encoding.
222
+ */
223
+ scales?: Partial<Record<ChannelWithScale, Scale>>;
224
+
214
225
  /**
215
226
  * Specifies a [data source](https://genomespy.app/docs/grammar/data/).
216
227
  * If omitted, the data source is inherited from the parent view.
@@ -86,7 +86,10 @@
86
86
  }
87
87
 
88
88
  .tooltip {
89
- position: absolute;
89
+ position: fixed;
90
+ inset: auto;
91
+ margin: 0;
92
+ border: 0;
90
93
 
91
94
  max-width: 450px;
92
95
  overflow: hidden;
@@ -177,6 +180,14 @@
177
180
  }
178
181
  }
179
182
 
183
+ .autoscroll-container {
184
+ max-height: min(40em, 50vh);
185
+ overflow-x: hidden;
186
+ overflow-y: auto;
187
+ padding-right: var(--genome-spy-basic-spacing);
188
+ margin-right: calc(-1 * var(--genome-spy-basic-spacing));
189
+ }
190
+
180
191
  .na {
181
192
  color: #aaa;
182
193
  font-style: italic;
@@ -1,3 +1,3 @@
1
1
  export default css;
2
- declare const css: "\n@scope {\n:scope {\n--genome-spy-basic-spacing: 10px;\n--genome-spy-font-family:\nsystem-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif,\n\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n\nfont-family: var(--genome-spy-font-family);\n\nposition: relative;\n\ndisplay: flex;\nflex-direction: column;\n}\n\n.canvas-wrapper {\nposition: relative;\nflex-grow: 1;\noverflow: hidden;\n}\n\ncanvas {\ndisplay: block;\ntouch-action: none;\ntransform: scale(1, 1);\nopacity: 1;\ntransition:\ntransform 0.6s,\nopacity 0.6s;\n\n&:focus,\n&:focus-visible {\noutline: none;\n}\n}\n\n.loading {\n> canvas {\ntransform: scale(0.95, 0.95);\nopacity: 0;\n}\n}\n\n.loading-indicators {\nposition: absolute;\ninset: 0;\n\nuser-select: none;\npointer-events: none;\n\ndiv {\nposition: absolute;\ndisplay: flex;\nalign-items: center;\njustify-content: center;\n\n> div {\nfont-size: 11px;\ntransition: opacity 0.2s;\nbackground: white;\npadding: 2px 5px;\ndisplay: flex;\nborder-radius: 3px;\ngap: 0.5em;\nopacity: 0;\n\n&.loading {\nopacity: 0.5;\n}\n\n&.error {\nopacity: 0.8;\ncolor: firebrick;\n}\n\n> * {\ndisplay: block;\n}\n\nimg {\nwidth: 1.5em;\nheight: 1.5em;\n}\n}\n}\n}\n\n.tooltip {\nposition: absolute;\n\nmax-width: 450px;\noverflow: hidden;\n\n--background-color: #f6f6f6;\nbackground: var(--background-color);\npadding: var(--genome-spy-basic-spacing);\n\n--font-size: 12px;\nfont-size: var(--font-size);\n\nbox-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n\n&:not(.sticky) {\npointer-events: none;\n}\n\ntransition:\noutline-color 0.3s ease-in-out,\nbox-shadow 0.3s ease-in-out;\n\noutline: 0px solid transparent;\n&.sticky {\noutline: 2px solid black;\nbox-shadow: 0px 3px 18px 0px rgba(0, 0, 0, 0.3);\n}\n\nz-index: 100;\n\n> :last-child {\nmargin-bottom: 0;\n}\n\n> .title {\npadding-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nmargin-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nborder-bottom: 1px dashed var(--background-color);\nborder-bottom: 1px dashed\ncolor-mix(in srgb, black 25%, var(--background-color));\n}\n\n.summary {\nfont-size: 12px;\n}\n\ntable {\n&:first-child {\nmargin-top: 0;\n}\n\nborder-collapse: collapse;\n\nth,\ntd {\npadding: 2px 0.4em;\nvertical-align: top;\nfont-size: var(--font-size);\n\n&:first-child {\npadding-left: 0;\n}\n}\n\nth {\ntext-align: left;\nfont-weight: bold;\n}\n}\n\n.color-legend {\ndisplay: inline-block;\nwidth: 0.8em;\nheight: 0.8em;\nmargin-left: 0.4em;\nbox-shadow: 0px 0px 3px 1px white;\n}\n\n.color-legend-unmapped {\nbackground-color: transparent;\nborder: 1px solid black;\nbox-sizing: border-box;\nbox-shadow: none;\n}\n\n.attributes {\n.hovered {\nbackground-color: #e0e0e0;\n}\n}\n\n.na {\ncolor: #aaa;\nfont-style: italic;\nfont-size: 80%;\n}\n}\n\n.gene-track-tooltip {\n.summary {\nfont-size: 90%;\n}\n}\n\n.gs-input-binding {\ndisplay: grid;\ngrid-template-columns: max-content max-content;\ncolumn-gap: 1em;\nrow-gap: 0.3em;\njustify-items: start;\n\n> select,\n> input:not([type=\"checkbox\"]) {\nwidth: 100%;\n}\n\ninput[type=\"range\"] + span {\ndisplay: inline-block;\nmargin-left: 0.3em;\nmin-width: 2.2em;\nfont-variant-numeric: tabular-nums;\n}\n\ninput[type=\"range\"],\ninput[type=\"radio\"] {\nvertical-align: text-bottom;\n}\n\n.radio-group {\ndisplay: flex;\nalign-items: center;\n}\n\n.description {\nmax-width: 26em;\ngrid-column: 1 / -1;\ncolor: #777;\nfont-size: 90%;\nmargin-top: -0.5em;\n}\n}\n\n.gs-input-bindings {\nflex-basis: content;\nfont-size: 14px;\npadding: var(--genome-spy-basic-spacing);\n}\n\n.message-box {\ndisplay: flex;\nalign-items: center;\njustify-content: center;\nposition: absolute;\ntop: 0;\nheight: 100%;\nwidth: 100%;\n\n> div {\nborder: 1px solid red;\npadding: 10px;\nbackground: #fff0f0;\n}\n}\n}\n";
2
+ declare const css: "\n@scope {\n:scope {\n--genome-spy-basic-spacing: 10px;\n--genome-spy-font-family:\nsystem-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif,\n\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n\nfont-family: var(--genome-spy-font-family);\n\nposition: relative;\n\ndisplay: flex;\nflex-direction: column;\n}\n\n.canvas-wrapper {\nposition: relative;\nflex-grow: 1;\noverflow: hidden;\n}\n\ncanvas {\ndisplay: block;\ntouch-action: none;\ntransform: scale(1, 1);\nopacity: 1;\ntransition:\ntransform 0.6s,\nopacity 0.6s;\n\n&:focus,\n&:focus-visible {\noutline: none;\n}\n}\n\n.loading {\n> canvas {\ntransform: scale(0.95, 0.95);\nopacity: 0;\n}\n}\n\n.loading-indicators {\nposition: absolute;\ninset: 0;\n\nuser-select: none;\npointer-events: none;\n\ndiv {\nposition: absolute;\ndisplay: flex;\nalign-items: center;\njustify-content: center;\n\n> div {\nfont-size: 11px;\ntransition: opacity 0.2s;\nbackground: white;\npadding: 2px 5px;\ndisplay: flex;\nborder-radius: 3px;\ngap: 0.5em;\nopacity: 0;\n\n&.loading {\nopacity: 0.5;\n}\n\n&.error {\nopacity: 0.8;\ncolor: firebrick;\n}\n\n> * {\ndisplay: block;\n}\n\nimg {\nwidth: 1.5em;\nheight: 1.5em;\n}\n}\n}\n}\n\n.tooltip {\nposition: fixed;\ninset: auto;\nmargin: 0;\nborder: 0;\n\nmax-width: 450px;\noverflow: hidden;\n\n--background-color: #f6f6f6;\nbackground: var(--background-color);\npadding: var(--genome-spy-basic-spacing);\n\n--font-size: 12px;\nfont-size: var(--font-size);\n\nbox-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n\n&:not(.sticky) {\npointer-events: none;\n}\n\ntransition:\noutline-color 0.3s ease-in-out,\nbox-shadow 0.3s ease-in-out;\n\noutline: 0px solid transparent;\n&.sticky {\noutline: 2px solid black;\nbox-shadow: 0px 3px 18px 0px rgba(0, 0, 0, 0.3);\n}\n\nz-index: 100;\n\n> :last-child {\nmargin-bottom: 0;\n}\n\n> .title {\npadding-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nmargin-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nborder-bottom: 1px dashed var(--background-color);\nborder-bottom: 1px dashed\ncolor-mix(in srgb, black 25%, var(--background-color));\n}\n\n.summary {\nfont-size: 12px;\n}\n\ntable {\n&:first-child {\nmargin-top: 0;\n}\n\nborder-collapse: collapse;\n\nth,\ntd {\npadding: 2px 0.4em;\nvertical-align: top;\nfont-size: var(--font-size);\n\n&:first-child {\npadding-left: 0;\n}\n}\n\nth {\ntext-align: left;\nfont-weight: bold;\n}\n}\n\n.color-legend {\ndisplay: inline-block;\nwidth: 0.8em;\nheight: 0.8em;\nmargin-left: 0.4em;\nbox-shadow: 0px 0px 3px 1px white;\n}\n\n.color-legend-unmapped {\nbackground-color: transparent;\nborder: 1px solid black;\nbox-sizing: border-box;\nbox-shadow: none;\n}\n\n.attributes {\n.hovered {\nbackground-color: #e0e0e0;\n}\n}\n\n.autoscroll-container {\nmax-height: min(40em, 50vh);\noverflow-x: hidden;\noverflow-y: auto;\npadding-right: var(--genome-spy-basic-spacing);\nmargin-right: calc(-1 * var(--genome-spy-basic-spacing));\n}\n\n.na {\ncolor: #aaa;\nfont-style: italic;\nfont-size: 80%;\n}\n}\n\n.gene-track-tooltip {\n.summary {\nfont-size: 90%;\n}\n}\n\n.gs-input-binding {\ndisplay: grid;\ngrid-template-columns: max-content max-content;\ncolumn-gap: 1em;\nrow-gap: 0.3em;\njustify-items: start;\n\n> select,\n> input:not([type=\"checkbox\"]) {\nwidth: 100%;\n}\n\ninput[type=\"range\"] + span {\ndisplay: inline-block;\nmargin-left: 0.3em;\nmin-width: 2.2em;\nfont-variant-numeric: tabular-nums;\n}\n\ninput[type=\"range\"],\ninput[type=\"radio\"] {\nvertical-align: text-bottom;\n}\n\n.radio-group {\ndisplay: flex;\nalign-items: center;\n}\n\n.description {\nmax-width: 26em;\ngrid-column: 1 / -1;\ncolor: #777;\nfont-size: 90%;\nmargin-top: -0.5em;\n}\n}\n\n.gs-input-bindings {\nflex-basis: content;\nfont-size: 14px;\npadding: var(--genome-spy-basic-spacing);\n}\n\n.message-box {\ndisplay: flex;\nalign-items: center;\njustify-content: center;\nposition: absolute;\ntop: 0;\nheight: 100%;\nwidth: 100%;\n\n> div {\nborder: 1px solid red;\npadding: 10px;\nbackground: #fff0f0;\n}\n}\n}\n";
3
3
  //# sourceMappingURL=genome-spy.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,isHA6PE"}
1
+ {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,o7HAwQE"}
@@ -87,7 +87,10 @@ height: 1.5em;
87
87
  }
88
88
 
89
89
  .tooltip {
90
- position: absolute;
90
+ position: fixed;
91
+ inset: auto;
92
+ margin: 0;
93
+ border: 0;
91
94
 
92
95
  max-width: 450px;
93
96
  overflow: hidden;
@@ -178,6 +181,14 @@ background-color: #e0e0e0;
178
181
  }
179
182
  }
180
183
 
184
+ .autoscroll-container {
185
+ max-height: min(40em, 50vh);
186
+ overflow-x: hidden;
187
+ overflow-y: auto;
188
+ padding-right: var(--genome-spy-basic-spacing);
189
+ margin-right: calc(-1 * var(--genome-spy-basic-spacing));
190
+ }
191
+
181
192
  .na {
182
193
  color: #aaa;
183
194
  font-style: italic;
@@ -2,6 +2,8 @@ import { ScaleResolutionApi } from "./scaleResolutionApi.js";
2
2
  import { TooltipHandler } from "../tooltip/tooltipHandler.js";
3
3
  import { RootSpec } from "../spec/root.js";
4
4
  import { GenomeSpyConfig } from "../spec/config.js";
5
+ import { Scalar } from "../spec/channel.js";
6
+ import { IntervalSelection } from "./selectionTypes.js";
5
7
 
6
8
  /**
7
9
  * Embeds GenomeSpy into the DOM
@@ -54,6 +56,47 @@ export interface EmbedOptions {
54
56
  onError?: (error: unknown, container: HTMLElement) => boolean | void;
55
57
  }
56
58
 
59
+ /**
60
+ * Runtime value type covered by the default embed parameter API.
61
+ *
62
+ * The default type covers scalar variable parameters and interval selections.
63
+ * Object and array variable parameters are supported at runtime, but callers
64
+ * should provide their own generic type when accessing them:
65
+ *
66
+ * `const param = api.getParam<MyValue>("myParam")`
67
+ *
68
+ * Current limitations:
69
+ *
70
+ * - Parameters are addressed by name only. Independent same-name parameters
71
+ * throw an ambiguity error.
72
+ * - Computed `expr` parameters are readable but cannot be written.
73
+ * - Point selections are readable as runtime values but are not supported for
74
+ * writes through the initial API because valid values require
75
+ * GenomeSpy-generated datum ids.
76
+ * - Projected selections are not supported.
77
+ */
78
+ export type ParamValue = Scalar | null | undefined | IntervalSelection;
79
+
80
+ /**
81
+ * A handle for reading, writing, and subscribing to an explicit parameter.
82
+ */
83
+ export interface ParamApi<T = ParamValue> {
84
+ /**
85
+ * Returns the current parameter value.
86
+ */
87
+ getValue: () => T;
88
+
89
+ /**
90
+ * Sets the parameter value. Computed `expr` parameters throw when set.
91
+ */
92
+ setValue: (value: T) => void;
93
+
94
+ /**
95
+ * Subscribes to parameter changes. Returns an unsubscribe function.
96
+ */
97
+ subscribe: (listener: (value: T) => void) => () => void;
98
+ }
99
+
57
100
  /**
58
101
  * An API for controlling the embedded GenomeSpy instance.
59
102
  */
@@ -82,6 +125,17 @@ export interface EmbedResult {
82
125
  */
83
126
  getScaleResolutionByName: (name: string) => ScaleResolutionApi;
84
127
 
128
+ /**
129
+ * Returns a handle for reading, writing, and subscribing to a named
130
+ * parameter.
131
+ *
132
+ * Parameters are addressed by name only. If the name resolves to multiple
133
+ * independent parameters, this method throws an ambiguity error. Parameters
134
+ * declared with `push: "outer"` are treated as aliases of the outer
135
+ * parameter they write to.
136
+ */
137
+ getParam: <T = ParamValue>(name: string) => ParamApi<T>;
138
+
85
139
  /**
86
140
  * Waits until lazy data sources have loaded data for the current visible
87
141
  * positional domain.
@@ -10,6 +10,25 @@ export interface ScaleResolutionEvent {
10
10
 
11
11
  export type ScaleResolutionListener = (event: ScaleResolutionEvent) => void;
12
12
 
13
+ export interface ZoomToOptions {
14
+ /**
15
+ * Approximate transition duration. Zero or omitted zooms immediately.
16
+ * Boolean `true` indicates a default duration.
17
+ */
18
+ duration?: boolean | number;
19
+
20
+ /**
21
+ * Render immediately without scheduling an animation frame.
22
+ *
23
+ * This is intended for synchronizing multiple GenomeSpy instances, where
24
+ * the target view should be redrawn during the same animation frame as the
25
+ * source view. Use it only for zero-duration zooms. It is not supported for
26
+ * animated transitions and may do redundant work if several domains are
27
+ * applied before the browser has a chance to paint.
28
+ */
29
+ renderImmediately?: boolean;
30
+ }
31
+
13
32
  /**
14
33
  * A public API for ScaleResolution
15
34
  */
@@ -59,6 +78,14 @@ export interface ScaleResolutionApi {
59
78
 
60
79
  zoomTo(
61
80
  domain: number[] | ComplexDomain,
62
- duration?: boolean | number
81
+ options?: ZoomToOptions
82
+ ): Promise<void>;
83
+
84
+ /**
85
+ * @deprecated Use the options object form: `zoomTo(domain, { duration })`.
86
+ */
87
+ zoomTo(
88
+ domain: number[] | ComplexDomain,
89
+ duration: boolean | number
63
90
  ): Promise<void>;
64
91
  }
@@ -34,6 +34,17 @@ export default interface ViewContext {
34
34
 
35
35
  requestLayoutReflow: () => void;
36
36
 
37
+ /**
38
+ * Renders the current scene immediately without scheduling an animation frame.
39
+ *
40
+ * This is intended for synchronization paths where another GenomeSpy
41
+ * instance is already rendering the current animation frame and this view
42
+ * must repaint after a synchronous state update, such as linked scale
43
+ * domains. Callers should use the animator for ordinary rendering. Calling
44
+ * this repeatedly before the browser paints may do redundant work.
45
+ */
46
+ renderImmediately: () => void;
47
+
37
48
  updateTooltip: <T>(
38
49
  datum: T,
39
50
  converter?: (datum: T) => Promise<TemplateResult>
@@ -16,6 +16,10 @@ export default class Tooltip {
16
16
  set visible(visible: boolean);
17
17
  get visible(): boolean;
18
18
  get enabled(): boolean;
19
+ /**
20
+ * @param {Event} event
21
+ */
22
+ containsEvent(event: Event): boolean;
19
23
  /**
20
24
  * @param {boolean} enabled True if tooltip is enabled (allowed to be shown)
21
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../../src/utils/ui/tooltip.js"],"names":[],"mappings":"AAIA,0CAA2C,qBAAqB,CAAC;AACjE,4CAA6C,uBAAuB,CAAC;AAErE;IAsBI;;OAEG;IACH,uBAFW,WAAW,EAUrB;IAED;;OAEG;IACH,mBAFW,OAAO,EAQjB;IAED,cAVW,OAAO,CAYjB;IAED;;OAEG;IACH,qBAFW,OAAO,EAOjB;IAED,eATW,OAAO,CAWjB;IAED,uBAEC;IAED;;OAEG;IACH,0BAFW,OAAO,QAOjB;IAED,wBAEC;IAED;;OAEG;IACH,4BAFW,UAAU,QAsCpB;IA/BG,8BAA2D;IAiC/D,wBAiBC;IAED;;OAEG;IACH,oBAFW,MAAM,GAAG,OAAO,KAAK,EAAE,cAAc,GAAG,WAAW,QAqB7D;IAED,cAGC;IAED;;;;;;;OAOG;IACH,gBAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAyBlF;IAED,sBAEC;;CACJ"}
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../../src/utils/ui/tooltip.js"],"names":[],"mappings":"AAGA,0CAA2C,qBAAqB,CAAC;AACjE,4CAA6C,uBAAuB,CAAC;AAErE;IAqBI;;OAEG;IACH,uBAFW,WAAW,EASrB;IAED;;OAEG;IACH,mBAFW,OAAO,EAQjB;IAED,cAVW,OAAO,CAYjB;IAED;;OAEG;IACH,qBAFW,OAAO,EAQjB;IAED,eAVW,OAAO,CAYjB;IAED,uBAEC;IAED;;OAEG;IACH,qBAFW,KAAK,WAIf;IAED;;OAEG;IACH,0BAFW,OAAO,QAOjB;IAED,wBAEC;IAED;;OAEG;IACH,4BAFW,UAAU,QAyCpB;IAlCG,8BAGE;IAiCN,wBAiBC;IAED;;OAEG;IACH,oBAFW,MAAM,GAAG,OAAO,KAAK,EAAE,cAAc,GAAG,WAAW,QAwB7D;IAED,cAGC;IAED;;;;;;;OAOG;IACH,gBAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAyBlF;IAED,sBAEC;;CAkBJ"}
@@ -1,4 +1,3 @@
1
- import clientPoint from "../point.js";
2
1
  import { html, render } from "lit";
3
2
  import { peek } from "../arrayUtils.js";
4
3
 
@@ -22,8 +21,7 @@ export default class Tooltip {
22
21
  /** @type {HTMLDivElement} */
23
22
  #element;
24
23
 
25
- /** @type {HTMLElement} */
26
- #container;
24
+ #popoverOpen = false;
27
25
 
28
26
  #enabledStack = [true];
29
27
 
@@ -31,11 +29,10 @@ export default class Tooltip {
31
29
  * @param {HTMLElement} container
32
30
  */
33
31
  constructor(container) {
34
- this.#container = container;
35
-
36
32
  this.#element = document.createElement("div");
37
33
  this.#element.className = "tooltip";
38
- this.#container.appendChild(this.#element);
34
+ this.#element.setAttribute("popover", "manual");
35
+ container.appendChild(this.#element);
39
36
 
40
37
  this.clear();
41
38
  }
@@ -61,6 +58,7 @@ export default class Tooltip {
61
58
  set visible(visible) {
62
59
  if (visible != this.#visible) {
63
60
  this.#element.style.display = visible ? null : "none";
61
+ this.#setPopoverOpen(visible);
64
62
  this.#visible = visible;
65
63
  }
66
64
  }
@@ -73,6 +71,13 @@ export default class Tooltip {
73
71
  return peek(this.#enabledStack) ?? true;
74
72
  }
75
73
 
74
+ /**
75
+ * @param {Event} event
76
+ */
77
+ containsEvent(event) {
78
+ return event.composedPath().includes(this.#element);
79
+ }
80
+
76
81
  /**
77
82
  * @param {boolean} enabled True if tooltip is enabled (allowed to be shown)
78
83
  */
@@ -95,7 +100,10 @@ export default class Tooltip {
95
100
  return;
96
101
  }
97
102
 
98
- this.mouseCoords = clientPoint(this.#container, mouseEvent);
103
+ this.mouseCoords = /** @type {[number, number]} */ ([
104
+ mouseEvent.clientX,
105
+ mouseEvent.clientY,
106
+ ]);
99
107
 
100
108
  const now = performance.now();
101
109
 
@@ -130,12 +138,12 @@ export default class Tooltip {
130
138
 
131
139
  updatePlacement() {
132
140
  /** Space between pointer and tooltip box */
133
- const spacing = 20;
141
+ const spacing = 10;
134
142
 
135
143
  const [mouseX, mouseY] = this.mouseCoords;
136
144
 
137
145
  let x = mouseX + spacing;
138
- if (x > this.#container.clientWidth - this.#element.offsetWidth) {
146
+ if (x > window.innerWidth - spacing - this.#element.offsetWidth) {
139
147
  x = mouseX - spacing - this.#element.offsetWidth;
140
148
  }
141
149
  this.#element.style.left = x + "px";
@@ -143,7 +151,7 @@ export default class Tooltip {
143
151
  this.#element.style.top =
144
152
  Math.min(
145
153
  mouseY + spacing,
146
- this.#container.clientHeight - this.#element.offsetHeight
154
+ window.innerHeight - spacing - this.#element.offsetHeight
147
155
  ) + "px";
148
156
  }
149
157
 
@@ -165,6 +173,9 @@ export default class Tooltip {
165
173
  }
166
174
 
167
175
  render(content, this.#element);
176
+ this.#element
177
+ .querySelectorAll(".autoscroll-container")
178
+ .forEach(scrollHoveredTooltipRow);
168
179
 
169
180
  this.visible = true;
170
181
 
@@ -211,6 +222,42 @@ export default class Tooltip {
211
222
  _isPenalty() {
212
223
  return this.#penaltyUntil && this.#penaltyUntil > performance.now();
213
224
  }
225
+
226
+ /**
227
+ * @param {boolean} open
228
+ */
229
+ #setPopoverOpen(open) {
230
+ if (open == this.#popoverOpen) {
231
+ return;
232
+ }
233
+
234
+ if (open && this.#element.showPopover) {
235
+ this.#element.showPopover();
236
+ } else if (!open && this.#element.hidePopover) {
237
+ this.#element.hidePopover();
238
+ }
239
+
240
+ this.#popoverOpen = open;
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Scrolls the highlighted tooltip row into view after Lit has committed the
246
+ * current render.
247
+ *
248
+ * @param {Element | undefined} element
249
+ */
250
+ function scrollHoveredTooltipRow(element) {
251
+ if (!element) {
252
+ return;
253
+ }
254
+
255
+ queueMicrotask(() => {
256
+ element.querySelector("tr.hovered")?.scrollIntoView({
257
+ block: "nearest",
258
+ inline: "nearest",
259
+ });
260
+ });
214
261
  }
215
262
 
216
263
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"containerMutationHelper.d.ts","sourceRoot":"","sources":["../../../src/view/containerMutationHelper.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH;IACI;;;;;;;;;;OAUG;IAEH;;;;;;;;;;;OAWG;IAEH;;;OAGG;IACH,uBAHW,OAAO,oBAAoB,EAAE,OAAO;uBAZ3B;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;4BAChD,OAAO,yBAAyB,EAAE,iBAAiB;wBACvD,OAAO;OAW3B;IAFG,yFAA0B;IAC1B;uBAjBgB;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;4BAChD,OAAO,yBAAyB,EAAE,iBAAiB;wBACvD,OAAO;MAUF;IAG1B;;;;;;OAMG;IACH,wBAJW,OAAO,iBAAiB,EAAE,QAAQ,GAAG,OAAO,iBAAiB,EAAE,UAAU,UACzE,MAAM,GACJ,OAAO,iEAAM,CAuDzB;IAED;;;;OAIG;IACH,qBAFW,MAAM,iBAehB;CACJ"}
1
+ {"version":3,"file":"containerMutationHelper.d.ts","sourceRoot":"","sources":["../../../src/view/containerMutationHelper.js"],"names":[],"mappings":"AAYA;;;GAGG;AACH;IACI;;;;;;;;;;OAUG;IAEH;;;;;;;;;;;OAWG;IAEH;;;OAGG;IACH,uBAHW,OAAO,oBAAoB,EAAE,OAAO;uBAZ3B;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;4BAChD,OAAO,yBAAyB,EAAE,iBAAiB;wBACvD,OAAO;OAW3B;IAFG,yFAA0B;IAC1B;uBAjBgB;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;4BAChD,OAAO,yBAAyB,EAAE,iBAAiB;wBACvD,OAAO;MAUF;IAG1B;;;;;;OAMG;IACH,wBAJW,OAAO,iBAAiB,EAAE,QAAQ,GAAG,OAAO,iBAAiB,EAAE,UAAU,UACzE,MAAM,GACJ,OAAO,iEAAM,CAyDzB;IAED;;;;OAIG;IACH,qBAFW,MAAM,iBAiBhB;CACJ"}