@genome-spy/core 0.45.0 → 0.46.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 (71) hide show
  1. package/dist/bundle/{index-lmJu1tfP.js → index-BtRKzKhM.js} +6 -6
  2. package/dist/bundle/{index--cKb-dKG.js → index-BwFvhduA.js} +5 -5
  3. package/dist/bundle/{index-1QVesMzU.js → index-C8lYPtq_.js} +1 -1
  4. package/dist/bundle/{index-Pv3tKJ1W.js → index-CkI3Kd2P.js} +3 -3
  5. package/dist/bundle/{index-Y-LdHNIz.js → index-CmBp-spD.js} +1 -1
  6. package/dist/bundle/{index-z4Cs62EO.js → index-Dixm7K89.js} +4 -4
  7. package/dist/bundle/{index-noY1e-G6.js → index-Sk-Wtwdn.js} +5 -5
  8. package/dist/bundle/{index-UyrC0vvF.js → index-Z7JiNsFI.js} +4 -4
  9. package/dist/bundle/{index-LD6yPc3X.js → index-mihmTLo-.js} +1 -1
  10. package/dist/bundle/index.es.js +2698 -2615
  11. package/dist/bundle/index.js +93 -84
  12. package/dist/bundle/{long-Veu0zKh9.js → long-CYrAUkxh.js} +2 -2
  13. package/dist/bundle/{remoteFile-Ur-gRKsH.js → remoteFile-1_eCK3VV.js} +1 -1
  14. package/dist/schema.json +156 -15
  15. package/dist/src/data/collector.d.ts +1 -0
  16. package/dist/src/data/collector.d.ts.map +1 -1
  17. package/dist/src/data/collector.js +19 -3
  18. package/dist/src/data/flow.test.js +4 -0
  19. package/dist/src/data/flowNode.d.ts +10 -8
  20. package/dist/src/data/flowNode.d.ts.map +1 -1
  21. package/dist/src/data/flowNode.js +25 -15
  22. package/dist/src/data/sources/dataSource.d.ts +17 -0
  23. package/dist/src/data/sources/dataSource.d.ts.map +1 -1
  24. package/dist/src/data/sources/dataSource.js +34 -0
  25. package/dist/src/data/sources/inlineSource.js +1 -1
  26. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  27. package/dist/src/data/sources/lazy/bigBedSource.js +6 -0
  28. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  29. package/dist/src/data/sources/lazy/bigWigSource.js +28 -15
  30. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +0 -8
  31. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  32. package/dist/src/data/sources/lazy/singleAxisLazySource.js +1 -13
  33. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +3 -3
  34. package/dist/src/data/sources/namedSource.js +1 -1
  35. package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
  36. package/dist/src/data/sources/sequenceSource.js +2 -1
  37. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  38. package/dist/src/data/sources/urlSource.js +9 -5
  39. package/dist/src/data/transforms/filter.d.ts +2 -2
  40. package/dist/src/data/transforms/filter.d.ts.map +1 -1
  41. package/dist/src/data/transforms/filter.js +3 -6
  42. package/dist/src/data/transforms/filter.test.js +6 -0
  43. package/dist/src/data/transforms/formula.d.ts +2 -2
  44. package/dist/src/data/transforms/formula.d.ts.map +1 -1
  45. package/dist/src/data/transforms/formula.js +3 -3
  46. package/dist/src/data/transforms/formula.test.js +7 -1
  47. package/dist/src/genomeSpy.d.ts +5 -2
  48. package/dist/src/genomeSpy.d.ts.map +1 -1
  49. package/dist/src/genomeSpy.js +24 -8
  50. package/dist/src/marks/mark.d.ts.map +1 -1
  51. package/dist/src/marks/mark.js +11 -0
  52. package/dist/src/spec/view.d.ts +52 -9
  53. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  54. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  55. package/dist/src/styles/genome-spy.css.js +4 -0
  56. package/dist/src/styles/genome-spy.scss +5 -0
  57. package/dist/src/types/viewContext.d.ts +9 -2
  58. package/dist/src/utils/expression.js +1 -1
  59. package/dist/src/view/paramMediator.d.ts +2 -1
  60. package/dist/src/view/paramMediator.d.ts.map +1 -1
  61. package/dist/src/view/paramMediator.js +3 -2
  62. package/dist/src/view/paramMediator.test.js +13 -0
  63. package/dist/src/view/viewFactory.d.ts +4 -1
  64. package/dist/src/view/viewFactory.d.ts.map +1 -1
  65. package/dist/src/view/viewFactory.js +52 -24
  66. package/dist/src/view/viewUtils.d.ts +2 -7
  67. package/dist/src/view/viewUtils.d.ts.map +1 -1
  68. package/dist/src/view/viewUtils.js +21 -30
  69. package/package.json +2 -2
  70. /package/dist/bundle/{__vite-browser-external-ENoMJThg.js → __vite-browser-external-C--ziKoh.js} +0 -0
  71. /package/dist/bundle/{_commonjsHelpers-QtkX90xp.js → _commonjsHelpers-BIiJCwQW.js} +0 -0
