@genome-spy/core 0.77.0 → 0.79.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/dist/bundle/{browser-KWU9rWZT.js → browser-CETrb2cm.js} +53 -33
- package/dist/bundle/esm-BdLYkz-m.js +248 -0
- package/dist/bundle/esm-BwiDsqSb.js +1367 -0
- package/dist/bundle/esm-CDFd1cjk.js +441 -0
- package/dist/bundle/{esm-CRMf_I9V.js → esm-CTUHLDbv.js} +30 -30
- package/dist/bundle/esm-Cx-EbkOj.js +1221 -0
- package/dist/bundle/esm-DlYGqi79.js +128 -0
- package/dist/bundle/{esm-BygJiwh0.js → esm-k9p3oHkt.js} +133 -158
- package/dist/bundle/{esm-CT3ygiMq.js → esm-zAZJQO6D.js} +226 -212
- package/dist/bundle/index.es.js +14102 -10810
- package/dist/bundle/index.js +109 -95
- package/dist/bundle/{parquetRead-DG_-F5j5.js → parquetRead-Cad1SOVV.js} +473 -399
- package/dist/schema.json +23788 -11049
- package/dist/src/config/axisConfig.d.ts +2 -2
- package/dist/src/config/axisConfig.d.ts.map +1 -1
- package/dist/src/config/axisConfig.js +28 -44
- package/dist/src/config/configLayers.d.ts +45 -0
- package/dist/src/config/configLayers.d.ts.map +1 -0
- package/dist/src/config/configLayers.js +110 -0
- package/dist/src/config/defaultConfig.d.ts.map +1 -1
- package/dist/src/config/defaultConfig.js +8 -1
- package/dist/src/config/defaults/legendDefaults.d.ts +14 -0
- package/dist/src/config/defaults/legendDefaults.d.ts.map +1 -0
- package/dist/src/config/defaults/legendDefaults.js +46 -0
- package/dist/src/config/defaults/titleDefaults.d.ts.map +1 -1
- package/dist/src/config/defaults/titleDefaults.js +26 -18
- package/dist/src/config/legendConfig.d.ts +11 -0
- package/dist/src/config/legendConfig.d.ts.map +1 -0
- package/dist/src/config/legendConfig.js +63 -0
- package/dist/src/config/styleUtils.d.ts +8 -2
- package/dist/src/config/styleUtils.d.ts.map +1 -1
- package/dist/src/config/styleUtils.js +25 -1
- package/dist/src/config/themes.d.ts.map +1 -1
- package/dist/src/config/themes.js +21 -2
- package/dist/src/config/titleConfig.d.ts.map +1 -1
- package/dist/src/config/titleConfig.js +2 -18
- package/dist/src/data/collector.d.ts.map +1 -1
- package/dist/src/data/collector.js +40 -18
- package/dist/src/data/flowInit.d.ts +6 -0
- package/dist/src/data/flowInit.d.ts.map +1 -1
- package/dist/src/data/flowInit.js +1 -1
- package/dist/src/data/flowNode.d.ts +32 -0
- package/dist/src/data/flowNode.d.ts.map +1 -1
- package/dist/src/data/flowNode.js +59 -0
- package/dist/src/data/sources/lazy/bamSource.d.ts +0 -1
- package/dist/src/data/sources/lazy/bamSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bamSource.js +39 -30
- package/dist/src/data/sources/lazy/bigBedSource.d.ts +0 -10
- package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigBedSource.js +127 -62
- package/dist/src/data/sources/lazy/bigWigSource.d.ts +2 -2
- package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigWigSource.js +234 -81
- package/dist/src/data/sources/lazy/gff3Source.d.ts +7 -3
- package/dist/src/data/sources/lazy/gff3Source.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/gff3Source.js +7 -8
- package/dist/src/data/sources/lazy/indexedFastaSource.d.ts +1 -1
- package/dist/src/data/sources/lazy/indexedFastaSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/indexedFastaSource.js +28 -19
- package/dist/src/data/sources/lazy/legendEntriesSource.d.ts +24 -0
- package/dist/src/data/sources/lazy/legendEntriesSource.d.ts.map +1 -0
- package/dist/src/data/sources/lazy/legendEntriesSource.js +217 -0
- package/dist/src/data/sources/lazy/legendGradientSource.d.ts +30 -0
- package/dist/src/data/sources/lazy/legendGradientSource.d.ts.map +1 -0
- package/dist/src/data/sources/lazy/legendGradientSource.js +388 -0
- package/dist/src/data/sources/lazy/mockLazySource.d.ts +4 -1
- package/dist/src/data/sources/lazy/mockLazySource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/mockLazySource.js +49 -4
- package/dist/src/data/sources/lazy/registerCoreLazySources.js +2 -0
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +3 -4
- package/dist/src/data/sources/lazy/tabixSource.d.ts +9 -4
- package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/tabixSource.js +201 -70
- package/dist/src/data/sources/lazy/tabixTsvSource.d.ts +2 -3
- package/dist/src/data/sources/lazy/tabixTsvSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/tabixTsvSource.js +14 -12
- package/dist/src/data/sources/lazy/vcfSource.d.ts +7 -3
- package/dist/src/data/sources/lazy/vcfSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/vcfSource.js +7 -8
- package/dist/src/data/sources/urlDescriptor.d.ts +165 -0
- package/dist/src/data/sources/urlDescriptor.d.ts.map +1 -0
- package/dist/src/data/sources/urlDescriptor.js +473 -0
- package/dist/src/data/sources/urlDescriptorController.d.ts +25 -0
- package/dist/src/data/sources/urlDescriptorController.d.ts.map +1 -0
- package/dist/src/data/sources/urlDescriptorController.js +72 -0
- package/dist/src/data/sources/urlDescriptorState.d.ts +47 -0
- package/dist/src/data/sources/urlDescriptorState.d.ts.map +1 -0
- package/dist/src/data/sources/urlDescriptorState.js +129 -0
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +101 -61
- package/dist/src/data/transforms/packLegendLabels.d.ts +21 -0
- package/dist/src/data/transforms/packLegendLabels.d.ts.map +1 -0
- package/dist/src/data/transforms/packLegendLabels.js +189 -0
- package/dist/src/data/transforms/transformFactory.d.ts.map +1 -1
- package/dist/src/data/transforms/transformFactory.js +4 -0
- package/dist/src/data/transforms/truncateText.d.ts +27 -0
- package/dist/src/data/transforms/truncateText.d.ts.map +1 -0
- package/dist/src/data/transforms/truncateText.js +94 -0
- package/dist/src/debug/dataflowDebugSnapshot.d.ts +58 -0
- package/dist/src/debug/dataflowDebugSnapshot.d.ts.map +1 -0
- package/dist/src/debug/dataflowDebugSnapshot.js +159 -0
- package/dist/src/debug/markDebugSnapshot.d.ts +54 -0
- package/dist/src/debug/markDebugSnapshot.d.ts.map +1 -0
- package/dist/src/debug/markDebugSnapshot.js +100 -0
- package/dist/src/debug/paramDebugSnapshot.d.ts +53 -0
- package/dist/src/debug/paramDebugSnapshot.d.ts.map +1 -0
- package/dist/src/debug/paramDebugSnapshot.js +86 -0
- package/dist/src/debug/resolutionDebugSnapshot.d.ts +155 -0
- package/dist/src/debug/resolutionDebugSnapshot.d.ts.map +1 -0
- package/dist/src/debug/resolutionDebugSnapshot.js +291 -0
- package/dist/src/debug/valuePreview.d.ts +9 -0
- package/dist/src/debug/valuePreview.d.ts.map +1 -0
- package/dist/src/debug/valuePreview.js +57 -0
- package/dist/src/debug/viewDebugSnapshot.d.ts +131 -0
- package/dist/src/debug/viewDebugSnapshot.d.ts.map +1 -0
- package/dist/src/debug/viewDebugSnapshot.js +390 -0
- package/dist/src/embedFactory.d.ts.map +1 -1
- package/dist/src/embedFactory.js +6 -1
- package/dist/src/encoder/encoder.d.ts +2 -2
- package/dist/src/encoder/encoder.d.ts.map +1 -1
- package/dist/src/encoder/encoder.js +5 -4
- package/dist/src/fonts/bmFontManager.d.ts +1 -1
- package/dist/src/fonts/bmFontManager.d.ts.map +1 -1
- package/dist/src/fonts/bmFontManager.js +45 -10
- package/dist/src/fonts/textMetrics.d.ts +69 -0
- package/dist/src/fonts/textMetrics.d.ts.map +1 -0
- package/dist/src/fonts/textMetrics.js +73 -0
- package/dist/src/genomeSpy/headlessBootstrap.d.ts.map +1 -1
- package/dist/src/genomeSpy/headlessBootstrap.js +8 -0
- package/dist/src/genomeSpy/interactionController.d.ts +4 -1
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
- package/dist/src/genomeSpy/interactionController.js +57 -13
- package/dist/src/genomeSpy/renderCoordinator.d.ts.map +1 -1
- package/dist/src/genomeSpy/renderCoordinator.js +25 -3
- package/dist/src/genomeSpy/viewDataInit.d.ts +14 -0
- package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -1
- package/dist/src/genomeSpy/viewDataInit.js +45 -8
- package/dist/src/genomeSpyBase.d.ts +6 -0
- package/dist/src/genomeSpyBase.d.ts.map +1 -1
- package/dist/src/genomeSpyBase.js +25 -4
- package/dist/src/gl/glslScaleGenerator.d.ts +17 -0
- package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
- package/dist/src/gl/glslScaleGenerator.js +39 -2
- package/dist/src/gl/includes/common.glsl.js +1 -1
- package/dist/src/gl/vertexRangeIndex.d.ts.map +1 -1
- package/dist/src/gl/vertexRangeIndex.js +4 -2
- package/dist/src/gl/webGLHelper.d.ts +1 -1
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +13 -8
- package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +140 -3
- package/dist/src/marks/mark.d.ts +47 -4
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +158 -54
- package/dist/src/marks/point.d.ts.map +1 -1
- package/dist/src/marks/point.js +4 -0
- package/dist/src/marks/point.vertex.glsl.js +1 -1
- package/dist/src/marks/text.d.ts +1 -1
- package/dist/src/marks/text.d.ts.map +1 -1
- package/dist/src/marks/text.js +2 -7
- package/dist/src/marks/text.vertex.glsl.js +1 -1
- package/dist/src/paramRuntime/paramUtils.d.ts +43 -9
- package/dist/src/paramRuntime/paramUtils.d.ts.map +1 -1
- package/dist/src/paramRuntime/paramUtils.js +61 -1
- package/dist/src/paramRuntime/viewParamRuntime.d.ts +32 -0
- package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -1
- package/dist/src/paramRuntime/viewParamRuntime.js +63 -0
- package/dist/src/scales/axisResolution.d.ts +35 -0
- package/dist/src/scales/axisResolution.d.ts.map +1 -1
- package/dist/src/scales/axisResolution.js +115 -7
- package/dist/src/scales/domainExpressions.d.ts +21 -0
- package/dist/src/scales/domainExpressions.d.ts.map +1 -0
- package/dist/src/scales/domainExpressions.js +43 -0
- package/dist/src/scales/domainPlanner.d.ts +12 -1
- package/dist/src/scales/domainPlanner.d.ts.map +1 -1
- package/dist/src/scales/domainPlanner.js +55 -36
- package/dist/src/scales/legendResolution.d.ts +83 -0
- package/dist/src/scales/legendResolution.d.ts.map +1 -0
- package/dist/src/scales/legendResolution.js +461 -0
- package/dist/src/scales/scaleInstanceManager.d.ts +1 -0
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
- package/dist/src/scales/scaleInstanceManager.js +5 -0
- package/dist/src/scales/scalePropsResolver.d.ts +6 -1
- package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
- package/dist/src/scales/scalePropsResolver.js +35 -10
- package/dist/src/scales/scaleResolution.d.ts +52 -0
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +195 -16
- package/dist/src/scales/scaleRules.d.ts +10 -0
- package/dist/src/scales/scaleRules.d.ts.map +1 -1
- package/dist/src/scales/scaleRules.js +38 -1
- package/dist/src/scales/viewLevelGuideConfig.d.ts +53 -0
- package/dist/src/scales/viewLevelGuideConfig.d.ts.map +1 -0
- package/dist/src/scales/viewLevelGuideConfig.js +224 -0
- package/dist/src/scales/viewLevelScaleConfig.d.ts +45 -0
- package/dist/src/scales/viewLevelScaleConfig.d.ts.map +1 -0
- package/dist/src/scales/viewLevelScaleConfig.js +149 -0
- package/dist/src/spec/axis.d.ts +109 -3
- package/dist/src/spec/channel.d.ts +23 -4
- package/dist/src/spec/config.d.ts +59 -4
- package/dist/src/spec/data.d.ts +177 -17
- package/dist/src/spec/legend.d.ts +246 -0
- package/dist/src/spec/mark.d.ts +16 -4
- package/dist/src/spec/scale.d.ts +19 -6
- package/dist/src/spec/title.d.ts +58 -1
- package/dist/src/spec/transform.d.ts +149 -0
- package/dist/src/spec/view.d.ts +50 -6
- package/dist/src/styles/genome-spy.css +4 -1
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +4 -1
- package/dist/src/types/embedApi.d.ts +262 -6
- package/dist/src/types/rendering.d.ts +19 -3
- package/dist/src/types/viewContext.d.ts +18 -2
- package/dist/src/utils/arrayUtils.d.ts +11 -0
- package/dist/src/utils/arrayUtils.d.ts.map +1 -1
- package/dist/src/utils/arrayUtils.js +23 -0
- package/dist/src/utils/suspension.d.ts +17 -0
- package/dist/src/utils/suspension.d.ts.map +1 -0
- package/dist/src/utils/suspension.js +41 -0
- package/dist/src/utils/ui/tooltip.d.ts +4 -0
- package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
- package/dist/src/utils/ui/tooltip.js +35 -10
- package/dist/src/view/axisGridView.d.ts.map +1 -1
- package/dist/src/view/axisGridView.js +1 -4
- package/dist/src/view/axisView.d.ts +18 -2
- package/dist/src/view/axisView.d.ts.map +1 -1
- package/dist/src/view/axisView.js +180 -75
- package/dist/src/view/concatView.d.ts +10 -2
- package/dist/src/view/concatView.d.ts.map +1 -1
- package/dist/src/view/concatView.js +46 -9
- package/dist/src/view/containerMutationHelper.d.ts +20 -1
- package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
- package/dist/src/view/containerMutationHelper.js +203 -32
- package/dist/src/view/facetView.d.ts +1 -1
- package/dist/src/view/gridView/gridChild.d.ts +54 -4
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +301 -120
- package/dist/src/view/gridView/gridChildLegends.d.ts +57 -0
- package/dist/src/view/gridView/gridChildLegends.d.ts.map +1 -0
- package/dist/src/view/gridView/gridChildLegends.js +503 -0
- package/dist/src/view/gridView/gridView.d.ts +25 -0
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +454 -78
- package/dist/src/view/gridView/legendLayout.d.ts +26 -0
- package/dist/src/view/gridView/legendLayout.d.ts.map +1 -0
- package/dist/src/view/gridView/legendLayout.js +111 -0
- package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
- package/dist/src/view/gridView/scrollbar.js +1 -4
- package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
- package/dist/src/view/gridView/selectionRect.js +1 -4
- package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
- package/dist/src/view/gridView/separatorView.js +1 -4
- package/dist/src/view/layerView.d.ts +9 -2
- package/dist/src/view/layerView.d.ts.map +1 -1
- package/dist/src/view/layerView.js +18 -1
- package/dist/src/view/layout/flexLayout.d.ts +20 -4
- package/dist/src/view/layout/flexLayout.d.ts.map +1 -1
- package/dist/src/view/layout/flexLayout.js +331 -31
- package/dist/src/view/layout/rectangle.d.ts +14 -0
- package/dist/src/view/layout/rectangle.d.ts.map +1 -1
- package/dist/src/view/layout/rectangle.js +40 -0
- package/dist/src/view/legend/legendEntries.d.ts +20 -0
- package/dist/src/view/legend/legendEntries.d.ts.map +1 -0
- package/dist/src/view/legend/legendEntries.js +21 -0
- package/dist/src/view/legendView.d.ts +134 -0
- package/dist/src/view/legendView.d.ts.map +1 -0
- package/dist/src/view/legendView.js +1611 -0
- package/dist/src/view/renderingContext/bufferedViewRenderingContext.d.ts.map +1 -1
- package/dist/src/view/renderingContext/bufferedViewRenderingContext.js +26 -4
- package/dist/src/view/renderingContext/clipOptions.d.ts +44 -0
- package/dist/src/view/renderingContext/clipOptions.d.ts.map +1 -0
- package/dist/src/view/renderingContext/clipOptions.js +140 -0
- package/dist/src/view/renderingContext/simpleViewRenderingContext.d.ts.map +1 -1
- package/dist/src/view/renderingContext/simpleViewRenderingContext.js +12 -1
- package/dist/src/view/resolutionPlanner.d.ts +2 -1
- package/dist/src/view/resolutionPlanner.d.ts.map +1 -1
- package/dist/src/view/resolutionPlanner.js +89 -25
- package/dist/src/view/testUtils.d.ts +4 -2
- package/dist/src/view/testUtils.d.ts.map +1 -1
- package/dist/src/view/testUtils.js +29 -7
- package/dist/src/view/titleView.d.ts +37 -0
- package/dist/src/view/titleView.d.ts.map +1 -0
- package/dist/src/view/titleView.js +584 -0
- package/dist/src/view/unitView.d.ts +3 -3
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +3 -2
- package/dist/src/view/view.d.ts +25 -24
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +121 -16
- package/dist/src/view/viewFactory.d.ts +2 -5
- package/dist/src/view/viewFactory.d.ts.map +1 -1
- package/dist/src/view/viewFactory.js +1 -2
- package/dist/src/view/viewIdentityRegistry.d.ts +37 -0
- package/dist/src/view/viewIdentityRegistry.d.ts.map +1 -0
- package/dist/src/view/viewIdentityRegistry.js +71 -0
- package/dist/src/view/viewMutationAcidTestUtils.d.ts +112 -0
- package/dist/src/view/viewMutationAcidTestUtils.d.ts.map +1 -0
- package/dist/src/view/viewMutationAcidTestUtils.js +234 -0
- package/dist/src/view/viewMutationApi.d.ts +42 -0
- package/dist/src/view/viewMutationApi.d.ts.map +1 -0
- package/dist/src/view/viewMutationApi.js +811 -0
- package/dist/src/view/viewSelectors.d.ts +10 -0
- package/dist/src/view/viewSelectors.d.ts.map +1 -1
- package/dist/src/view/viewSelectors.js +23 -1
- package/package.json +4 -4
- package/dist/bundle/esm-0dYHNV_D.js +0 -121
- package/dist/bundle/esm-C49STiCR.js +0 -1248
- package/dist/bundle/esm-CscjKVDc.js +0 -1426
- package/dist/bundle/esm-CuMSzCHy.js +0 -298
- package/dist/bundle/esm-DMXpJXM4.js +0 -369
- package/dist/src/view/title.d.ts +0 -13
- package/dist/src/view/title.d.ts.map +0 -1
- package/dist/src/view/title.js +0 -154
- /package/dist/bundle/{AbortablePromiseCache-3gHJdF3E.js → AbortablePromiseCache-BTmAcN-t.js} +0 -0
- /package/dist/bundle/{esm-CuVa5T98.js → esm-VvpZ9hsq.js} +0 -0
- /package/dist/bundle/{chunk-DmhlhrBa.js → rolldown-runtime-Dy4uBu1J.js} +0 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
import ConcatView from "./concatView.js";
|
|
2
|
+
import GridView from "./gridView/gridView.js";
|
|
3
|
+
import LayerView from "./layerView.js";
|
|
4
|
+
import { isMultiscaleSpec } from "./multiscale.js";
|
|
5
|
+
import UnitView from "./unitView.js";
|
|
6
|
+
import { isImportSpec, isLayerSpec, isUnitSpec } from "./viewSpecGuards.js";
|
|
7
|
+
import {
|
|
8
|
+
getImportScopeInfo,
|
|
9
|
+
getViewScopeChain,
|
|
10
|
+
getViewSelector,
|
|
11
|
+
registerViewScope,
|
|
12
|
+
resolveViewSelector,
|
|
13
|
+
} from "./viewSelectors.js";
|
|
14
|
+
import { getViewIdentityRegistry } from "./viewIdentityRegistry.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown by the public view mutation API.
|
|
18
|
+
*/
|
|
19
|
+
export class ViewMutationError extends Error {
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} code
|
|
22
|
+
* @param {string} message
|
|
23
|
+
*/
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "ViewMutationError";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Stable error code for programmatic handling.
|
|
30
|
+
*/
|
|
31
|
+
this.code = code;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {{
|
|
37
|
+
* depth: number,
|
|
38
|
+
* queue: Promise<void>,
|
|
39
|
+
* failed: boolean,
|
|
40
|
+
* error: unknown | undefined,
|
|
41
|
+
* requestedLayoutReflow: boolean,
|
|
42
|
+
* restoreLayoutReflow: () => void
|
|
43
|
+
* }} TransactionState
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates the public view hierarchy API for a GenomeSpy instance.
|
|
48
|
+
*
|
|
49
|
+
* @param {{ viewRoot: import("./view.js").default }} genomeSpy
|
|
50
|
+
* @returns {import("../types/embedApi.js").ViewApi}
|
|
51
|
+
*/
|
|
52
|
+
export function createViewMutationApi(genomeSpy) {
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {import("../types/embedApi.js").ViewAddress} ViewAddress
|
|
55
|
+
* @typedef {import("../types/embedApi.js").ViewHandle} ViewHandle
|
|
56
|
+
* @typedef {import("../view/viewUtilTypes.d.ts").ViewSelector} ViewSelector
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/** @type {WeakMap<import("./view.js").default, import("../types/embedApi.js").ViewHandle>} */
|
|
60
|
+
const handlesByView = new WeakMap();
|
|
61
|
+
|
|
62
|
+
/** @type {WeakMap<import("../types/embedApi.js").ViewHandle, import("./view.js").default>} */
|
|
63
|
+
const viewsByHandle = new WeakMap();
|
|
64
|
+
|
|
65
|
+
let queue = Promise.resolve();
|
|
66
|
+
|
|
67
|
+
/** @type {TransactionState | undefined} */
|
|
68
|
+
let activeTransaction;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @returns {import("./view.js").default}
|
|
72
|
+
*/
|
|
73
|
+
function getRootView() {
|
|
74
|
+
return genomeSpy.viewRoot;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {import("./view.js").default} view
|
|
79
|
+
* @returns {boolean}
|
|
80
|
+
*/
|
|
81
|
+
function isLiveView(view) {
|
|
82
|
+
const root = getRootView();
|
|
83
|
+
return (
|
|
84
|
+
view === root || Boolean(root?.getDescendants?.().includes(view))
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {import("./view.js").default} view
|
|
90
|
+
* @returns {import("../types/embedApi.js").ViewHandleType}
|
|
91
|
+
*/
|
|
92
|
+
function getHandleType(view) {
|
|
93
|
+
if (view instanceof ConcatView) {
|
|
94
|
+
return "concat";
|
|
95
|
+
} else if (view instanceof LayerView) {
|
|
96
|
+
return "layer";
|
|
97
|
+
} else if (view instanceof UnitView) {
|
|
98
|
+
return "unit";
|
|
99
|
+
} else if (view instanceof GridView) {
|
|
100
|
+
return "grid";
|
|
101
|
+
} else {
|
|
102
|
+
return "unknown";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {import("./view.js").default} view
|
|
108
|
+
* @returns {ViewSelector | undefined}
|
|
109
|
+
*/
|
|
110
|
+
function getSelector(view) {
|
|
111
|
+
if (!view.explicitName) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
return getViewSelector(view);
|
|
117
|
+
} catch {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {import("./view.js").default} view
|
|
124
|
+
* @returns {import("../types/embedApi.js").ViewHandle}
|
|
125
|
+
*/
|
|
126
|
+
function getHandle(view) {
|
|
127
|
+
let handle = handlesByView.get(view);
|
|
128
|
+
if (handle) {
|
|
129
|
+
return handle;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const id = getViewIdentityRegistry(getRootView()).getId(view);
|
|
133
|
+
|
|
134
|
+
handle = {
|
|
135
|
+
id,
|
|
136
|
+
|
|
137
|
+
get name() {
|
|
138
|
+
return view.explicitName;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
get selector() {
|
|
142
|
+
return getSelector(view);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
get type() {
|
|
146
|
+
return getHandleType(view);
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
isAlive: () => isLiveView(view),
|
|
150
|
+
|
|
151
|
+
parent: () => {
|
|
152
|
+
if (!isLiveView(view) || !view.layoutParent) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return getHandle(view.layoutParent);
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
children: () => {
|
|
160
|
+
const children = getLayoutChildren(view);
|
|
161
|
+
if (!isLiveView(view) || !children) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return children.map((child) => getHandle(child));
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
handlesByView.set(view, handle);
|
|
170
|
+
viewsByHandle.set(handle, view);
|
|
171
|
+
return handle;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @param {import("./view.js").default} view
|
|
176
|
+
* @returns {import("./view.js").default[] | undefined}
|
|
177
|
+
*/
|
|
178
|
+
function getLayoutChildren(view) {
|
|
179
|
+
const children = /** @type {{ children?: unknown }} */ (view).children;
|
|
180
|
+
return Array.isArray(children)
|
|
181
|
+
? /** @type {import("./view.js").default[]} */ (children)
|
|
182
|
+
: undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @param {ViewAddress} address
|
|
187
|
+
* @returns {import("../types/embedApi.js").ViewHandle | undefined}
|
|
188
|
+
*/
|
|
189
|
+
function resolve(address) {
|
|
190
|
+
if (address === "root") {
|
|
191
|
+
return getHandle(getRootView());
|
|
192
|
+
} else if (isViewHandle(address)) {
|
|
193
|
+
return viewsByHandle.has(address) && address.isAlive()
|
|
194
|
+
? address
|
|
195
|
+
: undefined;
|
|
196
|
+
} else if (isViewSelector(address)) {
|
|
197
|
+
const view = resolveViewSelector(getRootView(), address);
|
|
198
|
+
return view ? getHandle(view) : undefined;
|
|
199
|
+
} else {
|
|
200
|
+
throw new ViewMutationError(
|
|
201
|
+
"invalidAddress",
|
|
202
|
+
'View address must be a handle, selector, or "root".'
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {ViewAddress} address
|
|
209
|
+
* @returns {import("../types/embedApi.js").ViewHandle}
|
|
210
|
+
*/
|
|
211
|
+
function get(address) {
|
|
212
|
+
const handle = resolve(address);
|
|
213
|
+
if (handle) {
|
|
214
|
+
return handle;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (isViewHandle(address) && viewsByHandle.has(address)) {
|
|
218
|
+
throw new ViewMutationError(
|
|
219
|
+
"staleHandle",
|
|
220
|
+
"Stale view handle no longer refers to a live view."
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
throw new ViewMutationError(
|
|
225
|
+
"unresolvedAddress",
|
|
226
|
+
"View address did not resolve to a live view."
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @param {ViewAddress} address
|
|
232
|
+
* @returns {import("./view.js").default}
|
|
233
|
+
*/
|
|
234
|
+
function getView(address) {
|
|
235
|
+
const handle = get(address);
|
|
236
|
+
const view = viewsByHandle.get(handle);
|
|
237
|
+
if (!view) {
|
|
238
|
+
throw new ViewMutationError(
|
|
239
|
+
"invalidAddress",
|
|
240
|
+
"View handle is not owned by this GenomeSpy instance."
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return view;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {ViewAddress} address
|
|
249
|
+
* @returns {import("../types/embedApi.js").ViewLayoutBounds | undefined}
|
|
250
|
+
*/
|
|
251
|
+
function getLayoutBounds(address) {
|
|
252
|
+
const handle = resolve(address);
|
|
253
|
+
if (!handle) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const view = viewsByHandle.get(handle);
|
|
258
|
+
const coords = view?.coords;
|
|
259
|
+
if (!coords) {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
x: coords.x,
|
|
265
|
+
y: coords.y,
|
|
266
|
+
width: coords.width,
|
|
267
|
+
height: coords.height,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @param {() => void} listener
|
|
273
|
+
* @returns {() => void}
|
|
274
|
+
*/
|
|
275
|
+
function subscribeToLayout(listener) {
|
|
276
|
+
const context = getRootView().context;
|
|
277
|
+
const broadcastListener = () => listener();
|
|
278
|
+
context.addBroadcastListener("layoutComputed", broadcastListener);
|
|
279
|
+
|
|
280
|
+
return () =>
|
|
281
|
+
context.removeBroadcastListener(
|
|
282
|
+
"layoutComputed",
|
|
283
|
+
broadcastListener
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @template T
|
|
289
|
+
* @param {() => T | Promise<T>} operation
|
|
290
|
+
* @returns {Promise<T>}
|
|
291
|
+
*/
|
|
292
|
+
function enqueue(operation) {
|
|
293
|
+
if (activeTransaction) {
|
|
294
|
+
return enqueueTransactionOperation(activeTransaction, operation);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return enqueueTopLevelOperation(operation);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @template T
|
|
302
|
+
* @param {() => T | Promise<T>} operation
|
|
303
|
+
* @returns {Promise<T>}
|
|
304
|
+
*/
|
|
305
|
+
function enqueueTopLevelOperation(operation) {
|
|
306
|
+
const next = queue.then(operation, operation);
|
|
307
|
+
queue = next.then(clearQueueValue, clearQueueValue);
|
|
308
|
+
return next;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @template T
|
|
313
|
+
* @param {TransactionState} transaction
|
|
314
|
+
* @param {() => T | Promise<T>} operation
|
|
315
|
+
* @returns {Promise<T>}
|
|
316
|
+
*/
|
|
317
|
+
function enqueueTransactionOperation(transaction, operation) {
|
|
318
|
+
const next = transaction.queue.then(operation, operation);
|
|
319
|
+
transaction.queue = next.then(clearQueueValue, (error) => {
|
|
320
|
+
transaction.failed = true;
|
|
321
|
+
transaction.error = error;
|
|
322
|
+
});
|
|
323
|
+
return next;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @returns {void}
|
|
328
|
+
*/
|
|
329
|
+
function clearQueueValue() {}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* @returns {TransactionState}
|
|
333
|
+
*/
|
|
334
|
+
function beginTransaction() {
|
|
335
|
+
const transaction = activeTransaction;
|
|
336
|
+
if (transaction) {
|
|
337
|
+
transaction.depth++;
|
|
338
|
+
return transaction;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const context = getRootView().context;
|
|
342
|
+
const requestLayoutReflow = context.requestLayoutReflow;
|
|
343
|
+
/** @type {TransactionState} */
|
|
344
|
+
const topLevelTransaction = {
|
|
345
|
+
depth: 1,
|
|
346
|
+
queue: Promise.resolve(),
|
|
347
|
+
failed: false,
|
|
348
|
+
error: undefined,
|
|
349
|
+
requestedLayoutReflow: false,
|
|
350
|
+
restoreLayoutReflow: () => {
|
|
351
|
+
context.requestLayoutReflow = requestLayoutReflow;
|
|
352
|
+
if (topLevelTransaction.requestedLayoutReflow) {
|
|
353
|
+
requestLayoutReflow.call(context);
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
context.requestLayoutReflow = () => {
|
|
359
|
+
topLevelTransaction.requestedLayoutReflow = true;
|
|
360
|
+
};
|
|
361
|
+
activeTransaction = topLevelTransaction;
|
|
362
|
+
return topLevelTransaction;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* @param {TransactionState} transaction
|
|
367
|
+
* @returns {void}
|
|
368
|
+
*/
|
|
369
|
+
function endTransaction(transaction) {
|
|
370
|
+
transaction.depth--;
|
|
371
|
+
|
|
372
|
+
if (transaction.depth === 0) {
|
|
373
|
+
transaction.restoreLayoutReflow();
|
|
374
|
+
activeTransaction = undefined;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Mutations may enqueue more mutations from promise continuations. Keep
|
|
380
|
+
* draining until the transaction queue stops changing.
|
|
381
|
+
*
|
|
382
|
+
* @param {TransactionState} transaction
|
|
383
|
+
* @returns {Promise<void>}
|
|
384
|
+
*/
|
|
385
|
+
async function waitForTransactionQueue(transaction) {
|
|
386
|
+
let currentQueue;
|
|
387
|
+
do {
|
|
388
|
+
currentQueue = transaction.queue;
|
|
389
|
+
await currentQueue;
|
|
390
|
+
} while (transaction.queue !== currentQueue);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @template T
|
|
395
|
+
* @param {(views: import("../types/embedApi.js").ViewApi) => T | Promise<T>} callback
|
|
396
|
+
* @returns {Promise<T>}
|
|
397
|
+
*/
|
|
398
|
+
async function runTransaction(callback) {
|
|
399
|
+
const transaction = beginTransaction();
|
|
400
|
+
const previouslyFailed = transaction.failed;
|
|
401
|
+
/** @type {T | undefined} */
|
|
402
|
+
let result;
|
|
403
|
+
/** @type {unknown} */
|
|
404
|
+
let callbackError;
|
|
405
|
+
let callbackFailed = false;
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
result = await callback(api);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
callbackError = error;
|
|
411
|
+
callbackFailed = true;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
await waitForTransactionQueue(transaction);
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
if (callbackFailed) {
|
|
418
|
+
throw callbackError;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (transaction.failed && transaction.failed !== previouslyFailed) {
|
|
422
|
+
throw transaction.error;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return /** @type {T} */ (result);
|
|
426
|
+
} finally {
|
|
427
|
+
endTransaction(transaction);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @param {ViewAddress} parentAddress
|
|
433
|
+
* @param {import("../spec/view.js").ViewSpec | import("../spec/view.js").ImportSpec} spec
|
|
434
|
+
* @param {import("../types/embedApi.js").InsertViewOptions} [options]
|
|
435
|
+
* @returns {Promise<ViewHandle>}
|
|
436
|
+
*/
|
|
437
|
+
function insert(parentAddress, spec, options = {}) {
|
|
438
|
+
return enqueue(async () => {
|
|
439
|
+
const parentView = getView(parentAddress);
|
|
440
|
+
const childCount = getMutableContainerChildCount(parentView);
|
|
441
|
+
const index = getInsertIndex(options.index, childCount);
|
|
442
|
+
const scopeName = getInsertScopeName(spec, options);
|
|
443
|
+
|
|
444
|
+
if (scopeName !== undefined && typeof scopeName === "string") {
|
|
445
|
+
ensureScopeNameIsAvailable(parentView, scopeName);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const childSpec = structuredClone(spec);
|
|
449
|
+
const childView = await addChildToMutableContainer(
|
|
450
|
+
parentView,
|
|
451
|
+
childSpec,
|
|
452
|
+
index
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
if (options.scope !== undefined) {
|
|
456
|
+
registerViewScope(childView, options.scope);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return getHandle(childView);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* @param {import("./view.js").default} parentView
|
|
465
|
+
* @returns {number}
|
|
466
|
+
*/
|
|
467
|
+
function getMutableContainerChildCount(parentView) {
|
|
468
|
+
if (
|
|
469
|
+
!(parentView instanceof ConcatView) &&
|
|
470
|
+
!(parentView instanceof LayerView)
|
|
471
|
+
) {
|
|
472
|
+
throw new ViewMutationError(
|
|
473
|
+
"unsupportedContainer",
|
|
474
|
+
"Only concat and layer views support child insertion."
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const children = getLayoutChildren(parentView);
|
|
479
|
+
if (!children) {
|
|
480
|
+
throw new ViewMutationError(
|
|
481
|
+
"unsupportedContainer",
|
|
482
|
+
"Mutable container does not expose layout children."
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return children.length;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* @param {import("./view.js").default} parentView
|
|
491
|
+
* @param {import("../spec/view.js").ViewSpec | import("../spec/view.js").ImportSpec} childSpec
|
|
492
|
+
* @param {number} index
|
|
493
|
+
* @returns {Promise<import("./view.js").default>}
|
|
494
|
+
*/
|
|
495
|
+
async function addChildToMutableContainer(parentView, childSpec, index) {
|
|
496
|
+
if (parentView instanceof ConcatView) {
|
|
497
|
+
return parentView.addChildSpec(childSpec, index);
|
|
498
|
+
} else if (parentView instanceof LayerView) {
|
|
499
|
+
if (!isLayerChildSpec(childSpec)) {
|
|
500
|
+
throw new ViewMutationError(
|
|
501
|
+
"unsupportedChildSpec",
|
|
502
|
+
"Layer views accept only unit, layer, multiscale, or import specs as children."
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return parentView.addChildSpec(childSpec, index);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
throw new ViewMutationError(
|
|
510
|
+
"unsupportedContainer",
|
|
511
|
+
"Only concat and layer views support child insertion."
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* @param {number | undefined} index
|
|
517
|
+
* @param {number} childCount
|
|
518
|
+
* @returns {number}
|
|
519
|
+
*/
|
|
520
|
+
function getInsertIndex(index, childCount) {
|
|
521
|
+
const insertIndex = index ?? childCount;
|
|
522
|
+
|
|
523
|
+
if (!Number.isInteger(insertIndex)) {
|
|
524
|
+
throw new ViewMutationError(
|
|
525
|
+
"invalidIndex",
|
|
526
|
+
"Insert index must be an integer."
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (insertIndex < 0 || insertIndex > childCount) {
|
|
531
|
+
throw new ViewMutationError(
|
|
532
|
+
"invalidIndex",
|
|
533
|
+
"Insert index must be between 0 and the current child count."
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return insertIndex;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* @param {import("../spec/view.js").ViewSpec | import("../spec/view.js").ImportSpec} spec
|
|
542
|
+
* @param {import("../types/embedApi.js").InsertViewOptions} options
|
|
543
|
+
* @returns {string | null | undefined}
|
|
544
|
+
*/
|
|
545
|
+
function getInsertScopeName(spec, options) {
|
|
546
|
+
const importScopeName =
|
|
547
|
+
isImportSpec(spec) && "name" in spec ? spec.name : undefined;
|
|
548
|
+
|
|
549
|
+
if (
|
|
550
|
+
options.scope !== undefined &&
|
|
551
|
+
importScopeName !== undefined &&
|
|
552
|
+
options.scope !== importScopeName
|
|
553
|
+
) {
|
|
554
|
+
throw new ViewMutationError(
|
|
555
|
+
"scopeMismatch",
|
|
556
|
+
"Insert scope must match the import instance name."
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return options.scope ?? importScopeName;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* @param {import("./view.js").default} parentView
|
|
565
|
+
* @param {string} scopeName
|
|
566
|
+
*/
|
|
567
|
+
function ensureScopeNameIsAvailable(parentView, scopeName) {
|
|
568
|
+
const parentScope = getViewScopeChain(parentView);
|
|
569
|
+
const targetScope = parentScope.concat(scopeName);
|
|
570
|
+
|
|
571
|
+
getRootView().visit((view) => {
|
|
572
|
+
const info = getImportScopeInfo(view);
|
|
573
|
+
if (
|
|
574
|
+
info &&
|
|
575
|
+
info.name === scopeName &&
|
|
576
|
+
scopesEqual(getViewScopeChain(view), targetScope)
|
|
577
|
+
) {
|
|
578
|
+
throw new ViewMutationError(
|
|
579
|
+
"duplicateScope",
|
|
580
|
+
'Scope "' + scopeName + '" already exists.'
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* @param {string[]} a
|
|
588
|
+
* @param {string[]} b
|
|
589
|
+
* @returns {boolean}
|
|
590
|
+
*/
|
|
591
|
+
function scopesEqual(a, b) {
|
|
592
|
+
if (a.length !== b.length) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return a.every((value, index) => value === b[index]);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* @param {import("../spec/view.js").ViewSpec | import("../spec/view.js").ImportSpec} spec
|
|
601
|
+
* @returns {spec is import("../spec/view.js").UnitSpec | import("../spec/view.js").LayerSpec | import("../spec/view.js").MultiscaleSpec | import("../spec/view.js").ImportSpec}
|
|
602
|
+
*/
|
|
603
|
+
function isLayerChildSpec(spec) {
|
|
604
|
+
if (isImportSpec(spec)) {
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const viewSpec = /** @type {import("../spec/view.js").ViewSpec} */ (
|
|
609
|
+
spec
|
|
610
|
+
);
|
|
611
|
+
return (
|
|
612
|
+
isUnitSpec(viewSpec) ||
|
|
613
|
+
isLayerSpec(viewSpec) ||
|
|
614
|
+
isMultiscaleSpec(viewSpec)
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* @param {ViewAddress} targetAddress
|
|
620
|
+
* @returns {Promise<void>}
|
|
621
|
+
*/
|
|
622
|
+
function remove(targetAddress) {
|
|
623
|
+
return enqueue(async () => {
|
|
624
|
+
const targetView = getView(targetAddress);
|
|
625
|
+
if (targetView === getRootView() || !targetView.layoutParent) {
|
|
626
|
+
throw new ViewMutationError(
|
|
627
|
+
"cannotRemoveRoot",
|
|
628
|
+
"Removing the root view is not supported."
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const parentView = targetView.layoutParent;
|
|
633
|
+
const children = getLayoutChildren(parentView);
|
|
634
|
+
if (!children) {
|
|
635
|
+
throw new ViewMutationError(
|
|
636
|
+
"unsupportedContainer",
|
|
637
|
+
"Parent view does not expose layout children."
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const index = children.indexOf(targetView);
|
|
642
|
+
if (index < 0) {
|
|
643
|
+
throw new ViewMutationError(
|
|
644
|
+
"invalidHierarchy",
|
|
645
|
+
"Target view is not a child of its layout parent."
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
await removeChildFromMutableContainer(parentView, index);
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* @param {import("./view.js").default} parentView
|
|
655
|
+
* @param {number} index
|
|
656
|
+
*/
|
|
657
|
+
async function removeChildFromMutableContainer(parentView, index) {
|
|
658
|
+
if (parentView instanceof ConcatView) {
|
|
659
|
+
await parentView.removeChildAt(index);
|
|
660
|
+
} else if (parentView instanceof LayerView) {
|
|
661
|
+
await parentView.removeChildAt(index);
|
|
662
|
+
} else {
|
|
663
|
+
throw new ViewMutationError(
|
|
664
|
+
"unsupportedContainer",
|
|
665
|
+
"Only concat and layer views support child removal."
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* @param {ViewAddress} targetAddress
|
|
672
|
+
* @param {import("../types/embedApi.js").MoveViewOptions} options
|
|
673
|
+
* @returns {Promise<ViewHandle>}
|
|
674
|
+
*/
|
|
675
|
+
function move(targetAddress, options) {
|
|
676
|
+
return enqueue(async () => {
|
|
677
|
+
const targetView = getView(targetAddress);
|
|
678
|
+
if (targetView === getRootView() || !targetView.layoutParent) {
|
|
679
|
+
throw new ViewMutationError(
|
|
680
|
+
"cannotMoveRoot",
|
|
681
|
+
"Moving the root view is not supported."
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const parentView = targetView.layoutParent;
|
|
686
|
+
const children = getLayoutChildren(parentView);
|
|
687
|
+
if (!children) {
|
|
688
|
+
throw new ViewMutationError(
|
|
689
|
+
"unsupportedContainer",
|
|
690
|
+
"Parent view does not expose layout children."
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const fromIndex = children.indexOf(targetView);
|
|
695
|
+
if (fromIndex < 0) {
|
|
696
|
+
throw new ViewMutationError(
|
|
697
|
+
"invalidHierarchy",
|
|
698
|
+
"Target view is not a child of its layout parent."
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (!options) {
|
|
703
|
+
throw new ViewMutationError(
|
|
704
|
+
"invalidIndex",
|
|
705
|
+
"Move options with an index are required."
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const index = getMoveIndex(options.index, children.length - 1);
|
|
710
|
+
await moveChildWithinMutableContainer(parentView, fromIndex, index);
|
|
711
|
+
|
|
712
|
+
return getHandle(targetView);
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* @param {number | undefined} index
|
|
718
|
+
* @param {number} remainingChildCount
|
|
719
|
+
* @returns {number}
|
|
720
|
+
*/
|
|
721
|
+
function getMoveIndex(index, remainingChildCount) {
|
|
722
|
+
if (!Number.isInteger(index)) {
|
|
723
|
+
throw new ViewMutationError(
|
|
724
|
+
"invalidIndex",
|
|
725
|
+
"Move index must be an integer."
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (index < 0 || index > remainingChildCount) {
|
|
730
|
+
throw new ViewMutationError(
|
|
731
|
+
"invalidIndex",
|
|
732
|
+
"Move index must be between 0 and the remaining child count."
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return index;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* @param {import("./view.js").default} parentView
|
|
741
|
+
* @param {number} fromIndex
|
|
742
|
+
* @param {number} index
|
|
743
|
+
*/
|
|
744
|
+
async function moveChildWithinMutableContainer(
|
|
745
|
+
parentView,
|
|
746
|
+
fromIndex,
|
|
747
|
+
index
|
|
748
|
+
) {
|
|
749
|
+
if (parentView instanceof ConcatView) {
|
|
750
|
+
await parentView.moveChildAt(fromIndex, index);
|
|
751
|
+
} else if (parentView instanceof LayerView) {
|
|
752
|
+
parentView.moveChildAt(fromIndex, index);
|
|
753
|
+
} else {
|
|
754
|
+
throw new ViewMutationError(
|
|
755
|
+
"unsupportedContainer",
|
|
756
|
+
"Only concat and layer views support child reordering."
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** @type {import("../types/embedApi.js").ViewApi} */
|
|
762
|
+
const api = {
|
|
763
|
+
root: () => getHandle(getRootView()),
|
|
764
|
+
|
|
765
|
+
resolve,
|
|
766
|
+
|
|
767
|
+
get,
|
|
768
|
+
|
|
769
|
+
getLayoutBounds,
|
|
770
|
+
|
|
771
|
+
subscribeToLayout,
|
|
772
|
+
|
|
773
|
+
insert,
|
|
774
|
+
|
|
775
|
+
remove,
|
|
776
|
+
|
|
777
|
+
move,
|
|
778
|
+
|
|
779
|
+
transaction: (callback) =>
|
|
780
|
+
activeTransaction
|
|
781
|
+
? runTransaction(callback)
|
|
782
|
+
: enqueueTopLevelOperation(() => runTransaction(callback)),
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
return api;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* @param {unknown} address
|
|
790
|
+
* @returns {address is import("../types/embedApi.js").ViewHandle}
|
|
791
|
+
*/
|
|
792
|
+
function isViewHandle(address) {
|
|
793
|
+
return (
|
|
794
|
+
typeof address === "object" &&
|
|
795
|
+
address !== null &&
|
|
796
|
+
typeof (/** @type {any} */ (address).isAlive) === "function"
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* @param {unknown} address
|
|
802
|
+
* @returns {address is import("../view/viewUtilTypes.d.ts").ViewSelector}
|
|
803
|
+
*/
|
|
804
|
+
function isViewSelector(address) {
|
|
805
|
+
return (
|
|
806
|
+
typeof address === "object" &&
|
|
807
|
+
address !== null &&
|
|
808
|
+
Array.isArray(/** @type {any} */ (address).scope) &&
|
|
809
|
+
typeof (/** @type {any} */ (address).view) === "string"
|
|
810
|
+
);
|
|
811
|
+
}
|