@@ -124,6 +124,11 @@ export interface ViewSpecBase extends ResolveSpec {
124
124
  */
125
125
  description?: string | string[];
126
126
 
127
+ /**
128
+ * Optional base URL for constructing request URLs. When set, all views
129
+ * deeper in the hierarchy inherit this base URL, using it for importing
130
+ * loading data and importing specifications.
131
+ */
127
132
  baseUrl?: string;
128
133
 
129
134
  /**
@@ -151,6 +156,12 @@ export interface ViewSpecBase extends ResolveSpec {
151
156
  * **Default:** `false` for children of `layer`, `true` for others.
152
157
  */
153
158
  configurableVisibility?: boolean;
159
+
160
+ /**
161
+ * Templates that can be reused within the view specification by importing
162
+ * them with the template key.
163
+ */
164
+ templates?: Record<string, ViewSpec>;
154
165
  }
155
166
 
156
167
  export interface UnitSpec extends ViewSpecBase, AggregateSamplesSpec {
@@ -165,7 +176,7 @@ export interface AggregateSamplesSpec {
165
176
 
166
177
  export interface LayerSpec extends ViewSpecBase, AggregateSamplesSpec {
167
178
  view?: ViewBackground;
168
- layer: (LayerSpec | UnitSpec)[];
179
+ layer: (LayerSpec | UnitSpec | ImportSpec)[];
169
180
  }
170
181
 
171
182
  export interface FacetSpec extends ViewSpecBase {
@@ -218,39 +229,71 @@ export type ViewSpec =
218
229
  | ConcatSpec
219
230
  | SampleSpec;
220
231
 
221
- export interface ImportParams {
222
- url?: string;
232
+ export interface UrlImport {
233
+ /**
234
+ * Imports a specification from the specified URL.
235
+ */
236
+ url: string;
237
+ }
238
+
239
+ export interface TemplateImport {
240
+ /**
241
+ * Imports a specification from the current view hierarchy, searching
242
+ * first in the current view, then ascending through ancestors.
243
+ */
244
+ template: string;
245
+ }
223
246
 
247
+ export interface ImportSpec {
224
248
  /**
225
- * Name for the imported view. Overrides the name defined in the imported spec.
249
+ * The name given to the imported view. This property overrides the name
250
+ * specified in the imported specification.
226
251
  */
227
252
  name?: string;
228
253
 
229
254
  /**
230
- * Dynamic variables that parameterize a visualization. Parameters defined here
231
- * override the parameters defined in the imported spec.
255
+ * Dynamic variables that parameterize a visualization. Parameters defined
256
+ * here override the parameters defined in the imported specification.
232
257
  */
233
258
  params?: VariableParameter[] | Record<string, any>;
234
- }
235
259
 
236
- export interface ImportSpec {
237
- import: ImportParams;
260
+ /**
261
+ * The method to import a specification.
262
+ */
263
+ import: UrlImport | TemplateImport;
238
264
  }
239
265
 
240
266
  export interface ConcatBase extends ViewSpecBase {
267
+ /**
268
+ * The gap between the views, in pixels.
269
+ */
241
270
  spacing?: number;
242
271
  }
243
272
 
244
273
  export interface VConcatSpec extends ConcatBase {
274
+ /**
275
+ * Specifies views that will be concatenated vertically.
276
+ */
245
277
  vconcat: (ViewSpec | ImportSpec)[];
246
278
  }
247
279
 
248
280
  export interface HConcatSpec extends ConcatBase {
281
+ /**
282
+ * Specifies views that will be concatenated horizontally.
283
+ */
249
284
  hconcat: (ViewSpec | ImportSpec)[];
250
285
  }
251
286
 
252
287
  export interface ConcatSpec extends ConcatBase {
288
+ /**
289
+ * Specifies views that will be concatenated into a grid that wraps when
290
+ * the specified number of columns are used.
291
+ */
253
292
  concat: (ViewSpec | ImportSpec)[];
293
+
294
+ /**
295
+ * The number of columns in the grid.
296
+ */
254
297
  columns: number;
255
298
  }
256
299
 
@@ -1,3 +1,3 @@
1
1
  export default css;
2
- declare const css: "\n.genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n display: flex;\n flex-direction: column;\n}\n.genome-spy .canvas-wrapper {\n position: relative;\n flex-grow: 1;\n overflow: hidden;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy .loading > canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy .loading > .loading-message .message {\n opacity: 1;\n}\n.genome-spy .loading > .loading-message .message .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}\n\n.gs-input-binding {\n display: grid;\n grid-template-columns: max-content max-content;\n column-gap: 1em;\n row-gap: 0.3em;\n justify-items: start;\n}\n.gs-input-binding > select,\n.gs-input-binding > input:not([type=checkbox]) {\n width: 100%;\n}\n.gs-input-binding input[type=range] + span {\n display: inline-block;\n margin-left: 0.3em;\n min-width: 2.2em;\n font-variant-numeric: tabular-nums;\n}\n.gs-input-binding input[type=range],\n.gs-input-binding input[type=radio] {\n vertical-align: text-bottom;\n}\n.gs-input-binding .radio-group {\n display: flex;\n align-items: center;\n}\n.gs-input-binding .description {\n max-width: 26em;\n grid-column: 1/-1;\n color: #777;\n font-size: 90%;\n margin-top: -0.5em;\n}\n\n.gs-input-bindings {\n flex-basis: content;\n font-size: 14px;\n padding: 10px;\n}\n";
2
+ declare const css: "\n.genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n display: flex;\n flex-direction: column;\n}\n.genome-spy .canvas-wrapper {\n position: relative;\n flex-grow: 1;\n overflow: hidden;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy .loading > canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy .loading > .loading-message .message {\n opacity: 1;\n}\n.genome-spy .loading > .loading-message .message .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div.error {\n opacity: 0.8;\n color: firebrick;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}\n\n.gs-input-binding {\n display: grid;\n grid-template-columns: max-content max-content;\n column-gap: 1em;\n row-gap: 0.3em;\n justify-items: start;\n}\n.gs-input-binding > select,\n.gs-input-binding > input:not([type=checkbox]) {\n width: 100%;\n}\n.gs-input-binding input[type=range] + span {\n display: inline-block;\n margin-left: 0.3em;\n min-width: 2.2em;\n font-variant-numeric: tabular-nums;\n}\n.gs-input-binding input[type=range],\n.gs-input-binding input[type=radio] {\n vertical-align: text-bottom;\n}\n.gs-input-binding .radio-group {\n display: flex;\n align-items: center;\n}\n.gs-input-binding .description {\n max-width: 26em;\n grid-column: 1/-1;\n color: #777;\n font-size: 90%;\n margin-top: -0.5em;\n}\n\n.gs-input-bindings {\n flex-basis: content;\n font-size: 14px;\n padding: 10px;\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,6iIA4LE"}
1
+ {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,yoIAgME"}
@@ -67,6 +67,10 @@ const css = `
67
67
  .genome-spy .loading-indicators div > div.loading {
68
68
  opacity: 0.5;
69
69
  }
70
+ .genome-spy .loading-indicators div > div.error {
71
+ opacity: 0.8;
72
+ color: firebrick;
73
+ }
70
74
  .genome-spy .loading-indicators div > div > * {
71
75
  display: block;
72
76
  }
@@ -88,6 +88,11 @@ $font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
88
88
  opacity: 0.5;
89
89
  }
90
90
 
91
+ &.error {
92
+ opacity: 0.8;
93
+ color: firebrick;
94
+ }
95
+
91
96
  > * {
92
97
  display: block;
93
98
  }
@@ -18,6 +18,8 @@ export interface Hover {
18
18
  datum: Datum;
19
19
  }
20
20
 
21
+ export type DataLoadingStatus = "loading" | "complete" | "error";
22
+
21
23
  /**
22
24
  * ViewContext provides essential data and interfaces to View classes.
23
25
  */
@@ -74,9 +76,14 @@ export default interface ViewContext {
74
76
  * The status is shown in the UI somewhere within or near the view.
75
77
  *
76
78
  * @param view The view where the data source is located.
77
- * @param status true if loading, false if not loading.
79
+ * @param status
80
+ * @param detail Details about the error, if any.
78
81
  */
79
- setDataLoadingStatus: (view: View, status: boolean) => void;
82
+ setDataLoadingStatus: (
83
+ view: View,
84
+ status: DataLoadingStatus,
85
+ detail?: string
86
+ ) => void;
80
87
 
81
88
  /**
82
89
  * Returns true if the view is configured to be visible.
@@ -49,7 +49,7 @@ function buildFunctions(codegen) {
49
49
 
50
50
  const cg = codegenExpression({
51
51
  forbidden: [],
52
- allowed: ["datum"],
52
+ allowed: ["datum", "undefined"],
53
53
  globalvar: "globalObject",
54
54
  fieldvar: "datum",
55
55
  functions: buildFunctions,
@@ -52,10 +52,11 @@ export default class ParamMediator {
52
52
  *
53
53
  * @param {string} paramName
54
54
  * @param {T} initialValue
55
+ * @param {boolean} [passive] If true, the setter will not notify listeners when the value changes.
55
56
  * @returns {(value: T) => void}
56
57
  * @template T
57
58
  */
58
- allocateSetter<T>(paramName: string, initialValue: T): (value: T) => void;
59
+ allocateSetter<T>(paramName: string, initialValue: T, passive?: boolean): (value: T) => void;
59
60
  /**
60
61
  * Gets an existing setter for a parameter. Throws if the setter is not found.
61
62
  * @param {string} paramName
@@ -1 +1 @@
1
- {"version":3,"file":"paramMediator.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.js"],"names":[],"mappings":"AAsQA;;;GAGG;AACH,6BAHW,GAAG,+CAKb;AAED;;;;;;;GAOG;AACH,oFASC;AAED;;;;;;;;;;GAUG;AACH,mFANW,aAAa,+CAEW,IAAI,KAwCtC;AA7UD;;;;;GAKG;AACH;IA2BI;;;;;OAKG;IACH,2BALW,MAAM,aAAa,EAU7B;IA7BD;;;OAGG;IACH,0BAHU,IAAI,MAAM,EAAE,IAAI,MAAM,IAAI,CAAC,CAAC,CAGvB;IA2Bf;;;OAGG;IACH,gFAzCqB,GAAG,KAAK,IAAI,CAkEhC;IAED;;;;;;OAMG;IACH,6BALW,MAAM,kCAEU,IAAI,CA8B9B;IAED;;;OAGG;IACH,qBAFW,MAAM,WA9Fc,GAAG,KAAK,IAAI,CAsG1C;IAED;;;OAGG;IACH,oBAFW,MAAM,OAIhB;IAED;;;OAGG;IACH,qBAFW,MAAM,OAKhB;IAED;;OAEG;IACH,0FAIC;IAED;;;;;OAKG;IACH,0CAJW,MAAM,GACJ,aAAa,CASzB;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBA4EhB;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAKhB;;CACJ;;;;;;4BA7P4F,MAAM,IAAI,KAAK,IAAI;gBAAc,MAAM,IAAI;gBAAc,MAAM,MAAM"}
1
+ {"version":3,"file":"paramMediator.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.js"],"names":[],"mappings":"AAuQA;;;GAGG;AACH,6BAHW,GAAG,+CAKb;AAED;;;;;;;GAOG;AACH,oFASC;AAED;;;;;;;;;;GAUG;AACH,mFANW,aAAa,+CAEW,IAAI,KAwCtC;AA9UD;;;;;GAKG;AACH;IA2BI;;;;;OAKG;IACH,2BALW,MAAM,aAAa,EAU7B;IA7BD;;;OAGG;IACH,0BAHU,IAAI,MAAM,EAAE,IAAI,MAAM,IAAI,CAAC,CAAC,CAGvB;IA2Bf;;;OAGG;IACH,gFAzCqB,GAAG,KAAK,IAAI,CAkEhC;IAED;;;;;;;OAOG;IACH,6BANW,MAAM,6BAEN,OAAO,iBACS,IAAI,CA8B9B;IAED;;;OAGG;IACH,qBAFW,MAAM,WA/Fc,GAAG,KAAK,IAAI,CAuG1C;IAED;;;OAGG;IACH,oBAFW,MAAM,OAIhB;IAED;;;OAGG;IACH,qBAFW,MAAM,OAKhB;IAED;;OAEG;IACH,0FAIC;IAED;;;;;OAKG;IACH,0CAJW,MAAM,GACJ,aAAa,CASzB;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBA4EhB;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAKhB;;CACJ;;;;;;4BA9P4F,MAAM,IAAI,KAAK,IAAI;gBAAc,MAAM,IAAI;gBAAc,MAAM,MAAM"}
@@ -82,10 +82,11 @@ export default class ParamMediator {
82
82
  *
83
83
  * @param {string} paramName
84
84
  * @param {T} initialValue
85
+ * @param {boolean} [passive] If true, the setter will not notify listeners when the value changes.
85
86
  * @returns {(value: T) => void}
86
87
  * @template T
87
88
  */
88
- allocateSetter(paramName, initialValue) {
89
+ allocateSetter(paramName, initialValue, passive = false) {
89
90
  if (this.#allocatedSetters.has(paramName)) {
90
91
  throw new Error(
91
92
  "Setter already allocated for parameter: " + paramName
@@ -99,7 +100,7 @@ export default class ParamMediator {
99
100
  this.#paramValues.set(paramName, value);
100
101
 
101
102
  const listeners = this.paramListeners.get(paramName);
102
- if (listeners) {
103
+ if (listeners && !passive) {
103
104
  for (const listener of listeners) {
104
105
  listener();
105
106
  }
@@ -55,6 +55,19 @@ describe("Single-level ParamMediator", () => {
55
55
  expect(calls).toBe(2);
56
56
  });
57
57
 
58
+ test("Passive parameter does not trigger listeners", () => {
59
+ const pm = new ParamMediator();
60
+ const setter = pm.allocateSetter("foo", 42, true);
61
+ const expr = pm.createExpression("foo");
62
+
63
+ let result = expr();
64
+
65
+ expr.addListener(() => (result = expr()));
66
+
67
+ setter(50);
68
+ expect(result).toBe(42);
69
+ });
70
+
58
71
  test("Expression invalidation", () => {
59
72
  const pm = new ParamMediator();
60
73
  const setter = pm.allocateSetter("foo", 42);
@@ -55,7 +55,7 @@ export function isSampleSpec(spec: import("../spec/view.js").ViewSpec): spec is
55
55
  export const VIEW_ROOT_NAME: "viewRoot";
56
56
  /**
57
57
  * @typedef {object} ViewFactoryOptions
58
- * @property {boolean} [allowImport]
58
+ * @property {boolean} [allowImport] allows imports from urls
59
59
  * @property {boolean} [wrapRoot]
60
60
  */
61
61
  /**
@@ -102,6 +102,9 @@ export class ViewFactory {
102
102
  #private;
103
103
  }
104
104
  export type ViewFactoryOptions = {
105
+ /**
106
+ * allows imports from urls
107
+ */
105
108
  allowImport?: boolean;
106
109
  wrapRoot?: boolean;
107
110
  };
@@ -1 +1 @@
1
- {"version":3,"file":"viewFactory.d.ts","sourceRoot":"","sources":["../../../src/view/viewFactory.js"],"names":[],"mappings":"AAoPA;;;;GAIG;AACH,iHAEC;AAED;;;;GAIG;AACH,mHAEC;AAED;;;;GAIG;AACH,mHAOC;AAED;;;;GAIG;AACH,yIAMC;AAED;;;;GAIG;AACH,mCAHW,MAAM,gDAKhB;AAED;;;;GAIG;AACH,uHAEC;AAED;;;;GAIG;AACH,6FAEC;AAED;;;;GAIG;AACH,qHAEC;AAED;;;;GAIG;AACH,2HAOC;AAtUD,wCAAyC;AAEzC;;;;GAIG;AAEH;;GAEG;AACH;IAiBI;;OAEG;IACH,sBAFW,kBAAkB,EAoC5B;IAjCG,2CAA2C;IAC3C,SADW,SAAS,kBAAkB,CAAC,CAKtC;IA8BL;;;OAGG;IACH,qEAlDiC,OAAO,yHAC2B,OAAO,oBAAoB,EAAE,OAAO,eAAe,OAAO,WAAW,EAAE,OAAO,gBAAgB,MAAM,KAAK,IAAI,QAmD/K;IAED;;;;;;OAMG;IACH,wHAJW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,QAmBhB;IAED;;;;OAIG;IACH,iGAQC;IAED;;;;;;;;;;OAUG;IACH,uKALW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,4DACc,IAAI,iBA8DlC;;CACJ;;kBAxLa,OAAO;eACP,OAAO;;iBAfJ,WAAW"}
1
+ {"version":3,"file":"viewFactory.d.ts","sourceRoot":"","sources":["../../../src/view/viewFactory.js"],"names":[],"mappings":"AAgRA;;;;GAIG;AACH,iHAEC;AAED;;;;GAIG;AACH,mHAEC;AAED;;;;GAIG;AACH,mHAOC;AAED;;;;GAIG;AACH,yIAMC;AAED;;;;GAIG;AACH,mCAHW,MAAM,gDAKhB;AAED;;;;GAIG;AACH,uHAEC;AAED;;;;GAIG;AACH,6FAEC;AAED;;;;GAIG;AACH,qHAEC;AAED;;;;GAIG;AACH,2HAOC;AAlWD,wCAAyC;AAEzC;;;;GAIG;AAEH;;GAEG;AACH;IAiBI;;OAEG;IACH,sBAFW,kBAAkB,EAoC5B;IAjCG,2CAA2C;IAC3C,SADW,SAAS,kBAAkB,CAAC,CAKtC;IA8BL;;;OAGG;IACH,qEAlDiC,OAAO,yHAC2B,OAAO,oBAAoB,EAAE,OAAO,eAAe,OAAO,WAAW,EAAE,OAAO,gBAAgB,MAAM,KAAK,IAAI,QAmD/K;IAED;;;;;;OAMG;IACH,wHAJW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,QAmBhB;IAED;;;;OAIG;IACH,iGAQC;IAED;;;;;;;;;;OAUG;IACH,uKALW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,4DACc,IAAI,iBAuElC;;CACJ;;;;;kBAjMa,OAAO;eACP,OAAO;;iBAfJ,WAAW"}
@@ -13,7 +13,7 @@ export const VIEW_ROOT_NAME = "viewRoot";
13
13
 
14
14
  /**
15
15
  * @typedef {object} ViewFactoryOptions
16
- * @property {boolean} [allowImport]
16
+ * @property {boolean} [allowImport] allows imports from urls
17
17
  * @property {boolean} [wrapRoot]
18
18
  */
19
19
 
@@ -148,24 +148,33 @@ export class ViewFactory {
148
148
  let viewSpec;
149
149
 
150
150
  if (isImportSpec(spec)) {
151
- if (this.options.allowImport) {
152
- viewSpec = await loadExternalViewSpec(
153
- spec,
154
- dataParent.getBaseUrl(),
155
- context
156
- );
151
+ /** @type {ViewSpec} */
152
+ let importedSpec;
157
153
 
158
- if (validator) {
159
- validator(viewSpec);
154
+ if ("url" in spec.import) {
155
+ if (this.options.allowImport) {
156
+ importedSpec = await loadExternalViewSpec(
157
+ spec,
158
+ dataParent.getBaseUrl(),
159
+ context
160
+ );
161
+ } else {
162
+ throw new ViewError(
163
+ "Importing views is not allowed!",
164
+ layoutParent
165
+ );
160
166
  }
161
-
162
- applyParamsToImportedSpec(viewSpec, spec.import);
167
+ } else if ("template" in spec.import) {
168
+ importedSpec = findTemplate(spec.import.template, dataParent);
163
169
  } else {
164
- throw new ViewError(
165
- "Importing views is not allowed!",
166
- layoutParent
167
- );
170
+ throw new Error("Invalid import: " + JSON.stringify(spec));
168
171
  }
172
+
173
+ validator?.(importedSpec);
174
+
175
+ applyParamsToImportedSpec(importedSpec, spec);
176
+
177
+ viewSpec = importedSpec;
169
178
  } else {
170
179
  viewSpec = spec;
171
180
  }
@@ -200,19 +209,38 @@ export class ViewFactory {
200
209
  }
201
210
 
202
211
  /**
203
- *
212
+ * @param {string} name
213
+ * @param {View} view Start searching from this view, then search within its parent, etc.
214
+ */
215
+ function findTemplate(name, view) {
216
+ const template = view.spec?.templates?.[name];
217
+ if (template) {
218
+ // Ensure that the template is not altered
219
+ return structuredClone(template);
220
+ }
221
+
222
+ if (view.dataParent) {
223
+ return findTemplate(name, view.dataParent);
224
+ } else {
225
+ throw new Error(
226
+ `Cannot find template "${name}" in current view or its ancestors!`
227
+ );
228
+ }
229
+ }
230
+
231
+ /**
204
232
  * @param {ViewSpec} importedSpec
205
- * @param {import("../spec/view.js").ImportParams} importParams
233
+ * @param {import("../spec/view.js").ImportSpec} importSpec
206
234
  */
207
- function applyParamsToImportedSpec(importedSpec, importParams) {
208
- if (importParams.name != null) {
209
- importedSpec.name = importParams.name;
235
+ function applyParamsToImportedSpec(importedSpec, importSpec) {
236
+ if (importSpec.name != null) {
237
+ importedSpec.name = importSpec.name;
210
238
  }
211
239
 
212
- const params = isArray(importParams.params)
213
- ? importParams.params
214
- : isObject(importParams.params)
215
- ? Object.entries(importParams.params).map(([name, value]) => ({
240
+ const params = isArray(importSpec.params)
241
+ ? importSpec.params
242
+ : isObject(importSpec.params)
243
+ ? Object.entries(importSpec.params).map(([name, value]) => ({
216
244
  name,
217
245
  value,
218
246
  }))
@@ -10,12 +10,6 @@ export function isFacetFieldDef(def: import("../spec/channel.js").ChannelDef | i
10
10
  * @returns {spec is FacetMapping}
11
11
  */
12
12
  export function isFacetMapping(def: import("../spec/channel.js").FacetFieldDef | import("../spec/view.js").FacetMapping): spec is FacetMapping;
13
- /**
14
- *
15
- * @param {object} config
16
- * @returns {config is ImportConfig}
17
- */
18
- export function isImportConfig(config: object): config is ImportConfig;
19
13
  /**
20
14
  * Returns all marks in the order (DFS) they are rendered
21
15
  * @param {View} root
@@ -60,8 +54,9 @@ export function findEncodedFields(view: View): {
60
54
  * @param {import("../spec/view.js").ImportSpec} spec
61
55
  * @param {string} baseUrl
62
56
  * @param {import("../types/viewContext.js").default} viewContext
57
+ * @returns {Promise<import("../spec/view.js").ViewSpec>}
63
58
  */
64
- export function loadExternalViewSpec(spec: import("../spec/view.js").ImportSpec, baseUrl: string, viewContext: import("../types/viewContext.js").default): Promise<any>;
59
+ export function loadExternalViewSpec(spec: import("../spec/view.js").ImportSpec, baseUrl: string, viewContext: import("../types/viewContext.js").default): Promise<import("../spec/view.js").ViewSpec>;
65
60
  /**
66
61
  * @param {function(View, View[]):void} visitor
67
62
  */
@@ -1 +1 @@
1
- {"version":3,"file":"viewUtils.d.ts","sourceRoot":"","sources":["../../../src/view/viewUtils.js"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,qCAHW,OAAO,oBAAoB,EAAE,UAAU,GAAG,OAAO,iBAAiB,EAAE,YAAY,yBAK1F;AAED;;;;GAIG;AACH,oCAHW,OAAO,oBAAoB,EAAE,aAAa,GAAG,OAAO,iBAAiB,EAAE,YAAY,wBAQ7F;AAED;;;;GAIG;AACH,uCAHW,MAAM,0BAKhB;AAED;;;GAGG;AACH,+BAFW,IAAI,wCAMd;AAED;;;;GAIG;AACH,wCAFW,IAAI,UASd;AAED;;GAEG;AACH,kDAFW,IAAI,QAiBd;AAED;;;;;;;GAOG;AACH,4CAFW,IAAI,QAUd;AAED;;;;GAIG;AACH,qCAJW,IAAI,iBACJ,OAAO,qBAAqB,EAAE,OAAO,CAAC,IAAI,CAAC,wDAcrD;AAED;;;GAGG;AACH,wCAFW,IAAI;UAGO,QAAQ;aAAW,OAAO,oBAAoB,EAAE,OAAO;WAAS,OAAO,oBAAoB,EAAE,KAAK;UAAQ,OAAO,oBAAoB,EAAE,IAAI;IAqBhK;AAED;;;;GAIG;AACH,2CAJW,OAAO,iBAAiB,EAAE,UAAU,WACpC,MAAM,eACN,OAAO,yBAAyB,EAAE,OAAO,gBA8BnD;AAED;;GAEG;AACH,gDAFoB,IAAI,QAAE,IAAI,EAAE,KAAE,IAAI,+BAkBrC;AAED;;;;;;GAMG;AACH,4CAJW,IAAI,QACJ,MAAM,GACJ,IAAI,EAAE,CAalB;AAED;;;GAGG;AACH,0CAFW,IAAI,eAqBd;AAOD;;GAEG;AACH,gDAFW,IAAI,mDAId;AAED;;GAEG;AACH,kDAFW,OAAO,wBAAwB,EAAE,cAAc;;;EAczD;AAxBM,uCAFI,MAAM,WAE0D;iBAxP9B,WAAW;qBAFnC,eAAe"}
1
+ {"version":3,"file":"viewUtils.d.ts","sourceRoot":"","sources":["../../../src/view/viewUtils.js"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,qCAHW,OAAO,oBAAoB,EAAE,UAAU,GAAG,OAAO,iBAAiB,EAAE,YAAY,yBAK1F;AAED;;;;GAIG;AACH,oCAHW,OAAO,oBAAoB,EAAE,aAAa,GAAG,OAAO,iBAAiB,EAAE,YAAY,wBAQ7F;AAED;;;GAGG;AACH,+BAFW,IAAI,wCAMd;AAED;;;;GAIG;AACH,wCAFW,IAAI,UASd;AAED;;GAEG;AACH,kDAFW,IAAI,QAiBd;AAED;;;;;;;GAOG;AACH,4CAFW,IAAI,QAUd;AAED;;;;GAIG;AACH,qCAJW,IAAI,iBACJ,OAAO,qBAAqB,EAAE,OAAO,CAAC,IAAI,CAAC,wDAcrD;AAED;;;GAGG;AACH,wCAFW,IAAI;UAGO,QAAQ;aAAW,OAAO,oBAAoB,EAAE,OAAO;WAAS,OAAO,oBAAoB,EAAE,KAAK;UAAQ,OAAO,oBAAoB,EAAE,IAAI;IAqBhK;AAED;;;;;GAKG;AACH,2CALW,OAAO,iBAAiB,EAAE,UAAU,WACpC,MAAM,eACN,OAAO,yBAAyB,EAAE,OAAO,GACvC,QAAQ,OAAO,iBAAiB,EAAE,QAAQ,CAAC,CA6BvD;AAED;;GAEG;AACH,gDAFoB,IAAI,QAAE,IAAI,EAAE,KAAE,IAAI,+BAkBrC;AAED;;;;;;GAMG;AACH,4CAJW,IAAI,QACJ,MAAM,GACJ,IAAI,EAAE,CAalB;AAED;;;GAGG;AACH,0CAFW,IAAI,eAqBd;AAOD;;GAEG;AACH,gDAFW,IAAI,mDAId;AAED;;GAEG;AACH,kDAFW,OAAO,wBAAwB,EAAE,cAAc;;;EAczD;AAxBM,uCAFI,MAAM,WAE0D;iBA/O9B,WAAW;qBAFnC,eAAe"}
@@ -30,15 +30,6 @@ export function isFacetMapping(def) {
30
30
  );
31
31
  }
32
32
 
33
- /**
34
- *
35
- * @param {object} config
36
- * @returns {config is ImportConfig}
37
- */
38
- export function isImportConfig(config) {
39
- return "name" in config || "url" in config;
40
- }
41
-
42
33
  /**
43
34
  * Returns all marks in the order (DFS) they are rendered
44
35
  * @param {View} root
@@ -151,34 +142,34 @@ export function findEncodedFields(view) {
151
142
  * @param {import("../spec/view.js").ImportSpec} spec
152
143
  * @param {string} baseUrl
153
144
  * @param {import("../types/viewContext.js").default} viewContext
145
+ * @returns {Promise<import("../spec/view.js").ViewSpec>}
154
146
  */
155
147
  export async function loadExternalViewSpec(spec, baseUrl, viewContext) {
156
- if (!spec.import.url) {
157
- throw new Error(
158
- "Cannot import, not an import spec: " + JSON.stringify(spec)
159
- );
160
- }
148
+ const importParam = spec.import;
149
+ if ("url" in importParam) {
150
+ const loader = vegaLoader({ baseURL: baseUrl });
151
+ const url = importParam.url;
161
152
 
162
- const loader = vegaLoader({ baseURL: baseUrl });
163
- const url = spec.import.url;
153
+ const importedSpec = JSON.parse(
154
+ await loader.load(url).catch((/** @type {Error} */ e) => {
155
+ throw new Error(
156
+ `Could not load imported view spec: ${url} \nReason: ${e.message}`
157
+ );
158
+ })
159
+ );
164
160
 
165
- const importedSpec = JSON.parse(
166
- await loader.load(url).catch((/** @type {Error} */ e) => {
161
+ if (viewContext.isViewSpec(importedSpec)) {
162
+ importedSpec.baseUrl = url.match(/^[^?#]*\//)?.[0];
163
+ return importedSpec;
164
+ } else {
167
165
  throw new Error(
168
- `Could not load imported view spec: ${url} \nReason: ${e.message}`
166
+ `The imported spec "${url}" is not a view spec: ${JSON.stringify(
167
+ spec
168
+ )}`
169
169
  );
170
- })
171
- );
172
-
173
- if (viewContext.isViewSpec(importedSpec)) {
174
- importedSpec.baseUrl = url.match(/^[^?#]*\//)?.[0];
175
- return importedSpec;
170
+ }
176
171
  } else {
177
- throw new Error(
178
- `The imported spec "${url}" is not a view spec: ${JSON.stringify(
179
- spec
180
- )}`
181
- );
172
+ throw new Error("Not an url import: " + JSON.stringify(importParam));
182
173
  }
183
174
  }
184
175
 
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "contributors": [],
9
9
  "license": "MIT",
10
- "version": "0.45.0",
10
+ "version": "0.46.0",
11
11
  "jsdelivr": "dist/bundle/index.js",
12
12
  "unpkg": "dist/bundle/index.js",
13
13
  "browser": "dist/bundle/index.js",
@@ -64,5 +64,5 @@
64
64
  "vega-scale": "^7.3.1",
65
65
  "vega-util": "^1.17.2"
66
66
  },
67
- "gitHead": "7b6de39e856c85c755b2134bf451780c8b7922bb"
67
+ "gitHead": "cb5df0a9b0ea35b7325e148d34d0184a6384da85"
68
68
  }