matterviz 0.4.0 → 0.4.2
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/EmptyState.svelte +10 -4
- package/dist/FilePicker.svelte +12 -14
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +2 -2
- package/dist/app.css +53 -0
- package/dist/brillouin/BrillouinZone.svelte +132 -219
- package/dist/brillouin/BrillouinZone.svelte.d.ts +9 -20
- package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
- package/dist/brillouin/BrillouinZoneExportPane.svelte +47 -96
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +15 -42
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
- package/dist/brillouin/BrillouinZoneScene.svelte +63 -230
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
- package/dist/brillouin/ReciprocalVectors.svelte +39 -0
- package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
- package/dist/brillouin/compute.d.ts +2 -0
- package/dist/brillouin/compute.js +80 -77
- package/dist/brillouin/geometry.d.ts +8 -0
- package/dist/brillouin/geometry.js +57 -0
- package/dist/brillouin/index.d.ts +2 -0
- package/dist/brillouin/index.js +2 -0
- package/dist/brillouin/types.d.ts +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +143 -246
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +652 -976
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
- package/dist/chempot-diagram/async-compute.svelte.js +1 -1
- package/dist/chempot-diagram/color.d.ts +3 -6
- package/dist/chempot-diagram/color.js +5 -5
- package/dist/chempot-diagram/compute.d.ts +3 -3
- package/dist/chempot-diagram/compute.js +3 -1
- package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
- package/dist/chempot-diagram/controls-state.svelte.js +42 -0
- package/dist/chempot-diagram/export.d.ts +47 -0
- package/dist/chempot-diagram/export.js +133 -0
- package/dist/chempot-diagram/index.d.ts +1 -0
- package/dist/chempot-diagram/index.js +1 -0
- package/dist/chempot-diagram/pointer.d.ts +0 -10
- package/dist/chempot-diagram/pointer.js +4 -4
- package/dist/chempot-diagram/types.d.ts +3 -3
- package/dist/colors/index.d.ts +0 -4
- package/dist/colors/index.js +21 -22
- package/dist/composition/BarChart.svelte +37 -31
- package/dist/composition/BubbleChart.svelte +17 -17
- package/dist/composition/Composition.svelte +7 -11
- package/dist/composition/Formula.svelte +18 -25
- package/dist/composition/FormulaFilter.svelte +96 -98
- package/dist/composition/PieChart.svelte +60 -46
- package/dist/composition/chem-sys.js +3 -2
- package/dist/composition/format.js +4 -3
- package/dist/composition/index.d.ts +2 -0
- package/dist/composition/index.js +14 -0
- package/dist/composition/parse.d.ts +2 -3
- package/dist/composition/parse.js +27 -28
- package/dist/constants.js +11 -9
- package/dist/controls.d.ts +1 -0
- package/dist/controls.js +0 -1
- package/dist/convex-hull/ConvexHull.svelte +20 -25
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
- package/dist/convex-hull/ConvexHull2D.svelte +176 -256
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +269 -804
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +236 -755
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +264 -0
- package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
- package/dist/convex-hull/ConvexHullControls.svelte +128 -49
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
- package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
- package/dist/convex-hull/ConvexHullStats.svelte +210 -351
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +16 -14
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
- package/dist/convex-hull/GasPressureControls.svelte +5 -12
- package/dist/convex-hull/StructurePopup.svelte +17 -21
- package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
- package/dist/convex-hull/TemperatureSlider.svelte +4 -10
- package/dist/convex-hull/barycentric-coords.d.ts +2 -4
- package/dist/convex-hull/barycentric-coords.js +6 -33
- package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
- package/dist/convex-hull/canvas-interactions.svelte.js +279 -0
- package/dist/convex-hull/helpers.d.ts +47 -17
- package/dist/convex-hull/helpers.js +186 -101
- package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
- package/dist/convex-hull/hull-state.svelte.js +124 -0
- package/dist/convex-hull/index.d.ts +9 -7
- package/dist/convex-hull/index.js +7 -2
- package/dist/convex-hull/thermodynamics.js +109 -937
- package/dist/convex-hull/types.d.ts +12 -4
- package/dist/convex-hull/types.js +12 -0
- package/dist/coordination/CoordinationBarPlot.svelte +41 -43
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/coordination/calc-coordination.d.ts +1 -1
- package/dist/coordination/calc-coordination.js +28 -25
- package/dist/element/BohrAtom.svelte +6 -8
- package/dist/element/ElementHeading.svelte +4 -1
- package/dist/element/ElementPhoto.svelte +11 -9
- package/dist/element/ElementStats.svelte +12 -10
- package/dist/element/ElementTile.svelte +24 -29
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/element/Nucleus.svelte +4 -2
- package/dist/element/data.d.ts +1 -2
- package/dist/element/helpers.d.ts +4 -0
- package/dist/element/helpers.js +18 -0
- package/dist/element/index.d.ts +1 -0
- package/dist/element/index.js +3 -0
- package/dist/feedback/DragOverlay.svelte +5 -3
- package/dist/feedback/DragOverlay.svelte.d.ts +2 -1
- package/dist/feedback/StatusMessage.svelte +20 -7
- package/dist/fermi-surface/FermiSlice.svelte +21 -18
- package/dist/fermi-surface/FermiSurface.svelte +78 -165
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +9 -20
- package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
- package/dist/fermi-surface/FermiSurfaceScene.svelte +93 -272
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
- package/dist/fermi-surface/compute.js +14 -13
- package/dist/fermi-surface/export.js +4 -15
- package/dist/fermi-surface/index.d.ts +0 -1
- package/dist/fermi-surface/index.js +0 -1
- package/dist/fermi-surface/parse.d.ts +1 -1
- package/dist/fermi-surface/parse.js +84 -97
- package/dist/fermi-surface/types.d.ts +2 -2
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +161 -185
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +6 -5
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +12 -9
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
- package/dist/heatmap-matrix/index.d.ts +8 -2
- package/dist/icons.d.ts +4 -0
- package/dist/icons.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/io/ExportPane.svelte +170 -0
- package/dist/io/ExportPane.svelte.d.ts +17 -0
- package/dist/io/decompress.d.ts +2 -1
- package/dist/io/decompress.js +22 -36
- package/dist/io/export.d.ts +9 -1
- package/dist/io/export.js +37 -29
- package/dist/io/fetch.d.ts +2 -4
- package/dist/io/fetch.js +0 -11
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +13 -0
- package/dist/io/index.d.ts +1 -0
- package/dist/io/index.js +3 -0
- package/dist/io/is-binary.d.ts +9 -0
- package/dist/io/is-binary.js +45 -1
- package/dist/io/types.d.ts +13 -0
- package/dist/io/url-drop.js +13 -25
- package/dist/isosurface/Isosurface.svelte +17 -23
- package/dist/isosurface/IsosurfaceControls.svelte +37 -42
- package/dist/isosurface/parse-vaspwave.d.ts +3 -0
- package/dist/isosurface/parse-vaspwave.js +138 -0
- package/dist/isosurface/parse.js +50 -48
- package/dist/labels.d.ts +6 -2
- package/dist/labels.js +44 -14
- package/dist/layout/FullscreenButton.svelte +34 -0
- package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
- package/dist/layout/FullscreenToggle.svelte +12 -12
- package/dist/layout/InfoCard.svelte +7 -18
- package/dist/layout/InfoTag.svelte +1 -3
- package/dist/layout/NumberRangeInput.svelte +46 -0
- package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
- package/dist/layout/PropertyFilter.svelte +2 -6
- package/dist/layout/SettingsSection.svelte +21 -17
- package/dist/layout/SubpageGrid.svelte +3 -1
- package/dist/layout/ViewerChrome.svelte +115 -0
- package/dist/layout/ViewerChrome.svelte.d.ts +18 -0
- package/dist/layout/fullscreen.d.ts +4 -0
- package/dist/layout/fullscreen.js +3 -5
- package/dist/layout/fullscreen.svelte.d.ts +8 -0
- package/dist/layout/fullscreen.svelte.js +36 -0
- package/dist/layout/index.d.ts +4 -0
- package/dist/layout/index.js +4 -0
- package/dist/layout/json-tree/JsonNode.svelte +8 -11
- package/dist/layout/json-tree/JsonTree.svelte +53 -77
- package/dist/layout/json-tree/JsonValue.svelte +7 -15
- package/dist/layout/json-tree/utils.js +14 -23
- package/dist/math.d.ts +9 -3
- package/dist/math.js +37 -21
- package/dist/overlays/ContextMenu.svelte +4 -2
- package/dist/overlays/CopyButton.svelte +11 -5
- package/dist/overlays/DragControlTab.svelte +6 -1
- package/dist/overlays/DraggablePane.svelte +81 -26
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
- package/dist/overlays/GlassChip.svelte +30 -0
- package/dist/overlays/GlassChip.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +11 -23
- package/dist/overlays/index.d.ts +11 -0
- package/dist/overlays/index.js +26 -0
- package/dist/overlays/portal.d.ts +2 -0
- package/dist/overlays/portal.js +14 -0
- package/dist/periodic-table/PeriodicTable.svelte +115 -146
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
- package/dist/periodic-table/PeriodicTableControls.svelte +11 -63
- package/dist/periodic-table/TableInset.svelte +9 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
- package/dist/phase-diagram/PhaseDiagramControls.svelte +73 -96
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +4 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +49 -134
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
- package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
- package/dist/phase-diagram/build-diagram.js +3 -3
- package/dist/phase-diagram/colors.js +1 -1
- package/dist/phase-diagram/diagram-input.d.ts +1 -2
- package/dist/phase-diagram/parse.d.ts +2 -1
- package/dist/phase-diagram/parse.js +15 -15
- package/dist/phase-diagram/svg-to-diagram.js +21 -15
- package/dist/phase-diagram/utils.js +2 -3
- package/dist/plot/bar/BarPlot.svelte +494 -692
- package/dist/plot/bar/BarPlot.svelte.d.ts +9 -15
- package/dist/plot/bar/BarPlotControls.svelte +1 -3
- package/dist/plot/bar/BarPlotControls.svelte.d.ts +5 -7
- package/dist/plot/bar/SpacegroupBarPlot.svelte +31 -37
- package/dist/plot/bar/data.d.ts +5 -4
- package/dist/plot/bar/data.js +2 -1
- package/dist/plot/box/BoxPlot.svelte +361 -439
- package/dist/plot/box/BoxPlot.svelte.d.ts +6 -3
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
- package/dist/plot/box/Violin.svelte.d.ts +2 -1
- package/dist/plot/box/box-plot.d.ts +3 -2
- package/dist/plot/box/box-plot.js +6 -3
- package/dist/plot/box/kde.d.ts +2 -1
- package/dist/plot/box/kde.js +4 -4
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +5 -2
- package/dist/plot/core/axis-utils.d.ts +12 -7
- package/dist/plot/core/axis-utils.js +48 -48
- package/dist/plot/core/components/AxisLabel.svelte +82 -20
- package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
- package/dist/plot/core/components/ColorBar.svelte +106 -111
- package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
- package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
- package/dist/plot/core/components/FillArea.svelte +10 -17
- package/dist/plot/core/components/HierarchyControls.svelte +249 -0
- package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
- package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
- package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/core/components/Line.svelte +21 -11
- package/dist/plot/core/components/Line.svelte.d.ts +5 -2
- package/dist/plot/core/components/PlotAxis.svelte +42 -18
- package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
- package/dist/plot/core/components/PlotControls.svelte +65 -41
- package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/core/components/PlotLegend.svelte +31 -35
- package/dist/plot/core/components/PlotMarginals.svelte +616 -0
- package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
- package/dist/plot/core/components/PlotTooltip.svelte +1 -1
- package/dist/plot/core/components/PortalSelect.svelte +4 -8
- package/dist/plot/core/components/ReferenceLine.svelte +27 -24
- package/dist/plot/core/components/ReferenceLine3D.svelte +56 -59
- package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
- package/dist/plot/core/components/ReferencePlane.svelte +9 -10
- package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
- package/dist/plot/core/components/ZeroLines.svelte +4 -8
- package/dist/plot/core/components/ZoomRect.svelte +1 -2
- package/dist/plot/core/components/index.d.ts +1 -0
- package/dist/plot/core/components/index.js +1 -0
- package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
- package/dist/plot/core/data-cleaning-signal.js +467 -0
- package/dist/plot/core/data-cleaning.d.ts +2 -42
- package/dist/plot/core/data-cleaning.js +29 -476
- package/dist/plot/core/data-transform.js +9 -5
- package/dist/plot/core/fill-utils.d.ts +7 -4
- package/dist/plot/core/fill-utils.js +34 -9
- package/dist/plot/core/index.d.ts +1 -0
- package/dist/plot/core/index.js +1 -0
- package/dist/plot/core/interactions.d.ts +5 -1
- package/dist/plot/core/interactions.js +14 -2
- package/dist/plot/core/layout.d.ts +3 -1
- package/dist/plot/core/layout.js +22 -13
- package/dist/plot/core/marginals.d.ts +170 -0
- package/dist/plot/core/marginals.js +436 -0
- package/dist/plot/core/pan-zoom.svelte.d.ts +35 -0
- package/dist/plot/core/pan-zoom.svelte.js +221 -0
- package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -0
- package/dist/plot/core/reference-line.d.ts +10 -10
- package/dist/plot/core/reference-line.js +7 -7
- package/dist/plot/core/scales.d.ts +40 -25
- package/dist/plot/core/scales.js +49 -21
- package/dist/plot/core/svg.d.ts +2 -1
- package/dist/plot/core/types/fills.d.ts +101 -0
- package/dist/plot/core/types/fills.js +15 -0
- package/dist/plot/core/types/plot-3d.d.ts +87 -0
- package/dist/plot/core/types/plot-3d.js +4 -0
- package/dist/plot/core/types/reference-lines.d.ts +140 -0
- package/dist/plot/core/types/reference-lines.js +9 -0
- package/dist/plot/core/types.d.ts +47 -331
- package/dist/plot/core/types.js +23 -20
- package/dist/plot/core/utils/hierarchy-chart.d.ts +51 -0
- package/dist/plot/core/utils/hierarchy-chart.js +179 -0
- package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
- package/dist/plot/core/utils/hierarchy-labels.js +99 -0
- package/dist/plot/core/utils/label-placement.d.ts +1 -1
- package/dist/plot/core/utils/label-placement.js +5 -5
- package/dist/plot/core/utils.d.ts +2 -1
- package/dist/plot/histogram/Histogram.svelte +373 -631
- package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
- package/dist/plot/histogram/HistogramControls.svelte +30 -28
- package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/histogram/histogram.d.ts +31 -0
- package/dist/plot/histogram/histogram.js +70 -0
- package/dist/plot/histogram/index.d.ts +1 -0
- package/dist/plot/histogram/index.js +1 -0
- package/dist/plot/index.d.ts +1 -0
- package/dist/plot/index.js +1 -0
- package/dist/plot/sankey/Sankey.svelte +79 -73
- package/dist/plot/sankey/Sankey.svelte.d.ts +3 -3
- package/dist/plot/sankey/SankeyControls.svelte +22 -16
- package/dist/plot/sankey/sankey.js +3 -1
- package/dist/plot/scatter/BinnedScatterPlot.svelte +264 -112
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +11 -4
- package/dist/plot/scatter/ElementScatter.svelte +4 -1
- package/dist/plot/scatter/ScatterPlot.svelte +623 -821
- package/dist/plot/scatter/ScatterPlot.svelte.d.ts +9 -15
- package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
- package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/scatter/ScatterPoint.svelte +16 -9
- package/dist/plot/scatter/binned-scatter-types.d.ts +6 -12
- package/dist/plot/scatter/binned-scatter-types.js +6 -1
- package/dist/plot/scatter/index.d.ts +1 -1
- package/dist/plot/scatter/scatter-data.d.ts +1 -1
- package/dist/plot/scatter/scatter-data.js +8 -7
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte +40 -60
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +17 -15
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +232 -234
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
- package/dist/plot/scatter-3d/Surface3D.svelte +9 -17
- package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
- package/dist/plot/sunburst/Sunburst.svelte +256 -338
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
- package/dist/plot/sunburst/index.d.ts +1 -1
- package/dist/plot/sunburst/index.js +2 -1
- package/dist/plot/sunburst/render.d.ts +2 -2
- package/dist/plot/sunburst/render.js +47 -22
- package/dist/plot/sunburst/sunburst.d.ts +5 -1
- package/dist/plot/sunburst/sunburst.js +14 -4
- package/dist/plot/treemap/Treemap.svelte +907 -0
- package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
- package/dist/plot/treemap/index.d.ts +3 -0
- package/dist/plot/treemap/index.js +4 -0
- package/dist/plot/treemap/treemap.d.ts +26 -0
- package/dist/plot/treemap/treemap.js +99 -0
- package/dist/rdf/RdfPlot.svelte +22 -26
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/calc-rdf.js +4 -8
- package/dist/sanitize.js +14 -3
- package/dist/scene/SceneCamera.svelte +62 -0
- package/dist/scene/SceneCamera.svelte.d.ts +19 -0
- package/dist/scene/bind-renderer.svelte.d.ts +2 -0
- package/dist/scene/bind-renderer.svelte.js +22 -0
- package/dist/scene/index.d.ts +4 -0
- package/dist/scene/index.js +5 -0
- package/dist/scene/props.js +52 -0
- package/dist/scene/types.d.ts +26 -0
- package/dist/scene/types.js +1 -0
- package/dist/settings.d.ts +33 -11
- package/dist/settings.js +136 -51
- package/dist/spectral/Bands.svelte +231 -237
- package/dist/spectral/Bands.svelte.d.ts +3 -2
- package/dist/spectral/BandsAndDos.svelte +29 -29
- package/dist/spectral/BrillouinBandsDos.svelte +21 -29
- package/dist/spectral/Dos.svelte +75 -77
- package/dist/spectral/Dos.svelte.d.ts +2 -1
- package/dist/spectral/helpers.d.ts +9 -9
- package/dist/spectral/helpers.js +66 -70
- package/dist/state.svelte.d.ts +0 -7
- package/dist/state.svelte.js +5 -24
- package/dist/structure/Arrow.svelte +6 -21
- package/dist/structure/ArrowInstances.svelte +160 -0
- package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
- package/dist/structure/AtomLegend.svelte +115 -75
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Bond.svelte +31 -23
- package/dist/structure/CanvasTooltip.svelte +16 -3
- package/dist/structure/CellSelect.svelte +22 -29
- package/dist/structure/CellSelect.svelte.d.ts +2 -1
- package/dist/structure/Cylinder.svelte +24 -40
- package/dist/structure/InstancedAtoms.svelte +114 -0
- package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
- package/dist/structure/Lattice.svelte +85 -149
- package/dist/structure/SiteLabels.svelte +122 -0
- package/dist/structure/SiteLabels.svelte.d.ts +18 -0
- package/dist/structure/Structure.svelte +725 -735
- package/dist/structure/Structure.svelte.d.ts +8 -16
- package/dist/structure/StructureCarousel.svelte +630 -0
- package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
- package/dist/structure/StructureControls.svelte +298 -325
- package/dist/structure/StructureControls.svelte.d.ts +5 -3
- package/dist/structure/StructureExportPane.svelte +62 -168
- package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
- package/dist/structure/StructureInfoPane.svelte +77 -69
- package/dist/structure/StructureInfoPane.svelte.d.ts +6 -6
- package/dist/structure/StructureScene.svelte +758 -600
- package/dist/structure/StructureScene.svelte.d.ts +24 -20
- package/dist/structure/StructureViewport.svelte +386 -0
- package/dist/structure/StructureViewport.svelte.d.ts +63 -0
- package/dist/structure/atom-label-placement.d.ts +18 -0
- package/dist/structure/atom-label-placement.js +115 -0
- package/dist/structure/atom-properties.d.ts +3 -1
- package/dist/structure/atom-properties.js +115 -54
- package/dist/structure/bond-order-perception.js +2 -4
- package/dist/structure/bonding.d.ts +12 -2
- package/dist/structure/bonding.js +172 -73
- package/dist/structure/edit-history.d.ts +6 -0
- package/dist/structure/edit-history.js +27 -0
- package/dist/structure/export.d.ts +25 -4
- package/dist/structure/export.js +103 -153
- package/dist/structure/format-detect.d.ts +1 -0
- package/dist/structure/format-detect.js +22 -0
- package/dist/structure/geometry.d.ts +9 -0
- package/dist/structure/geometry.js +27 -0
- package/dist/structure/index.d.ts +18 -1
- package/dist/structure/index.js +15 -2
- package/dist/structure/measure.d.ts +1 -1
- package/dist/structure/measure.js +6 -12
- package/dist/structure/parse.d.ts +5 -3
- package/dist/structure/parse.js +551 -567
- package/dist/structure/partial-occupancy.d.ts +0 -1
- package/dist/structure/partial-occupancy.js +5 -3
- package/dist/structure/pbc.d.ts +1 -1
- package/dist/structure/pbc.js +204 -51
- package/dist/structure/polyhedra.d.ts +40 -0
- package/dist/structure/polyhedra.js +587 -0
- package/dist/structure/site.d.ts +4 -0
- package/dist/structure/site.js +1 -0
- package/dist/structure/supercell.d.ts +1 -2
- package/dist/structure/supercell.js +10 -15
- package/dist/structure/validation.js +5 -6
- package/dist/symmetry/SymmetryElementControls.svelte +69 -0
- package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
- package/dist/symmetry/SymmetryElements.svelte +350 -0
- package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
- package/dist/symmetry/SymmetryStats.svelte +142 -49
- package/dist/symmetry/WyckoffTable.svelte +58 -16
- package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
- package/dist/symmetry/cell-transform.js +8 -14
- package/dist/symmetry/index.d.ts +15 -4
- package/dist/symmetry/index.js +308 -81
- package/dist/symmetry/spacegroups.d.ts +5 -1
- package/dist/symmetry/spacegroups.js +18 -3
- package/dist/symmetry/symmetry-elements.d.ts +33 -0
- package/dist/symmetry/symmetry-elements.js +521 -0
- package/dist/symmetry/wyckoff-db.d.ts +9 -0
- package/dist/symmetry/wyckoff-db.js +87 -0
- package/dist/table/HeatmapTable.svelte +1104 -378
- package/dist/table/HeatmapTable.svelte.d.ts +12 -2
- package/dist/table/ToggleMenu.svelte +5 -12
- package/dist/table/index.d.ts +21 -6
- package/dist/table/index.js +34 -21
- package/dist/theme/ThemeControl.svelte +16 -8
- package/dist/theme/index.d.ts +1 -5
- package/dist/theme/index.js +4 -10
- package/dist/theme/themes.mjs +14 -7
- package/dist/tooltip/KCoords.svelte +4 -1
- package/dist/tooltip/TooltipContent.svelte +2 -5
- package/dist/trajectory/Trajectory.svelte +556 -352
- package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
- package/dist/trajectory/TrajectoryError.svelte +11 -8
- package/dist/trajectory/TrajectoryExportPane.svelte +28 -54
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
- package/dist/trajectory/TrajectoryInfoPane.svelte +88 -99
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
- package/dist/trajectory/constants.js +6 -2
- package/dist/trajectory/extract.js +73 -71
- package/dist/trajectory/format-detect.d.ts +0 -1
- package/dist/trajectory/format-detect.js +13 -17
- package/dist/trajectory/frame-reader.d.ts +2 -1
- package/dist/trajectory/frame-reader.js +74 -136
- package/dist/trajectory/helpers.d.ts +12 -2
- package/dist/trajectory/helpers.js +68 -37
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +3 -0
- package/dist/trajectory/parse/ase.d.ts +9 -1
- package/dist/trajectory/parse/ase.js +47 -32
- package/dist/trajectory/parse/diagnostics.d.ts +3 -0
- package/dist/trajectory/parse/diagnostics.js +14 -0
- package/dist/trajectory/parse/h5-utils.d.ts +13 -0
- package/dist/trajectory/parse/h5-utils.js +109 -0
- package/dist/trajectory/parse/hdf5.d.ts +1 -1
- package/dist/trajectory/parse/hdf5.js +116 -106
- package/dist/trajectory/parse/index.d.ts +3 -4
- package/dist/trajectory/parse/index.js +68 -119
- package/dist/trajectory/parse/lammps.d.ts +0 -2
- package/dist/trajectory/parse/lammps.js +14 -16
- package/dist/trajectory/parse/pymatgen.d.ts +2 -0
- package/dist/trajectory/parse/pymatgen.js +74 -0
- package/dist/trajectory/parse/vasp.js +13 -9
- package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
- package/dist/trajectory/parse/vaspout-electronic.js +207 -0
- package/dist/trajectory/parse/vaspout-h5.d.ts +5 -0
- package/dist/trajectory/parse/vaspout-h5.js +284 -0
- package/dist/trajectory/parse/xyz.d.ts +9 -21
- package/dist/trajectory/parse/xyz.js +38 -39
- package/dist/trajectory/plotting.d.ts +8 -10
- package/dist/trajectory/plotting.js +102 -127
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +8 -1
- package/dist/xrd/XrdPlot.svelte +56 -85
- package/dist/xrd/broadening.d.ts +2 -1
- package/dist/xrd/broadening.js +2 -1
- package/dist/xrd/calc-xrd.js +8 -15
- package/dist/xrd/index.d.ts +2 -2
- package/dist/xrd/parse.d.ts +0 -1
- package/dist/xrd/parse.js +56 -73
- package/package.json +77 -36
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/element/data.json +0 -11864
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/core/hover-lock.svelte.js +0 -45
- package/dist/plot/sunburst/SunburstControls.svelte +0 -200
- package/dist/structure/label-placement.d.ts +0 -14
- package/dist/structure/label-placement.js +0 -72
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
lang="ts"
|
|
3
3
|
generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
|
|
4
4
|
>
|
|
5
|
-
import type {
|
|
6
|
-
import { format_value } from '../../labels'
|
|
5
|
+
import type { D3InterpolateName } from '../../colors'
|
|
6
|
+
import { format_value, format_value_or_num } from '../../labels'
|
|
7
7
|
import { sanitize_html } from '../../sanitize'
|
|
8
8
|
import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
|
|
9
9
|
import type { Point2D, Vec2 } from '../../math'
|
|
10
10
|
import type {
|
|
11
11
|
AxisLoadError,
|
|
12
12
|
BasePlotProps,
|
|
13
|
+
ColorScaleConfig,
|
|
13
14
|
ControlsConfig,
|
|
14
15
|
DataLoaderFn,
|
|
15
16
|
DataSeries,
|
|
@@ -17,7 +18,6 @@
|
|
|
17
18
|
FillHandlerEvent,
|
|
18
19
|
FillRegion,
|
|
19
20
|
HoverConfig,
|
|
20
|
-
InitialRanges,
|
|
21
21
|
InternalPoint,
|
|
22
22
|
LabelPlacementConfig,
|
|
23
23
|
LegendConfig,
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
Point,
|
|
27
27
|
RefLine,
|
|
28
28
|
RefLineEvent,
|
|
29
|
-
ScaleType,
|
|
30
29
|
ScatterHandlerEvent,
|
|
31
30
|
ScatterHandlerProps,
|
|
31
|
+
SizeScaleConfig,
|
|
32
32
|
StyleOverrides,
|
|
33
33
|
UserContentProps,
|
|
34
34
|
} from '..'
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
Line,
|
|
41
41
|
PlotAxis,
|
|
42
42
|
PlotLegend,
|
|
43
|
+
PlotMarginals,
|
|
43
44
|
PlotTooltip,
|
|
44
45
|
ReferenceLine,
|
|
45
46
|
ScatterPlotControls,
|
|
@@ -47,23 +48,33 @@
|
|
|
47
48
|
ZeroLines,
|
|
48
49
|
ZoomRect,
|
|
49
50
|
} from '..'
|
|
51
|
+
import type { MarginalSeriesInput, MarginalsProp } from '../core/marginals'
|
|
52
|
+
import {
|
|
53
|
+
add_sides,
|
|
54
|
+
marginal_axis,
|
|
55
|
+
marginal_axis_presence,
|
|
56
|
+
normalize_marginals,
|
|
57
|
+
reserve_marginal_pad,
|
|
58
|
+
} from '../core/marginals'
|
|
50
59
|
import {
|
|
51
60
|
build_obstacles_norm,
|
|
52
61
|
has_explicit_position,
|
|
53
62
|
measured_footprint,
|
|
54
63
|
place_decorations,
|
|
55
64
|
} from '../core/auto-place'
|
|
56
|
-
import type { AxisChangeState } from '../core/axis-utils'
|
|
57
|
-
import { AXIS_DEFAULTS, create_axis_loader } from '../core/axis-utils'
|
|
58
|
-
import { get_series_color, get_series_symbol } from '../core/data-transform'
|
|
59
65
|
import {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
AXIS_DEFAULTS,
|
|
67
|
+
type AxisChangeState,
|
|
68
|
+
create_axis_loader,
|
|
69
|
+
} from '../core/axis-utils'
|
|
70
|
+
import { get_series_color, get_series_symbol } from '../core/data-transform'
|
|
71
|
+
import { create_placed_tween } from '../core/placed-tween.svelte'
|
|
63
72
|
import {
|
|
73
|
+
COLOR_BAR_DEFAULTS,
|
|
64
74
|
DEFAULT_MARKERS,
|
|
65
75
|
get_scale_type_name,
|
|
66
76
|
is_time_scale,
|
|
77
|
+
SCALE_DEFAULTS,
|
|
67
78
|
} from '../core/types'
|
|
68
79
|
import { compute_label_positions } from '../core/utils/label-placement'
|
|
69
80
|
import { create_legend_visibility } from '../core/utils/series-visibility'
|
|
@@ -73,7 +84,7 @@
|
|
|
73
84
|
import type { ComponentProps, Snippet } from 'svelte'
|
|
74
85
|
import { onDestroy, untrack } from 'svelte'
|
|
75
86
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
76
|
-
import {
|
|
87
|
+
import type { TweenOptions } from 'svelte/motion'
|
|
77
88
|
import { SvelteSet } from 'svelte/reactivity'
|
|
78
89
|
import type { Pt } from '../core/fill-utils'
|
|
79
90
|
import {
|
|
@@ -84,18 +95,14 @@
|
|
|
84
95
|
import {
|
|
85
96
|
expand_range_if_needed,
|
|
86
97
|
get_relative_coords,
|
|
87
|
-
|
|
98
|
+
invert_rect_range,
|
|
88
99
|
normalize_y2_sync,
|
|
89
|
-
pan_range_by_pixels,
|
|
90
|
-
PINCH_ZOOM_THRESHOLD,
|
|
91
|
-
remove_drag_listeners,
|
|
92
|
-
sorted_range,
|
|
93
100
|
sync_y2_range,
|
|
94
|
-
to_epoch_num,
|
|
95
|
-
zoom_range_by_factor,
|
|
96
101
|
} from '../core/interactions'
|
|
102
|
+
import { create_pan_zoom } from '../core/pan-zoom.svelte'
|
|
97
103
|
import type { Rect, Sides } from '../core/layout'
|
|
98
104
|
import {
|
|
105
|
+
AXIS_TITLE_OFFSET,
|
|
99
106
|
calc_auto_padding,
|
|
100
107
|
filter_padding,
|
|
101
108
|
LABEL_GAP_DEFAULT,
|
|
@@ -114,11 +121,10 @@
|
|
|
114
121
|
get_nice_data_range,
|
|
115
122
|
} from '../core/scales'
|
|
116
123
|
import { resolve_line_tween, unique_id } from '../core/utils'
|
|
117
|
-
import {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} from './scatter-data'
|
|
124
|
+
import { build_legend_data, filter_series_to_ranges, pick_tooltip_bg } from './scatter-data'
|
|
125
|
+
|
|
126
|
+
// Plots with at least this many visible markers get smaller default point radii
|
|
127
|
+
const DENSE_MARKER_COUNT = 100
|
|
122
128
|
|
|
123
129
|
let {
|
|
124
130
|
series = $bindable([]),
|
|
@@ -130,7 +136,7 @@
|
|
|
130
136
|
styles: styles_init = {},
|
|
131
137
|
controls: controls_init = {},
|
|
132
138
|
padding = {},
|
|
133
|
-
range_padding = 0
|
|
139
|
+
range_padding = 0,
|
|
134
140
|
current_x_value = null,
|
|
135
141
|
tooltip_point = $bindable(null),
|
|
136
142
|
selected_point = null,
|
|
@@ -138,13 +144,9 @@
|
|
|
138
144
|
tooltip,
|
|
139
145
|
user_content,
|
|
140
146
|
change = () => {},
|
|
141
|
-
color_scale =
|
|
142
|
-
type: `linear`,
|
|
143
|
-
scheme: `interpolateViridis`,
|
|
144
|
-
value_range: undefined,
|
|
145
|
-
},
|
|
147
|
+
color_scale = SCALE_DEFAULTS.color,
|
|
146
148
|
color_bar = {},
|
|
147
|
-
size_scale =
|
|
149
|
+
size_scale = SCALE_DEFAULTS.size,
|
|
148
150
|
label_placement_config = {},
|
|
149
151
|
hover_config = {},
|
|
150
152
|
legend = {},
|
|
@@ -171,113 +173,99 @@
|
|
|
171
173
|
on_axis_change,
|
|
172
174
|
on_error,
|
|
173
175
|
pan = {},
|
|
176
|
+
marginals = false,
|
|
174
177
|
...rest
|
|
175
|
-
}: HTMLAttributes<HTMLDivElement> &
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
[
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
selected_series_idx?: number
|
|
233
|
-
wrapper?: HTMLDivElement
|
|
234
|
-
// Interactive axis props
|
|
235
|
-
data_loader?: DataLoaderFn<Metadata>
|
|
236
|
-
on_axis_change?: (
|
|
237
|
-
axis: `x` | `x2` | `y` | `y2`,
|
|
238
|
-
key: string,
|
|
239
|
-
new_series: DataSeries<Metadata>[],
|
|
240
|
-
) => void
|
|
241
|
-
on_error?: (error: AxisLoadError) => void
|
|
242
|
-
pan?: PanConfig
|
|
243
|
-
} = $props()
|
|
178
|
+
}: HTMLAttributes<HTMLDivElement> &
|
|
179
|
+
Omit<BasePlotProps, `change`> &
|
|
180
|
+
PlotConfig & {
|
|
181
|
+
series?: DataSeries<Metadata>[]
|
|
182
|
+
styles?: StyleOverrides
|
|
183
|
+
controls?: ControlsConfig
|
|
184
|
+
current_x_value?: number | null
|
|
185
|
+
tooltip_point?: InternalPoint<Metadata> | null
|
|
186
|
+
selected_point?: { series_idx: number; point_idx: number } | null
|
|
187
|
+
tooltip?: Snippet<[ScatterHandlerProps<Metadata>]>
|
|
188
|
+
user_content?: Snippet<[UserContentProps]>
|
|
189
|
+
header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
|
|
190
|
+
controls_extra?: Snippet<
|
|
191
|
+
[{ styles: StyleOverrides; selected_series_idx: number } & Required<PlotConfig>]
|
|
192
|
+
>
|
|
193
|
+
change?: (data: (Point<Metadata> & { series: DataSeries<Metadata> }) | null) => void
|
|
194
|
+
color_scale?: ColorScaleConfig | D3InterpolateName
|
|
195
|
+
size_scale?: SizeScaleConfig
|
|
196
|
+
color_bar?:
|
|
197
|
+
| (ComponentProps<typeof ColorBar> & {
|
|
198
|
+
margin?: number | Sides
|
|
199
|
+
tween?: TweenOptions<Point2D>
|
|
200
|
+
responsive?: boolean // Allow colorbar to reposition if density changes (default: false)
|
|
201
|
+
axis_clearance?: number // Min distance kept from plot edges/axes (default: 15)
|
|
202
|
+
})
|
|
203
|
+
| null
|
|
204
|
+
label_placement_config?: Partial<LabelPlacementConfig>
|
|
205
|
+
hover_config?: Partial<HoverConfig>
|
|
206
|
+
legend?: LegendConfig | null
|
|
207
|
+
point_tween?: TweenOptions<Point2D>
|
|
208
|
+
line_tween?: TweenOptions<string>
|
|
209
|
+
point_events?: Record<
|
|
210
|
+
string,
|
|
211
|
+
(payload: { point: InternalPoint<Metadata>; event: Event }) => void
|
|
212
|
+
>
|
|
213
|
+
on_point_click?: (data: ScatterHandlerEvent<Metadata>) => void
|
|
214
|
+
on_point_hover?: (data: ScatterHandlerEvent<Metadata> | null) => void
|
|
215
|
+
fill_regions?: FillRegion[] // Bindable for legend toggle support
|
|
216
|
+
error_bands?: ErrorBand[]
|
|
217
|
+
on_fill_click?: (event: FillHandlerEvent) => void
|
|
218
|
+
on_fill_hover?: (event: FillHandlerEvent | null) => void
|
|
219
|
+
ref_lines?: RefLine[] // Bindable for legend toggle support
|
|
220
|
+
on_ref_line_click?: (event: RefLineEvent) => void
|
|
221
|
+
on_ref_line_hover?: (event: RefLineEvent | null) => void
|
|
222
|
+
selected_series_idx?: number
|
|
223
|
+
wrapper?: HTMLDivElement
|
|
224
|
+
// Interactive axis props
|
|
225
|
+
data_loader?: DataLoaderFn<Metadata>
|
|
226
|
+
on_axis_change?: (
|
|
227
|
+
axis: `x` | `x2` | `y` | `y2`,
|
|
228
|
+
key: string,
|
|
229
|
+
new_series: DataSeries<Metadata>[],
|
|
230
|
+
) => void
|
|
231
|
+
on_error?: (error: AxisLoadError) => void
|
|
232
|
+
pan?: PanConfig
|
|
233
|
+
marginals?: MarginalsProp
|
|
234
|
+
} = $props()
|
|
244
235
|
|
|
245
236
|
// Merged axis/display values with defaults (use $derived to avoid breaking $bindable)
|
|
246
237
|
const final_x_axis = $derived({
|
|
247
238
|
...AXIS_DEFAULTS,
|
|
248
|
-
label_shift: { x: 0, y:
|
|
239
|
+
label_shift: { x: 0, y: 0 },
|
|
249
240
|
...x_axis,
|
|
250
241
|
})
|
|
251
242
|
const final_y_axis = $derived({ ...AXIS_DEFAULTS, ...y_axis })
|
|
252
243
|
const final_x2_axis = $derived({
|
|
253
244
|
...AXIS_DEFAULTS,
|
|
254
|
-
label_shift: { x: 0, y:
|
|
245
|
+
label_shift: { x: 0, y: AXIS_TITLE_OFFSET }, // x2-axis label above top edge
|
|
255
246
|
...x2_axis,
|
|
256
247
|
})
|
|
257
248
|
const final_y2_axis = $derived({ ...AXIS_DEFAULTS, ...y2_axis })
|
|
258
249
|
// Cache time-axis check — used in ~10 places for scale/tick/tooltip logic
|
|
259
|
-
let is_time_x = $derived(
|
|
260
|
-
|
|
261
|
-
)
|
|
262
|
-
let is_time_x2 = $derived(
|
|
263
|
-
is_time_scale(final_x2_axis.scale_type, final_x2_axis.format),
|
|
264
|
-
)
|
|
250
|
+
let is_time_x = $derived(is_time_scale(final_x_axis.scale_type, final_x_axis.format))
|
|
251
|
+
let is_time_x2 = $derived(is_time_scale(final_x2_axis.scale_type, final_x2_axis.format))
|
|
265
252
|
const final_display = $derived({ ...DEFAULTS.scatter.display, ...display })
|
|
266
253
|
// Local state for styles (initialized from prop, owned by this component for controls)
|
|
267
254
|
// Using $state because styles has bindings in ScatterPlotControls
|
|
268
255
|
// untrack() explicitly captures initial prop value (intentional - props provide initial config)
|
|
269
|
-
let styles = $state(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
256
|
+
let styles = $state(
|
|
257
|
+
untrack(() => ({
|
|
258
|
+
show_points: DEFAULTS.scatter.show_points,
|
|
259
|
+
show_lines: DEFAULTS.scatter.show_lines,
|
|
260
|
+
point: { ...DEFAULTS.scatter.point, ...styles_init?.point },
|
|
261
|
+
line: { ...DEFAULTS.scatter.line, ...styles_init?.line },
|
|
262
|
+
...styles_init,
|
|
263
|
+
})),
|
|
264
|
+
)
|
|
276
265
|
let controls = $derived({ show: true, open: false, ...controls_init })
|
|
277
266
|
|
|
278
267
|
let [width, height] = $state([0, 0])
|
|
279
268
|
let svg_element: SVGElement | null = $state(null) // Bind the SVG element
|
|
280
|
-
let svg_bounding_box: DOMRect | null = $state(null) // Store SVG bounds during drag
|
|
281
269
|
|
|
282
270
|
// Track which specific control properties user has modified
|
|
283
271
|
let touched = new SvelteSet<string>()
|
|
@@ -296,20 +284,18 @@
|
|
|
296
284
|
}),
|
|
297
285
|
)
|
|
298
286
|
|
|
299
|
-
// State for rectangle zoom selection
|
|
300
|
-
let drag_start_coords = $state<Point2D | null>(null)
|
|
301
|
-
let drag_current_coords = $state<Point2D | null>(null)
|
|
302
|
-
|
|
303
287
|
// Zoom/pan state - track both initial (data-driven) and current (after pan/zoom) ranges
|
|
304
|
-
let
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
288
|
+
let ranges = $state<{
|
|
289
|
+
initial: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
|
|
290
|
+
current: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
|
|
291
|
+
}>({
|
|
292
|
+
initial: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
|
|
293
|
+
current: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
|
|
294
|
+
})
|
|
295
|
+
const legend_vis = create_legend_visibility(
|
|
296
|
+
() => series,
|
|
297
|
+
(next) => (series = next),
|
|
298
|
+
)
|
|
313
299
|
|
|
314
300
|
// Y2 axis sync configuration
|
|
315
301
|
let y2_sync_config = $derived(normalize_y2_sync(y2_axis?.sync))
|
|
@@ -319,7 +305,7 @@
|
|
|
319
305
|
// Helper to compute synced y2 range or return fallback when sync disabled
|
|
320
306
|
const get_synced_y2 = (y1_range: Vec2, fallback: Vec2): Vec2 =>
|
|
321
307
|
y2_sync_config.mode !== `none`
|
|
322
|
-
? sync_y2_range(y1_range,
|
|
308
|
+
? sync_y2_range(y1_range, ranges.initial.y2, y2_sync_config)
|
|
323
309
|
: fallback
|
|
324
310
|
|
|
325
311
|
// Effect to update y2 range when sync mode changes - use $effect.pre to capture
|
|
@@ -330,25 +316,15 @@
|
|
|
330
316
|
if (mode !== prev_sync_mode) {
|
|
331
317
|
// When sync mode becomes enabled (or changes), apply sync immediately
|
|
332
318
|
if (mode !== `none`) {
|
|
333
|
-
|
|
319
|
+
ranges.current.y2 = sync_y2_range(ranges.current.y, ranges.initial.y2, y2_sync_config)
|
|
334
320
|
} else {
|
|
335
321
|
// When switching to independent mode, reset Y2 to its data range
|
|
336
|
-
|
|
322
|
+
ranges.current.y2 = [...ranges.initial.y2] as Vec2
|
|
337
323
|
}
|
|
338
324
|
prev_sync_mode = mode
|
|
339
325
|
}
|
|
340
326
|
})
|
|
341
327
|
|
|
342
|
-
// Pan state
|
|
343
|
-
let is_focused = $state(false)
|
|
344
|
-
let shift_held = $state(false)
|
|
345
|
-
let pan_drag_state = $state<
|
|
346
|
-
InitialRanges & { start: { x: number; y: number } } | null
|
|
347
|
-
>(null)
|
|
348
|
-
let touch_state = $state<
|
|
349
|
-
InitialRanges & { start_touches: { x: number; y: number }[] } | null
|
|
350
|
-
>(null)
|
|
351
|
-
|
|
352
328
|
// Fill region hover state
|
|
353
329
|
let hovered_fill_key = $state<string | null>(null)
|
|
354
330
|
|
|
@@ -370,17 +346,6 @@
|
|
|
370
346
|
// State for legend/colorbar placement stability
|
|
371
347
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
372
348
|
let colorbar_element = $state<HTMLDivElement | undefined>()
|
|
373
|
-
const legend_hover = create_hover_lock()
|
|
374
|
-
const colorbar_hover = create_hover_lock()
|
|
375
|
-
const dim_tracker = create_dimension_tracker()
|
|
376
|
-
let has_initial_legend_placement = $state(false)
|
|
377
|
-
let has_initial_colorbar_placement = $state(false)
|
|
378
|
-
|
|
379
|
-
// Clear pending hover lock timeouts on unmount
|
|
380
|
-
$effect(() => () => {
|
|
381
|
-
legend_hover.cleanup()
|
|
382
|
-
colorbar_hover.cleanup()
|
|
383
|
-
})
|
|
384
349
|
|
|
385
350
|
// Module-level constants to avoid repeated allocations
|
|
386
351
|
// Create and categorize points in a single pass (instead of 3 separate iterations)
|
|
@@ -393,8 +358,13 @@
|
|
|
393
358
|
|
|
394
359
|
for (const srs of series_with_ids) {
|
|
395
360
|
if (!srs) continue
|
|
396
|
-
const {
|
|
397
|
-
|
|
361
|
+
const {
|
|
362
|
+
x: xs,
|
|
363
|
+
y: ys,
|
|
364
|
+
visible = true,
|
|
365
|
+
y_axis: series_y_axis = `y1`,
|
|
366
|
+
x_axis: x_ax = `x1`,
|
|
367
|
+
} = srs as DataSeries
|
|
398
368
|
for (let idx = 0; idx < xs.length; idx++) {
|
|
399
369
|
const point = { x: xs[idx], y: ys[idx] }
|
|
400
370
|
all.push(point)
|
|
@@ -419,37 +389,44 @@
|
|
|
419
389
|
|
|
420
390
|
// Update padding when format or ticks change
|
|
421
391
|
$effect(() => {
|
|
422
|
-
const new_pad =
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
392
|
+
const new_pad =
|
|
393
|
+
width &&
|
|
394
|
+
height &&
|
|
395
|
+
(y_tick_values.length > 0 || y2_tick_values.length > 0 || x2_tick_values.length > 0)
|
|
396
|
+
? calc_auto_padding({
|
|
397
|
+
padding,
|
|
398
|
+
default_padding,
|
|
399
|
+
x2_axis: { ...final_x2_axis, tick_values: x2_tick_values },
|
|
400
|
+
y_axis: { ...final_y_axis, tick_values: y_tick_values },
|
|
401
|
+
y2_axis: { ...final_y2_axis, tick_values: y2_tick_values },
|
|
402
|
+
})
|
|
403
|
+
: filter_padding(padding, default_padding)
|
|
432
404
|
|
|
433
405
|
if (
|
|
434
406
|
base_pad.t !== new_pad.t ||
|
|
435
407
|
base_pad.b !== new_pad.b ||
|
|
436
408
|
base_pad.l !== new_pad.l ||
|
|
437
409
|
base_pad.r !== new_pad.r
|
|
438
|
-
)
|
|
410
|
+
)
|
|
411
|
+
base_pad = new_pad
|
|
439
412
|
})
|
|
440
413
|
|
|
441
414
|
// === Auto-move legend/colorbar outside the plot when interior overlap is unavoidable ===
|
|
442
415
|
// (shared logic lives in auto-place.ts so every 2D plot reuses it)
|
|
443
416
|
// ColorBar's orientation prop defaults to horizontal, so treat unset as horizontal too
|
|
444
|
-
const colorbar_is_horizontal = $derived(
|
|
417
|
+
const colorbar_is_horizontal = $derived(
|
|
418
|
+
(color_bar?.orientation ?? `horizontal`) === `horizontal`,
|
|
419
|
+
)
|
|
445
420
|
const colorbar_footprint = $derived(
|
|
446
421
|
colorbar_element?.offsetWidth && colorbar_element?.offsetHeight
|
|
447
422
|
? measure_full_footprint(colorbar_element)
|
|
448
423
|
: colorbar_is_horizontal
|
|
449
|
-
|
|
450
|
-
|
|
424
|
+
? COLOR_BAR_DEFAULTS.horizontal_footprint
|
|
425
|
+
: COLOR_BAR_DEFAULTS.vertical_footprint,
|
|
426
|
+
)
|
|
427
|
+
const legend_footprint = $derived(
|
|
428
|
+
measured_footprint(legend_element, { width: 120, height: 80 }),
|
|
451
429
|
)
|
|
452
|
-
const legend_footprint = $derived(measured_footprint(legend_element, { width: 120, height: 80 }))
|
|
453
430
|
const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
|
|
454
431
|
|
|
455
432
|
// Plot-specific obstacle field: series points/lines normalized to [0,1] (y=0 at top)
|
|
@@ -459,7 +436,9 @@
|
|
|
459
436
|
const base_h = height - base_pad.t - base_pad.b
|
|
460
437
|
if (base_w <= 0 || base_h <= 0) return []
|
|
461
438
|
const norm_x = is_time_x
|
|
462
|
-
? scaleTime()
|
|
439
|
+
? scaleTime()
|
|
440
|
+
.domain([new Date(x_min), new Date(x_max)])
|
|
441
|
+
.range([0, 1])
|
|
463
442
|
: create_scale(final_x_axis.scale_type ?? `linear`, [x_min, x_max], [0, 1])
|
|
464
443
|
const norm_y = create_scale(final_y_axis.scale_type ?? `linear`, [y_min, y_max], [0, 1])
|
|
465
444
|
return build_obstacles_norm(
|
|
@@ -485,23 +464,58 @@
|
|
|
485
464
|
obstacles_norm,
|
|
486
465
|
// gate on legend_element (the actual render signal) not legend_data, whose fill entries read
|
|
487
466
|
// computed_fills -> pad and would make this derived reference itself
|
|
488
|
-
legend:
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
467
|
+
legend:
|
|
468
|
+
legend != null &&
|
|
469
|
+
legend_element != null &&
|
|
470
|
+
!legend_has_explicit_pos &&
|
|
471
|
+
!legend_is_dragging &&
|
|
472
|
+
!legend_manual_position
|
|
473
|
+
? { footprint: legend_footprint, clearance: legend?.axis_clearance }
|
|
474
|
+
: null,
|
|
492
475
|
// gate on a measured colorbar: its outside style stretches it to full width, so deciding from
|
|
493
476
|
// the (wide) pre-measure fallback would flip-flop placement between interior and outside
|
|
494
|
-
colorbar:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
477
|
+
colorbar:
|
|
478
|
+
Boolean(color_bar) &&
|
|
479
|
+
all_color_values.length > 0 &&
|
|
480
|
+
!color_bar?.wrapper_style &&
|
|
481
|
+
(colorbar_element?.offsetWidth ?? 0) > 0 &&
|
|
482
|
+
(colorbar_element?.offsetHeight ?? 0) > 0
|
|
483
|
+
? {
|
|
484
|
+
footprint: colorbar_footprint,
|
|
485
|
+
horizontal: colorbar_is_horizontal,
|
|
486
|
+
clearance: color_bar?.axis_clearance,
|
|
487
|
+
}
|
|
488
|
+
: null,
|
|
489
|
+
}),
|
|
490
|
+
)
|
|
491
|
+
// Resolve marginals and reserve outer-band padding so the plot shrinks to make room
|
|
492
|
+
const resolved_marginals = $derived(
|
|
493
|
+
normalize_marginals(marginals, { top: true, right: true }),
|
|
494
|
+
)
|
|
495
|
+
const pad = $derived(add_sides(decor.pad, reserve_marginal_pad(resolved_marginals)))
|
|
496
|
+
// Map series to the generic marginal input, reusing the line/legend color fallback
|
|
497
|
+
const marginal_series = $derived<MarginalSeriesInput[]>(
|
|
498
|
+
series_with_ids.map((srs, idx) => {
|
|
499
|
+
const point_fill = Array.isArray(srs?.point_style)
|
|
500
|
+
? srs.point_style[0]?.fill
|
|
501
|
+
: srs?.point_style?.fill
|
|
502
|
+
return {
|
|
503
|
+
x: srs?.x ?? [],
|
|
504
|
+
y: srs?.y ?? [],
|
|
505
|
+
color:
|
|
506
|
+
srs?.line_style?.stroke ??
|
|
507
|
+
point_fill ??
|
|
508
|
+
get_series_color(srs?.orig_series_idx ?? idx),
|
|
509
|
+
label: srs?.label,
|
|
510
|
+
visible: srs?.visible ?? true,
|
|
511
|
+
x_axis: srs?.x_axis,
|
|
512
|
+
y_axis: srs?.y_axis,
|
|
513
|
+
}
|
|
514
|
+
}),
|
|
515
|
+
)
|
|
516
|
+
const marginal_has_axis = $derived(
|
|
517
|
+
marginal_axis_presence(x2_points.length > 0, y2_points.length > 0),
|
|
503
518
|
)
|
|
504
|
-
const pad = $derived(decor.pad)
|
|
505
519
|
const legend_auto_outside = $derived(decor.legend_outside)
|
|
506
520
|
const legend_outside_x = $derived(decor.legend_pos.x)
|
|
507
521
|
const legend_outside_y = $derived(decor.legend_pos.y)
|
|
@@ -514,9 +528,7 @@
|
|
|
514
528
|
x: pad.l || 0,
|
|
515
529
|
y: pad.t || 0,
|
|
516
530
|
width: isFinite(width - pad.l - pad.r) ? Math.max(1, width - pad.l - pad.r) : 1,
|
|
517
|
-
height: isFinite(height - pad.t - pad.b)
|
|
518
|
-
? Math.max(1, height - pad.t - pad.b)
|
|
519
|
-
: 1,
|
|
531
|
+
height: isFinite(height - pad.t - pad.b) ? Math.max(1, height - pad.t - pad.b) : 1,
|
|
520
532
|
})
|
|
521
533
|
|
|
522
534
|
// Calculate plot area center coordinates
|
|
@@ -530,8 +542,12 @@
|
|
|
530
542
|
for (const srs of series_with_ids) {
|
|
531
543
|
if (!srs) continue
|
|
532
544
|
const { color_values: cvs, size_values: svs } = srs as DataSeries
|
|
533
|
-
if (cvs) {
|
|
534
|
-
|
|
545
|
+
if (cvs) {
|
|
546
|
+
for (const val of cvs) if (val != null) color_values.push(val)
|
|
547
|
+
}
|
|
548
|
+
if (svs) {
|
|
549
|
+
for (const val of svs) if (val != null) size_values.push(val)
|
|
550
|
+
}
|
|
535
551
|
}
|
|
536
552
|
return { color_values, size_values }
|
|
537
553
|
})
|
|
@@ -596,77 +612,68 @@
|
|
|
596
612
|
return { explicit, range }
|
|
597
613
|
}
|
|
598
614
|
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
// X axis: explicit → direct, auto → lazy expand
|
|
605
|
-
if (x.explicit) {
|
|
606
|
-
zoom_x_range = x.range
|
|
607
|
-
} else {
|
|
608
|
-
const result = expand_range_if_needed(initial_x_range, x.range)
|
|
609
|
-
if (result.changed) {
|
|
610
|
-
;[initial_x_range, zoom_x_range] = [result.range, result.range]
|
|
611
|
-
}
|
|
615
|
+
const resolved = {
|
|
616
|
+
x: get_range(final_x_axis, auto_x_range),
|
|
617
|
+
x2: get_range(final_x2_axis, auto_x2_range),
|
|
618
|
+
y: get_range(final_y_axis, auto_y_range),
|
|
619
|
+
y2: get_range(final_y2_axis, auto_y2_range),
|
|
612
620
|
}
|
|
613
621
|
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (result.changed) {
|
|
630
|
-
;[initial_y_range, zoom_y_range] = [result.range, result.range]
|
|
622
|
+
// untrack reads of `ranges`: this effect also writes it, and tracked reads of the
|
|
623
|
+
// deep proxy would re-trigger the effect on every current/initial write
|
|
624
|
+
for (const axis of [`x`, `x2`, `y`] as const) {
|
|
625
|
+
const { explicit, range } = resolved[axis]
|
|
626
|
+
if (explicit) {
|
|
627
|
+
ranges.current[axis] = range
|
|
628
|
+
} else {
|
|
629
|
+
const result = expand_range_if_needed(
|
|
630
|
+
untrack(() => ranges.initial[axis]),
|
|
631
|
+
range,
|
|
632
|
+
)
|
|
633
|
+
if (result.changed) {
|
|
634
|
+
ranges.initial[axis] = result.range
|
|
635
|
+
ranges.current[axis] = result.range
|
|
636
|
+
}
|
|
631
637
|
}
|
|
632
638
|
}
|
|
633
639
|
|
|
634
640
|
// Y2 axis: explicit → direct, else expand initial range then optionally sync
|
|
635
|
-
if (y2.explicit) {
|
|
636
|
-
|
|
641
|
+
if (resolved.y2.explicit) {
|
|
642
|
+
ranges.current.y2 = resolved.y2.range
|
|
637
643
|
} else {
|
|
638
|
-
const result = expand_range_if_needed(
|
|
639
|
-
|
|
644
|
+
const result = expand_range_if_needed(
|
|
645
|
+
untrack(() => ranges.initial.y2),
|
|
646
|
+
resolved.y2.range,
|
|
647
|
+
)
|
|
648
|
+
if (result.changed) ranges.initial.y2 = result.range
|
|
640
649
|
// Apply sync if enabled, otherwise use expanded range (or keep current if unchanged)
|
|
641
650
|
if (y2_sync_config.mode !== `none`) {
|
|
642
|
-
//
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
untrack(() => zoom_y_range),
|
|
647
|
-
initial_y2_range,
|
|
651
|
+
// Pan/zoom handlers sync y2 themselves.
|
|
652
|
+
ranges.current.y2 = sync_y2_range(
|
|
653
|
+
untrack(() => ranges.current.y),
|
|
654
|
+
untrack(() => ranges.initial.y2),
|
|
648
655
|
y2_sync_config,
|
|
649
656
|
)
|
|
650
657
|
} else if (result.changed) {
|
|
651
|
-
|
|
658
|
+
ranges.current.y2 = result.range
|
|
652
659
|
}
|
|
653
660
|
}
|
|
654
661
|
})
|
|
655
662
|
|
|
656
|
-
let [x_min, x_max] = $derived(
|
|
657
|
-
let [x2_min, x2_max] = $derived(
|
|
658
|
-
let [y_min, y_max] = $derived(
|
|
659
|
-
let [y2_min, y2_max] = $derived(
|
|
663
|
+
let [x_min, x_max] = $derived(ranges.current.x)
|
|
664
|
+
let [x2_min, x2_max] = $derived(ranges.current.x2)
|
|
665
|
+
let [y_min, y_max] = $derived(ranges.current.y)
|
|
666
|
+
let [y2_min, y2_max] = $derived(ranges.current.y2)
|
|
660
667
|
|
|
661
668
|
// Create auto color range
|
|
662
669
|
let auto_color_range = $derived(
|
|
663
670
|
// Ensure we only calculate extent on actual numbers, filtering out nulls/undefined
|
|
664
671
|
all_color_values.length > 0
|
|
665
672
|
? extent(
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
673
|
+
all_color_values.filter(
|
|
674
|
+
(color_val: number | null): color_val is number => typeof color_val === `number`,
|
|
675
|
+
),
|
|
676
|
+
)
|
|
670
677
|
: [0, 1],
|
|
671
678
|
) as Vec2
|
|
672
679
|
|
|
@@ -675,37 +682,37 @@
|
|
|
675
682
|
let x_scale_fn = $derived(
|
|
676
683
|
is_time_x
|
|
677
684
|
? scaleTime()
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
: create_scale(
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
685
|
+
.domain([new Date(x_min), new Date(x_max)])
|
|
686
|
+
.range([pad.l, width - pad.r])
|
|
687
|
+
: create_scale(
|
|
688
|
+
final_x_axis.scale_type ?? `linear`,
|
|
689
|
+
[x_min, x_max],
|
|
690
|
+
[pad.l, width - pad.r],
|
|
691
|
+
),
|
|
684
692
|
)
|
|
685
693
|
|
|
686
694
|
let x2_scale_fn = $derived(
|
|
687
695
|
is_time_x2
|
|
688
696
|
? scaleTime()
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
: create_scale(
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
697
|
+
.domain([new Date(x2_min), new Date(x2_max)])
|
|
698
|
+
.range([pad.l, width - pad.r])
|
|
699
|
+
: create_scale(
|
|
700
|
+
final_x2_axis.scale_type ?? `linear`,
|
|
701
|
+
[x2_min, x2_max],
|
|
702
|
+
[pad.l, width - pad.r],
|
|
703
|
+
),
|
|
695
704
|
)
|
|
696
705
|
|
|
697
706
|
let y_scale_fn = $derived(
|
|
698
|
-
create_scale(final_y_axis.scale_type ?? `linear`, [y_min, y_max], [
|
|
699
|
-
height - pad.b,
|
|
700
|
-
pad.t,
|
|
701
|
-
]),
|
|
707
|
+
create_scale(final_y_axis.scale_type ?? `linear`, [y_min, y_max], [height - pad.b, pad.t]),
|
|
702
708
|
)
|
|
703
709
|
|
|
704
710
|
let y2_scale_fn = $derived(
|
|
705
|
-
create_scale(
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
711
|
+
create_scale(
|
|
712
|
+
final_y2_axis.scale_type ?? `linear`,
|
|
713
|
+
[y2_min, y2_max],
|
|
714
|
+
[height - pad.b, pad.t],
|
|
715
|
+
),
|
|
709
716
|
)
|
|
710
717
|
|
|
711
718
|
// All size values from series (for size scale) - extracted in series_value_arrays
|
|
@@ -742,6 +749,22 @@
|
|
|
742
749
|
})
|
|
743
750
|
let effective_line_tween = $derived(resolve_line_tween(line_tween, line_tween_load))
|
|
744
751
|
|
|
752
|
+
let visible_marker_count = $derived.by(() => {
|
|
753
|
+
if (!styles.show_points) return 0
|
|
754
|
+
let count = 0
|
|
755
|
+
for (const series_data of filtered_series ?? []) {
|
|
756
|
+
if ((series_data.markers ?? DEFAULT_MARKERS).includes(`points`)) {
|
|
757
|
+
count += series_data.filtered_data.length
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return count
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
const default_point_radius = (markers: string): number => {
|
|
764
|
+
const [sparse_radius, dense_radius] = markers.includes(`line`) ? [2.5, 2] : [3, 2.5]
|
|
765
|
+
return visible_marker_count >= DENSE_MARKER_COUNT ? dense_radius : sparse_radius
|
|
766
|
+
}
|
|
767
|
+
|
|
745
768
|
// Obstacle field for legend/colorbar auto-placement. Sampling only data points lets the
|
|
746
769
|
// legend land on top of a steep connecting line whose markers are sparse (e.g. y=x^2), so
|
|
747
770
|
// sample_series_obstacle_points also walks each drawn segment at a fixed pixel cadence.
|
|
@@ -757,14 +780,16 @@
|
|
|
757
780
|
const active_x_scale = use_x2_scale ? x2_scale_fn : x_scale_fn
|
|
758
781
|
const active_is_time_x = use_x2_scale ? is_time_x2 : is_time_x
|
|
759
782
|
const active_y_scale = series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn
|
|
760
|
-
const draws_line =
|
|
761
|
-
(series_data.markers ?? DEFAULT_MARKERS).includes(`line`)
|
|
783
|
+
const draws_line =
|
|
784
|
+
styles.show_lines && (series_data.markers ?? DEFAULT_MARKERS).includes(`line`)
|
|
762
785
|
|
|
763
786
|
const pixel_points = series_data.filtered_data.map((point) => ({
|
|
764
787
|
x: active_is_time_x ? active_x_scale(new Date(point.x)) : active_x_scale(point.x),
|
|
765
788
|
y: active_y_scale(point.y),
|
|
766
789
|
}))
|
|
767
|
-
points.push(
|
|
790
|
+
points.push(
|
|
791
|
+
...sample_series_obstacle_points(pixel_points, draws_line, SEGMENT_SAMPLE_STEP),
|
|
792
|
+
)
|
|
768
793
|
}
|
|
769
794
|
return points
|
|
770
795
|
})
|
|
@@ -849,14 +874,16 @@
|
|
|
849
874
|
}
|
|
850
875
|
|
|
851
876
|
return all_regions
|
|
852
|
-
.filter(
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
877
|
+
.filter(
|
|
878
|
+
(
|
|
879
|
+
entry,
|
|
880
|
+
): entry is {
|
|
881
|
+
region: FillRegion
|
|
882
|
+
source_type: `fill_region` | `error_band`
|
|
883
|
+
source_idx: number
|
|
884
|
+
hover_key: string
|
|
885
|
+
} => entry.region !== null,
|
|
886
|
+
)
|
|
860
887
|
.map(({ region, source_type, source_idx, hover_key }, idx) => {
|
|
861
888
|
// Hidden fills keep their entry (with empty path_segments -> nothing renders) so the
|
|
862
889
|
// legend item persists greyed-out and can be toggled back on.
|
|
@@ -864,15 +891,15 @@
|
|
|
864
891
|
const path_segments = hidden
|
|
865
892
|
? []
|
|
866
893
|
: compute_fill_segments(region, series_with_ids, domains)
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
894
|
+
.map((seg) =>
|
|
895
|
+
generate_fill_path(
|
|
896
|
+
seg.upper.map(to_px),
|
|
897
|
+
seg.lower.map(to_px),
|
|
898
|
+
seg.upper_curve,
|
|
899
|
+
seg.lower_curve,
|
|
900
|
+
),
|
|
873
901
|
)
|
|
874
|
-
|
|
875
|
-
.filter((path) => path.length > 0)
|
|
902
|
+
.filter((path) => path.length > 0)
|
|
876
903
|
|
|
877
904
|
// Drop only visible fills with no geometry; keep hidden ones for the legend
|
|
878
905
|
if (!hidden && path_segments.length === 0) return null
|
|
@@ -906,13 +933,12 @@
|
|
|
906
933
|
})
|
|
907
934
|
|
|
908
935
|
// Compute ref_lines with index and group by z-index (using shared utilities)
|
|
909
|
-
let
|
|
910
|
-
let ref_lines_by_z = $derived(group_ref_lines_by_z(indexed_ref_lines))
|
|
936
|
+
let ref_lines_by_z = $derived(group_ref_lines_by_z(index_ref_lines(ref_lines)))
|
|
911
937
|
|
|
912
938
|
// Calculate best legend placement using continuous grid sampling
|
|
913
939
|
let legend_placement = $derived.by(() => {
|
|
914
|
-
const should_place =
|
|
915
|
-
(legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)
|
|
940
|
+
const should_place =
|
|
941
|
+
legend != null && (legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)
|
|
916
942
|
|
|
917
943
|
if (!should_place || !width || !height) return null
|
|
918
944
|
|
|
@@ -940,10 +966,9 @@
|
|
|
940
966
|
|
|
941
967
|
// Fallback estimate (with room for tick labels) used before the colorbar first
|
|
942
968
|
// renders; compute_element_placement measures the real footprint once it's laid out
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
: { width: 56, height: 100 }
|
|
969
|
+
const colorbar_size = colorbar_is_horizontal
|
|
970
|
+
? COLOR_BAR_DEFAULTS.horizontal_footprint
|
|
971
|
+
: COLOR_BAR_DEFAULTS.vertical_footprint
|
|
947
972
|
|
|
948
973
|
// Build exclusion rects (avoid legend if it's placed)
|
|
949
974
|
const exclude_rects: Rect[] = []
|
|
@@ -968,75 +993,28 @@
|
|
|
968
993
|
})
|
|
969
994
|
})
|
|
970
995
|
|
|
971
|
-
// Active legend placement (null if user set explicit position)
|
|
972
|
-
let active_legend_placement = $derived
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
// Skip auto-placement if user set explicit position in style
|
|
976
|
-
const legend_style = legend?.style ?? ``
|
|
977
|
-
if (
|
|
978
|
-
/(^|[;{]\s*)(top|bottom|left|right)\s*:|position\s*:\s*absolute/.test(
|
|
979
|
-
legend_style,
|
|
980
|
-
)
|
|
981
|
-
) return null
|
|
982
|
-
|
|
983
|
-
return legend_placement
|
|
984
|
-
})
|
|
985
|
-
|
|
986
|
-
// Initialize tweened values for color bar position - create once, update target via effect
|
|
987
|
-
// untrack() explicitly captures initial tween config (intentional - config set once at mount)
|
|
988
|
-
const tweened_colorbar_coords = new Tween(
|
|
989
|
-
{ x: 0, y: 0 },
|
|
990
|
-
untrack(() => ({ duration: 400, ...color_bar?.tween })),
|
|
991
|
-
)
|
|
992
|
-
// Initialize tweened values for legend position - create once, update target via effect
|
|
993
|
-
const tweened_legend_coords = new Tween(
|
|
994
|
-
{ x: 0, y: 0 },
|
|
995
|
-
untrack(() => ({ duration: 400, ...legend?.tween })),
|
|
996
|
+
// Active legend placement (null if user set explicit position in style)
|
|
997
|
+
let active_legend_placement = $derived(
|
|
998
|
+
legend_placement && !legend_has_explicit_pos ? legend_placement : null,
|
|
996
999
|
)
|
|
997
1000
|
|
|
998
|
-
//
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
has_initial_colorbar_placement ? undefined : { duration: 0 },
|
|
1016
|
-
)
|
|
1017
|
-
if (colorbar_element && !has_initial_colorbar_placement) {
|
|
1018
|
-
has_initial_colorbar_placement = true
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// Update legend position (stable after initial placement unless responsive)
|
|
1024
|
-
if (legend_manual_position && !legend_is_dragging) {
|
|
1025
|
-
// Immediate update (no animation) for manually dragged positions
|
|
1026
|
-
tweened_legend_coords.set(legend_manual_position, { duration: 0 })
|
|
1027
|
-
} else if (active_legend_placement && !legend_is_dragging) {
|
|
1028
|
-
const is_responsive = legend?.responsive ?? false
|
|
1029
|
-
const should_update = dims_changed || (!legend_hover.is_locked.current &&
|
|
1030
|
-
(is_responsive || !has_initial_legend_placement))
|
|
1031
|
-
|
|
1032
|
-
if (should_update) {
|
|
1033
|
-
tweened_legend_coords.set(
|
|
1034
|
-
{ x: active_legend_placement.x, y: active_legend_placement.y },
|
|
1035
|
-
has_initial_legend_placement ? undefined : { duration: 0 },
|
|
1036
|
-
)
|
|
1037
|
-
if (legend_element) has_initial_legend_placement = true
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1001
|
+
// Tweened colorbar/legend coordinates with shared placement stability gating
|
|
1002
|
+
const colorbar_tween = create_placed_tween({
|
|
1003
|
+
placement: () => color_bar_placement,
|
|
1004
|
+
dims: () => ({ width, height }),
|
|
1005
|
+
responsive: () => color_bar?.responsive ?? false,
|
|
1006
|
+
element: () => colorbar_element,
|
|
1007
|
+
tween: () => color_bar?.tween,
|
|
1008
|
+
})
|
|
1009
|
+
const legend_tween = create_placed_tween({
|
|
1010
|
+
placement: () => active_legend_placement,
|
|
1011
|
+
dims: () => ({ width, height }),
|
|
1012
|
+
responsive: () => legend?.responsive ?? false,
|
|
1013
|
+
element: () => legend_element,
|
|
1014
|
+
tween: () => legend?.tween,
|
|
1015
|
+
// Leave coords alone mid-drag; once dragged, the manual position wins permanently
|
|
1016
|
+
suspended: () => legend_is_dragging,
|
|
1017
|
+
manual_position: () => legend_manual_position,
|
|
1040
1018
|
})
|
|
1041
1019
|
|
|
1042
1020
|
// Generate axis ticks - consolidated into single derived for efficiency
|
|
@@ -1061,15 +1039,16 @@
|
|
|
1061
1039
|
x_scale_for_ticks,
|
|
1062
1040
|
{ format: final_x_axis.format },
|
|
1063
1041
|
),
|
|
1064
|
-
x2:
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1042
|
+
x2:
|
|
1043
|
+
x2_points.length > 0
|
|
1044
|
+
? generate_ticks(
|
|
1045
|
+
[x2_min, x2_max],
|
|
1046
|
+
final_x2_axis.scale_type ?? `linear`,
|
|
1047
|
+
final_x2_axis.ticks,
|
|
1048
|
+
x2_scale_for_ticks,
|
|
1049
|
+
{ format: final_x2_axis.format },
|
|
1050
|
+
)
|
|
1051
|
+
: [],
|
|
1073
1052
|
y: generate_ticks(
|
|
1074
1053
|
[y_min, y_max],
|
|
1075
1054
|
final_y_axis.scale_type ?? `linear`,
|
|
@@ -1077,15 +1056,16 @@
|
|
|
1077
1056
|
y_scale_fn,
|
|
1078
1057
|
{ default_count: 5 },
|
|
1079
1058
|
),
|
|
1080
|
-
y2:
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1059
|
+
y2:
|
|
1060
|
+
y2_points.length > 0
|
|
1061
|
+
? generate_ticks(
|
|
1062
|
+
[y2_min, y2_max],
|
|
1063
|
+
final_y2_axis.scale_type ?? `linear`,
|
|
1064
|
+
final_y2_axis.ticks,
|
|
1065
|
+
y2_scale_fn,
|
|
1066
|
+
{ default_count: 5 },
|
|
1067
|
+
)
|
|
1068
|
+
: [],
|
|
1089
1069
|
}
|
|
1090
1070
|
})
|
|
1091
1071
|
|
|
@@ -1102,250 +1082,76 @@
|
|
|
1102
1082
|
y2_max: measure_max_tick_width(y2_tick_values, final_y2_axis.format ?? ``),
|
|
1103
1083
|
})
|
|
1104
1084
|
|
|
1105
|
-
//
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
// X2 axis: invert screen coords using x2 scale
|
|
1156
|
-
if (x2_points.length > 0) {
|
|
1157
|
-
const x2_a = to_epoch_num(x2_scale_fn.invert(drag_start_coords.x))
|
|
1158
|
-
const x2_b = to_epoch_num(x2_scale_fn.invert(drag_current_coords.x))
|
|
1159
|
-
x2_axis = { ...x2_axis, range: sorted_range(x2_a, x2_b) }
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// Y2 axis: when sync is enabled the y_axis effect derives y2; with sync 'none'
|
|
1163
|
-
// y2 must zoom from the rect directly (parity with BarPlot/Histogram/BoxPlot)
|
|
1164
|
-
if (y2_points.length > 0 && y2_sync_config.mode === `none`) {
|
|
1165
|
-
const y2_a = y2_scale_fn.invert(drag_start_coords.y)
|
|
1166
|
-
const y2_b = y2_scale_fn.invert(drag_current_coords.y)
|
|
1167
|
-
y2_axis = { ...y2_axis, range: sorted_range(y2_a, y2_b) }
|
|
1168
|
-
}
|
|
1085
|
+
// Shared pan/zoom/touch/drag-rect interaction controller. set_range routes y2
|
|
1086
|
+
// writes through get_synced_y2 (write-order contract: y is written before y2, so
|
|
1087
|
+
// the sync reads the just-updated y range).
|
|
1088
|
+
const pan_zoom = create_pan_zoom({
|
|
1089
|
+
ranges: () => ranges.current,
|
|
1090
|
+
scale_type: (axis) =>
|
|
1091
|
+
({ x: final_x_axis, x2: final_x2_axis, y: final_y_axis, y2: final_y2_axis })[axis]
|
|
1092
|
+
.scale_type,
|
|
1093
|
+
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
1094
|
+
plot_dims: () => ({
|
|
1095
|
+
width: Math.max(1, width - pad.l - pad.r),
|
|
1096
|
+
height: Math.max(1, height - pad.t - pad.b),
|
|
1097
|
+
}),
|
|
1098
|
+
pan: () => pan,
|
|
1099
|
+
set_range: (axis, range) => {
|
|
1100
|
+
if (axis === `y2`) ranges.current.y2 = get_synced_y2(ranges.current.y, range)
|
|
1101
|
+
else ranges.current[axis] = range
|
|
1102
|
+
},
|
|
1103
|
+
svg: () => svg_element,
|
|
1104
|
+
on_rect_zoom: (start, current) => {
|
|
1105
|
+
// Update axis ranges to trigger reactivity; both x and y must invert to valid
|
|
1106
|
+
// (finite, non-degenerate) ranges or the rect zoom is discarded entirely
|
|
1107
|
+
const next_x = invert_rect_range(x_scale_fn, start.x, current.x)
|
|
1108
|
+
const next_y = invert_rect_range(y_scale_fn, start.y, current.y)
|
|
1109
|
+
if (!next_x || !next_y) return
|
|
1110
|
+
x_axis = { ...x_axis, range: next_x }
|
|
1111
|
+
y_axis = { ...y_axis, range: next_y }
|
|
1112
|
+
|
|
1113
|
+
// X2 axis: invert screen coords using x2 scale
|
|
1114
|
+
const next_x2 =
|
|
1115
|
+
x2_points.length > 0 ? invert_rect_range(x2_scale_fn, start.x, current.x) : null
|
|
1116
|
+
if (next_x2) x2_axis = { ...x2_axis, range: next_x2 }
|
|
1117
|
+
|
|
1118
|
+
// Y2 axis: when sync is enabled the y_axis effect derives y2; with sync 'none'
|
|
1119
|
+
// y2 must zoom from the rect directly (parity with BarPlot/Histogram/BoxPlot)
|
|
1120
|
+
const next_y2 =
|
|
1121
|
+
y2_points.length > 0 && y2_sync_config.mode === `none`
|
|
1122
|
+
? invert_rect_range(y2_scale_fn, start.y, current.y)
|
|
1123
|
+
: null
|
|
1124
|
+
if (next_y2) y2_axis = { ...y2_axis, range: next_y2 }
|
|
1125
|
+
},
|
|
1126
|
+
on_reset: () => {
|
|
1127
|
+
// Reset to current auto ranges (not stale initial ranges which may have expanded)
|
|
1128
|
+
// This ensures lazy expansion restarts fresh from current data bounds
|
|
1129
|
+
ranges.initial = {
|
|
1130
|
+
x: [...auto_x_range] as Vec2,
|
|
1131
|
+
x2: [...auto_x2_range] as Vec2,
|
|
1132
|
+
y: [...auto_y_range] as Vec2,
|
|
1133
|
+
y2: [...auto_y2_range] as Vec2,
|
|
1169
1134
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
svg_bounding_box = null
|
|
1176
|
-
window.removeEventListener(`mousemove`, on_window_mouse_move)
|
|
1177
|
-
window.removeEventListener(`mouseup`, on_window_mouse_up)
|
|
1178
|
-
document.body.style.cursor = `default`
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
// Pan/zoom all four axes from an interaction-start snapshot, each in its own
|
|
1182
|
-
// scale's transform space (log axes pan by a constant factor, linear by a shift).
|
|
1183
|
-
// Plot dims clamped to 1px so degenerate containers can't produce Infinity deltas.
|
|
1184
|
-
const pan_all_axes = (init: InitialRanges, dx_px: number, dy_px: number) => {
|
|
1185
|
-
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
1186
|
-
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
1187
|
-
zoom_x_range = pan_range_by_pixels(init.initial_x_range, dx_px, plot_width, final_x_axis.scale_type)
|
|
1188
|
-
zoom_x2_range = pan_range_by_pixels(init.initial_x2_range, dx_px, plot_width, final_x2_axis.scale_type)
|
|
1189
|
-
zoom_y_range = pan_range_by_pixels(init.initial_y_range, dy_px, plot_height, final_y_axis.scale_type)
|
|
1190
|
-
zoom_y2_range = get_synced_y2(
|
|
1191
|
-
zoom_y_range,
|
|
1192
|
-
pan_range_by_pixels(init.initial_y2_range, dy_px, plot_height, final_y2_axis.scale_type),
|
|
1193
|
-
)
|
|
1194
|
-
}
|
|
1195
|
-
const zoom_all_axes = (init: InitialRanges, factor: number) => {
|
|
1196
|
-
zoom_x_range = zoom_range_by_factor(init.initial_x_range, factor, final_x_axis.scale_type)
|
|
1197
|
-
zoom_x2_range = zoom_range_by_factor(init.initial_x2_range, factor, final_x2_axis.scale_type)
|
|
1198
|
-
zoom_y_range = zoom_range_by_factor(init.initial_y_range, factor, final_y_axis.scale_type)
|
|
1199
|
-
zoom_y2_range = get_synced_y2(
|
|
1200
|
-
zoom_y_range,
|
|
1201
|
-
zoom_range_by_factor(init.initial_y2_range, factor, final_y2_axis.scale_type),
|
|
1202
|
-
)
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
// Pan drag handler (drag direction inverted on x for natural pan feel)
|
|
1206
|
-
const on_pan_move = (evt: MouseEvent) => {
|
|
1207
|
-
if (!pan_drag_state) return
|
|
1208
|
-
const sensitivity = pan?.drag_sensitivity ?? 1
|
|
1209
|
-
pan_all_axes(
|
|
1210
|
-
pan_drag_state,
|
|
1211
|
-
-(evt.clientX - pan_drag_state.start.x) * sensitivity,
|
|
1212
|
-
(evt.clientY - pan_drag_state.start.y) * sensitivity,
|
|
1213
|
-
)
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
const on_pan_end = () => {
|
|
1217
|
-
pan_drag_state = null
|
|
1218
|
-
document.body.style.cursor = ``
|
|
1219
|
-
window.removeEventListener(`mousemove`, on_pan_move)
|
|
1220
|
-
window.removeEventListener(`mouseup`, on_pan_end)
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// Tear down any window listeners + cursor override if the component unmounts mid-drag
|
|
1224
|
-
// (mouseup/panend would otherwise never fire, leaking listeners and a stuck cursor).
|
|
1225
|
-
// onDestroy also runs during SSR teardown, where window/document don't exist.
|
|
1226
|
-
onDestroy(() => {
|
|
1227
|
-
remove_drag_listeners([on_window_mouse_move, on_pan_move], [on_window_mouse_up, on_pan_end])
|
|
1228
|
-
drag_start_coords = null
|
|
1229
|
-
drag_current_coords = null
|
|
1230
|
-
svg_bounding_box = null
|
|
1231
|
-
pan_drag_state = null
|
|
1232
|
-
})
|
|
1233
|
-
|
|
1234
|
-
function handle_mouse_down(evt: MouseEvent) {
|
|
1235
|
-
if (!svg_element) return
|
|
1236
|
-
|
|
1237
|
-
// Check if pan is enabled and shift is held for pan mode
|
|
1238
|
-
const pan_enabled = pan?.enabled !== false
|
|
1239
|
-
if (pan_enabled && evt.shiftKey) {
|
|
1240
|
-
evt.preventDefault()
|
|
1241
|
-
pan_drag_state = {
|
|
1242
|
-
start: { x: evt.clientX, y: evt.clientY },
|
|
1243
|
-
initial_x_range: [...zoom_x_range] as [number, number],
|
|
1244
|
-
initial_x2_range: [...zoom_x2_range] as [number, number],
|
|
1245
|
-
initial_y_range: [...zoom_y_range] as [number, number],
|
|
1246
|
-
initial_y2_range: [...zoom_y2_range] as [number, number],
|
|
1135
|
+
ranges.current = {
|
|
1136
|
+
x: [...auto_x_range] as Vec2,
|
|
1137
|
+
x2: [...auto_x2_range] as Vec2,
|
|
1138
|
+
y: [...auto_y_range] as Vec2,
|
|
1139
|
+
y2: get_synced_y2(auto_y_range, [...auto_y2_range] as Vec2),
|
|
1247
1140
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
//
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
drag_start_coords = coords
|
|
1263
|
-
drag_current_coords = coords
|
|
1264
|
-
|
|
1265
|
-
window.addEventListener(`mousemove`, on_window_mouse_move)
|
|
1266
|
-
window.addEventListener(`mouseup`, on_window_mouse_up)
|
|
1267
|
-
document.body.style.cursor = `crosshair`
|
|
1268
|
-
evt.preventDefault()
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
// Wheel handler for pan (requires focus and shift)
|
|
1272
|
-
function handle_wheel(evt: WheelEvent) {
|
|
1273
|
-
const pan_enabled = pan?.enabled !== false
|
|
1274
|
-
// Only capture wheel when focused AND Shift is held
|
|
1275
|
-
// Use shift_held state (tracked via keydown/keyup) for compatibility with synthetic events
|
|
1276
|
-
if (!pan_enabled || !is_focused || !shift_held) return
|
|
1277
|
-
|
|
1278
|
-
evt.preventDefault()
|
|
1279
|
-
|
|
1280
|
-
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
1281
|
-
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
1282
|
-
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
1283
|
-
const sensitivity = pan?.wheel_sensitivity ?? 1
|
|
1284
|
-
|
|
1285
|
-
// Pan along the dominant wheel direction
|
|
1286
|
-
// (deltaX for horizontal scroll on trackpads, deltaY for vertical)
|
|
1287
|
-
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
1288
|
-
const dx = evt.deltaX * sensitivity
|
|
1289
|
-
zoom_x_range = pan_range_by_pixels(zoom_x_range, dx, plot_width, final_x_axis.scale_type)
|
|
1290
|
-
zoom_x2_range = pan_range_by_pixels(zoom_x2_range, dx, plot_width, final_x2_axis.scale_type)
|
|
1291
|
-
} else {
|
|
1292
|
-
const dy = evt.deltaY * sensitivity
|
|
1293
|
-
zoom_y_range = pan_range_by_pixels(zoom_y_range, dy, plot_height, final_y_axis.scale_type)
|
|
1294
|
-
zoom_y2_range = get_synced_y2(
|
|
1295
|
-
zoom_y_range,
|
|
1296
|
-
pan_range_by_pixels(zoom_y2_range, dy, plot_height, final_y2_axis.scale_type),
|
|
1297
|
-
)
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
// Touch handlers for pinch-zoom and two-finger pan
|
|
1302
|
-
function handle_touch_start(evt: TouchEvent) {
|
|
1303
|
-
const touch_enabled = pan?.enabled !== false && pan?.touch_enabled !== false
|
|
1304
|
-
if (!touch_enabled || evt.touches.length !== 2) return
|
|
1305
|
-
|
|
1306
|
-
evt.preventDefault()
|
|
1307
|
-
const touches = Array.from(evt.touches)
|
|
1308
|
-
touch_state = {
|
|
1309
|
-
start_touches: touches.map((touch) => ({ x: touch.clientX, y: touch.clientY })),
|
|
1310
|
-
initial_x_range: [...zoom_x_range] as [number, number],
|
|
1311
|
-
initial_x2_range: [...zoom_x2_range] as [number, number],
|
|
1312
|
-
initial_y_range: [...zoom_y_range] as [number, number],
|
|
1313
|
-
initial_y2_range: [...zoom_y2_range] as [number, number],
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
function handle_touch_move(evt: TouchEvent) {
|
|
1318
|
-
if (!touch_state || evt.touches.length !== 2) return
|
|
1319
|
-
evt.preventDefault()
|
|
1320
|
-
|
|
1321
|
-
const [t1, t2] = Array.from(evt.touches)
|
|
1322
|
-
const [s1, s2] = touch_state.start_touches
|
|
1323
|
-
|
|
1324
|
-
// Calculate center movement for pan
|
|
1325
|
-
const start_center = { x: (s1.x + s2.x) / 2, y: (s1.y + s2.y) / 2 }
|
|
1326
|
-
const curr_center = {
|
|
1327
|
-
x: (t1.clientX + t2.clientX) / 2,
|
|
1328
|
-
y: (t1.clientY + t2.clientY) / 2,
|
|
1329
|
-
}
|
|
1330
|
-
const dx = curr_center.x - start_center.x
|
|
1331
|
-
const dy = curr_center.y - start_center.y
|
|
1332
|
-
|
|
1333
|
-
// Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
|
|
1334
|
-
const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
|
|
1335
|
-
// ignore near-coincident touches so curr_dist / start_dist can't blow up the scale
|
|
1336
|
-
if (start_dist < MIN_TOUCH_DISTANCE_PIXELS) return
|
|
1337
|
-
const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
|
|
1338
|
-
const scale = curr_dist / start_dist
|
|
1339
|
-
|
|
1340
|
-
// Pinch zoom about the view center (spread = zoom in, pinch = zoom out)
|
|
1341
|
-
if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
|
|
1342
|
-
zoom_all_axes(touch_state, scale)
|
|
1343
|
-
} else pan_all_axes(touch_state, -dx, dy)
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
function handle_touch_end() {
|
|
1347
|
-
touch_state = null
|
|
1348
|
-
}
|
|
1141
|
+
// Also reset axis props so future data changes recalculate auto ranges
|
|
1142
|
+
x_axis = { ...x_axis, range: [null, null] }
|
|
1143
|
+
x2_axis = { ...x2_axis, range: [null, null] }
|
|
1144
|
+
y_axis = { ...y_axis, range: [null, null] }
|
|
1145
|
+
y2_axis = { ...y2_axis, range: [null, null] }
|
|
1146
|
+
},
|
|
1147
|
+
// Live tooltip while rect-dragging: update for the closest point inside the
|
|
1148
|
+
// plot bounds, clear when the cursor leaves the svg
|
|
1149
|
+
on_drag_move: (coords, inside_svg) => {
|
|
1150
|
+
if (inside_svg) update_tooltip_point(coords.x, coords.y)
|
|
1151
|
+
else tooltip_point = null
|
|
1152
|
+
},
|
|
1153
|
+
})
|
|
1154
|
+
onDestroy(() => pan_zoom.destroy())
|
|
1349
1155
|
|
|
1350
1156
|
// tooltip logic: find closest point and update tooltip state
|
|
1351
1157
|
function update_tooltip_point(x_rel: number, y_rel: number, evt?: MouseEvent) {
|
|
@@ -1369,9 +1175,7 @@
|
|
|
1369
1175
|
const point_cx = tooltip_is_time_x
|
|
1370
1176
|
? tooltip_x_scale(new Date(point.x))
|
|
1371
1177
|
: tooltip_x_scale(point.x)
|
|
1372
|
-
const point_cy = (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(
|
|
1373
|
-
point.y,
|
|
1374
|
-
)
|
|
1178
|
+
const point_cy = (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(point.y)
|
|
1375
1179
|
|
|
1376
1180
|
// Calculate squared screen distance between mouse and point
|
|
1377
1181
|
const screen_dx = x_rel - point_cx
|
|
@@ -1388,11 +1192,7 @@
|
|
|
1388
1192
|
}
|
|
1389
1193
|
|
|
1390
1194
|
// Check if the closest point is within the hover threshold
|
|
1391
|
-
if (
|
|
1392
|
-
closest_point &&
|
|
1393
|
-
closest_series &&
|
|
1394
|
-
min_screen_dist_sq <= hover_threshold_px_sq
|
|
1395
|
-
) {
|
|
1195
|
+
if (closest_point && closest_series && min_screen_dist_sq <= hover_threshold_px_sq) {
|
|
1396
1196
|
// Construct handler props synchronously to avoid stale derived reads
|
|
1397
1197
|
const props = construct_handler_props(closest_point)
|
|
1398
1198
|
tooltip_point = closest_point
|
|
@@ -1467,8 +1267,8 @@
|
|
|
1467
1267
|
const new_y = event.clientY - svg_rect.top - legend_drag_offset.y
|
|
1468
1268
|
|
|
1469
1269
|
// Get actual legend dimensions for accurate bounds checking using the bound element reference
|
|
1470
|
-
const { width: legend_width, height: legend_height } =
|
|
1471
|
-
.getBoundingClientRect()
|
|
1270
|
+
const { width: legend_width, height: legend_height } =
|
|
1271
|
+
legend_element.getBoundingClientRect()
|
|
1472
1272
|
|
|
1473
1273
|
// Constrain to plot bounds using measured legend size
|
|
1474
1274
|
const constrained_x = Math.max(0, Math.min(width - legend_width, new_x))
|
|
@@ -1477,7 +1277,7 @@
|
|
|
1477
1277
|
legend_manual_position = { x: constrained_x, y: constrained_y }
|
|
1478
1278
|
}
|
|
1479
1279
|
|
|
1480
|
-
function get_screen_coords(point: Point, data_series?: DataSeries):
|
|
1280
|
+
function get_screen_coords(point: Point, data_series?: DataSeries): Vec2 {
|
|
1481
1281
|
// convert data coordinates to potentially non-finite screen coordinates
|
|
1482
1282
|
const use_x2 = data_series?.x_axis === `x2`
|
|
1483
1283
|
const active_x_scale = use_x2 ? x2_scale_fn : x_scale_fn
|
|
@@ -1514,9 +1314,7 @@
|
|
|
1514
1314
|
const cx = handler_is_time_x ? handler_x_scale(new Date(x)) : handler_x_scale(x)
|
|
1515
1315
|
const cy = (hovered_series.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(y)
|
|
1516
1316
|
const active_x_config = handler_use_x2 ? final_x2_axis : final_x_axis
|
|
1517
|
-
const active_y_config = hovered_series.y_axis === `y2`
|
|
1518
|
-
? final_y2_axis
|
|
1519
|
-
: final_y_axis
|
|
1317
|
+
const active_y_config = hovered_series.y_axis === `y2` ? final_y2_axis : final_y_axis
|
|
1520
1318
|
const coords = {
|
|
1521
1319
|
x,
|
|
1522
1320
|
y,
|
|
@@ -1533,8 +1331,8 @@
|
|
|
1533
1331
|
metadata,
|
|
1534
1332
|
label: hovered_series.label ?? null,
|
|
1535
1333
|
series_idx,
|
|
1536
|
-
x_formatted:
|
|
1537
|
-
y_formatted:
|
|
1334
|
+
x_formatted: format_value_or_num(x, active_x_config.format),
|
|
1335
|
+
y_formatted: format_value_or_num(y, active_y_config.format),
|
|
1538
1336
|
color_value: color_value ?? null,
|
|
1539
1337
|
colorbar: {
|
|
1540
1338
|
value: color_value ?? null,
|
|
@@ -1556,9 +1354,7 @@
|
|
|
1556
1354
|
// Precompute non-click event names from point_events so we don't rebuild
|
|
1557
1355
|
// the entries array on every point render.
|
|
1558
1356
|
let point_event_names = $derived(
|
|
1559
|
-
point_events
|
|
1560
|
-
? Object.keys(point_events).filter((name) => name !== `onclick`)
|
|
1561
|
-
: [],
|
|
1357
|
+
point_events ? Object.keys(point_events).filter((name) => name !== `onclick`) : [],
|
|
1562
1358
|
)
|
|
1563
1359
|
|
|
1564
1360
|
// Set theme-aware background when entering fullscreen
|
|
@@ -1567,41 +1363,30 @@
|
|
|
1567
1363
|
})
|
|
1568
1364
|
|
|
1569
1365
|
// State accessors for shared axis change handler
|
|
1366
|
+
// Spread into existing state in each setter to preserve merged type structure
|
|
1570
1367
|
const axis_state: AxisChangeState<DataSeries<Metadata>> = {
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
},
|
|
1577
|
-
set_axis: (axis, config) => {
|
|
1578
|
-
// Spread into existing state to preserve merged type structure
|
|
1579
|
-
if (axis === `x`) x_axis = { ...x_axis, ...config }
|
|
1580
|
-
else if (axis === `x2`) x2_axis = { ...x2_axis, ...config }
|
|
1581
|
-
else if (axis === `y`) y_axis = { ...y_axis, ...config }
|
|
1582
|
-
else y2_axis = { ...y2_axis, ...config }
|
|
1368
|
+
axes: {
|
|
1369
|
+
x: { get: () => x_axis, set: (config) => (x_axis = { ...x_axis, ...config }) },
|
|
1370
|
+
x2: { get: () => x2_axis, set: (config) => (x2_axis = { ...x2_axis, ...config }) },
|
|
1371
|
+
y: { get: () => y_axis, set: (config) => (y_axis = { ...y_axis, ...config }) },
|
|
1372
|
+
y2: { get: () => y2_axis, set: (config) => (y2_axis = { ...y2_axis, ...config }) },
|
|
1583
1373
|
},
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
get_loading: () => axis_loading,
|
|
1587
|
-
set_loading: (axis) => (axis_loading = axis),
|
|
1374
|
+
series: { get: () => series, set: (next) => (series = next) },
|
|
1375
|
+
loading: { get: () => axis_loading, set: (axis) => (axis_loading = axis) },
|
|
1588
1376
|
}
|
|
1589
1377
|
|
|
1590
1378
|
// Shared handler + one-shot auto-load bound to this component's state
|
|
1591
|
-
const { handle_axis_change, try_auto_load } = create_axis_loader(
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1379
|
+
const { handle_axis_change, try_auto_load } = create_axis_loader(axis_state, () => ({
|
|
1380
|
+
data_loader,
|
|
1381
|
+
on_axis_change,
|
|
1382
|
+
on_error,
|
|
1383
|
+
}))
|
|
1595
1384
|
$effect(try_auto_load)
|
|
1596
1385
|
</script>
|
|
1597
1386
|
|
|
1598
1387
|
{#snippet fill_regions_layer(fills: typeof computed_fills)}
|
|
1599
1388
|
{#each fills as fill (fill.hover_key)}
|
|
1600
|
-
{#each fill.path_segments as
|
|
1601
|
-
path_d,
|
|
1602
|
-
segment_idx
|
|
1603
|
-
(`${fill.id ?? fill.idx}-${segment_idx}`)
|
|
1604
|
-
}
|
|
1389
|
+
{#each fill.path_segments as path_d, segment_idx (`${fill.id ?? fill.idx}-${segment_idx}`)}
|
|
1605
1390
|
<FillArea
|
|
1606
1391
|
region={fill}
|
|
1607
1392
|
region_idx={fill.idx}
|
|
@@ -1658,11 +1443,9 @@
|
|
|
1658
1443
|
evt.preventDefault()
|
|
1659
1444
|
fullscreen = false
|
|
1660
1445
|
}
|
|
1661
|
-
|
|
1662
|
-
}}
|
|
1663
|
-
onkeyup={(evt) => {
|
|
1664
|
-
if (evt.key === `Shift`) shift_held = false
|
|
1446
|
+
pan_zoom.on_window_key_down(evt)
|
|
1665
1447
|
}}
|
|
1448
|
+
onkeyup={pan_zoom.on_window_key_up}
|
|
1666
1449
|
/>
|
|
1667
1450
|
|
|
1668
1451
|
<div
|
|
@@ -1670,7 +1453,7 @@
|
|
|
1670
1453
|
bind:clientWidth={width}
|
|
1671
1454
|
bind:clientHeight={height}
|
|
1672
1455
|
{...rest}
|
|
1673
|
-
class=
|
|
1456
|
+
class={[`scatter`, rest.class]}
|
|
1674
1457
|
class:fullscreen
|
|
1675
1458
|
>
|
|
1676
1459
|
{#if width && height}
|
|
@@ -1686,49 +1469,30 @@
|
|
|
1686
1469
|
bind:this={svg_element}
|
|
1687
1470
|
role="application"
|
|
1688
1471
|
aria-label={rest[`aria-label`] ??
|
|
1689
|
-
|
|
1690
|
-
|
|
1472
|
+
([final_x_axis.label, final_y_axis.label].filter(Boolean).join(` vs `) ||
|
|
1473
|
+
`Scatter plot`)}
|
|
1691
1474
|
tabindex="0"
|
|
1692
|
-
onfocusin={() => (
|
|
1693
|
-
onfocusout={() => (
|
|
1475
|
+
onfocusin={() => pan_zoom.set_focused(true)}
|
|
1476
|
+
onfocusout={() => pan_zoom.set_focused(false)}
|
|
1694
1477
|
onmouseenter={() => (hovered = true)}
|
|
1695
|
-
onmousedown={
|
|
1478
|
+
onmousedown={pan_zoom.on_mouse_down}
|
|
1696
1479
|
onmousemove={(evt: MouseEvent) => {
|
|
1697
1480
|
// Only find closest point if not actively dragging
|
|
1698
|
-
if (!
|
|
1481
|
+
if (!pan_zoom.drag_start && !pan_zoom.is_pan_dragging) on_mouse_move(evt)
|
|
1699
1482
|
}}
|
|
1700
1483
|
onmouseleave={() => {
|
|
1701
1484
|
hovered = false
|
|
1702
1485
|
tooltip_point = null
|
|
1703
1486
|
on_point_hover?.(null)
|
|
1704
1487
|
}}
|
|
1705
|
-
ondblclick={
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
zoom_x2_range = [...auto_x2_range] as [number, number]
|
|
1714
|
-
zoom_y_range = [...auto_y_range] as [number, number]
|
|
1715
|
-
zoom_y2_range = get_synced_y2(auto_y_range, [...auto_y2_range] as Vec2)
|
|
1716
|
-
// Also reset axis props so future data changes recalculate auto ranges
|
|
1717
|
-
x_axis = { ...x_axis, range: [null, null] }
|
|
1718
|
-
x2_axis = { ...x2_axis, range: [null, null] }
|
|
1719
|
-
y_axis = { ...y_axis, range: [null, null] }
|
|
1720
|
-
y2_axis = { ...y2_axis, range: [null, null] }
|
|
1721
|
-
}}
|
|
1722
|
-
onwheel={handle_wheel}
|
|
1723
|
-
ontouchstart={handle_touch_start}
|
|
1724
|
-
ontouchmove={handle_touch_move}
|
|
1725
|
-
ontouchend={handle_touch_end}
|
|
1726
|
-
ontouchcancel={handle_touch_end}
|
|
1727
|
-
style:cursor={pan_drag_state
|
|
1728
|
-
? `grabbing`
|
|
1729
|
-
: shift_held && pan?.enabled !== false
|
|
1730
|
-
? `grab`
|
|
1731
|
-
: `crosshair`}
|
|
1488
|
+
ondblclick={pan_zoom.reset_view}
|
|
1489
|
+
onkeydown={pan_zoom.on_key_down}
|
|
1490
|
+
onwheel={pan_zoom.on_wheel}
|
|
1491
|
+
ontouchstart={pan_zoom.on_touch_start}
|
|
1492
|
+
ontouchmove={pan_zoom.on_touch_move}
|
|
1493
|
+
ontouchend={pan_zoom.on_touch_end}
|
|
1494
|
+
ontouchcancel={pan_zoom.on_touch_end}
|
|
1495
|
+
style:cursor={pan_zoom.cursor}
|
|
1732
1496
|
>
|
|
1733
1497
|
{@render user_content?.({
|
|
1734
1498
|
height,
|
|
@@ -1763,7 +1527,7 @@
|
|
|
1763
1527
|
domain={[x_min, x_max]}
|
|
1764
1528
|
tick_label={(tick) => get_tick_label(tick, final_x_axis.ticks)}
|
|
1765
1529
|
label_x={width / 2 + (final_x_axis.label_shift?.x ?? 0)}
|
|
1766
|
-
label_y={height - pad.b
|
|
1530
|
+
label_y={height - pad.b + AXIS_TITLE_OFFSET + (final_x_axis.label_shift?.y ?? 0)}
|
|
1767
1531
|
axis_loading={axis_loading === `x`}
|
|
1768
1532
|
on_axis_change={(key) => handle_axis_change(`x`, key)}
|
|
1769
1533
|
/>
|
|
@@ -1771,8 +1535,8 @@
|
|
|
1771
1535
|
<!-- Current frame indicator -->
|
|
1772
1536
|
{#if current_x_value != null}
|
|
1773
1537
|
{@const current_pos_raw = is_time_x
|
|
1774
|
-
|
|
1775
|
-
|
|
1538
|
+
? x_scale_fn(new Date(current_x_value))
|
|
1539
|
+
: x_scale_fn(current_x_value)}
|
|
1776
1540
|
{#if isFinite(current_pos_raw)}
|
|
1777
1541
|
{@const current_pos = current_pos_raw}
|
|
1778
1542
|
{#if current_pos >= pad.l && current_pos <= width - pad.r}
|
|
@@ -1806,7 +1570,8 @@
|
|
|
1806
1570
|
tick_label={(tick) => get_tick_label(tick, final_y_axis.ticks)}
|
|
1807
1571
|
label_x={Math.max(
|
|
1808
1572
|
12,
|
|
1809
|
-
pad.l -
|
|
1573
|
+
pad.l -
|
|
1574
|
+
(final_y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
|
|
1810
1575
|
LABEL_GAP_DEFAULT,
|
|
1811
1576
|
) + (final_y_axis.label_shift?.x ?? 0)}
|
|
1812
1577
|
label_y={pad.t + (height - pad.t - pad.b) / 2 + (final_y_axis.label_shift?.y ?? 0)}
|
|
@@ -1851,7 +1616,7 @@
|
|
|
1851
1616
|
domain={[x2_min, x2_max]}
|
|
1852
1617
|
tick_label={(tick) => get_tick_label(tick, final_x2_axis.ticks)}
|
|
1853
1618
|
label_x={width / 2 + (final_x2_axis.label_shift?.x ?? 0)}
|
|
1854
|
-
label_y={Math.max(12, pad.t - (final_x2_axis.label_shift?.y ??
|
|
1619
|
+
label_y={Math.max(12, pad.t - (final_x2_axis.label_shift?.y ?? AXIS_TITLE_OFFSET))}
|
|
1855
1620
|
axis_loading={axis_loading === `x2`}
|
|
1856
1621
|
on_axis_change={(key) => handle_axis_change(`x2`, key)}
|
|
1857
1622
|
/>
|
|
@@ -1859,7 +1624,7 @@
|
|
|
1859
1624
|
|
|
1860
1625
|
<!-- Tooltip rendered inside overlay (moved outside SVG for stacking above colorbar) -->
|
|
1861
1626
|
|
|
1862
|
-
<ZoomRect start={
|
|
1627
|
+
<ZoomRect start={pan_zoom.drag_start} current={pan_zoom.drag_current} />
|
|
1863
1628
|
|
|
1864
1629
|
<ZeroLines
|
|
1865
1630
|
display={final_display}
|
|
@@ -1867,10 +1632,10 @@
|
|
|
1867
1632
|
{x2_scale_fn}
|
|
1868
1633
|
{y_scale_fn}
|
|
1869
1634
|
{y2_scale_fn}
|
|
1870
|
-
x_range={
|
|
1871
|
-
x2_range={
|
|
1872
|
-
y_range={
|
|
1873
|
-
y2_range={
|
|
1635
|
+
x_range={ranges.current.x}
|
|
1636
|
+
x2_range={ranges.current.x2}
|
|
1637
|
+
y_range={ranges.current.y}
|
|
1638
|
+
y2_range={ranges.current.y2}
|
|
1874
1639
|
x_scale_type={final_x_axis.scale_type}
|
|
1875
1640
|
x2_scale_type={final_x2_axis.scale_type}
|
|
1876
1641
|
y_scale_type={final_y_axis.scale_type}
|
|
@@ -1909,30 +1674,32 @@
|
|
|
1909
1674
|
data-series-id={series_data._id}
|
|
1910
1675
|
clip-path="url(#{clip_path_id})"
|
|
1911
1676
|
opacity={hovered_legend_series_idx !== null &&
|
|
1912
|
-
|
|
1677
|
+
hovered_legend_series_idx !== series_data.orig_series_idx
|
|
1913
1678
|
? 0.25
|
|
1914
1679
|
: 1}
|
|
1915
1680
|
>
|
|
1916
|
-
{#if series_markers
|
|
1681
|
+
{#if series_markers.includes(`line`)}
|
|
1917
1682
|
{@const all_line_points = series_data.x.map((x, idx) => ({
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1683
|
+
x,
|
|
1684
|
+
y: series_data.y[idx],
|
|
1685
|
+
}))}
|
|
1921
1686
|
{@const finite_screen_points = all_line_points
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
{@const apply_line_controls =
|
|
1925
|
-
|
|
1926
|
-
|
|
1687
|
+
.map((point) => get_screen_coords(point, series_data))
|
|
1688
|
+
.filter(([sx, sy]) => isFinite(sx) && isFinite(sy))}
|
|
1689
|
+
{@const apply_line_controls =
|
|
1690
|
+
controls.show &&
|
|
1691
|
+
(!has_multiple_series ||
|
|
1692
|
+
series_data._id === series_with_ids[selected_series_idx]?._id)}
|
|
1927
1693
|
{@const ls = series_data.line_style}
|
|
1928
1694
|
{@const tc = (key: string) => apply_line_controls && touched.has(key)}
|
|
1929
|
-
{@const color_fallback =
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1695
|
+
{@const color_fallback =
|
|
1696
|
+
ls?.stroke ??
|
|
1697
|
+
(Array.isArray(series_data.point_style)
|
|
1698
|
+
? series_data.point_style[0]?.fill
|
|
1699
|
+
: series_data.point_style?.fill) ??
|
|
1700
|
+
(series_data.color_values?.[0] != null
|
|
1701
|
+
? color_scale_fn(series_data.color_values[0])
|
|
1702
|
+
: series_default_color)}
|
|
1936
1703
|
<Line
|
|
1937
1704
|
points={finite_screen_points}
|
|
1938
1705
|
origin={[
|
|
@@ -1940,8 +1707,11 @@
|
|
|
1940
1707
|
series_data.y_axis === `y2` ? y2_scale_fn(y2_min) : y_scale_fn(y_min),
|
|
1941
1708
|
]}
|
|
1942
1709
|
line_color={(tc(`line.color`) ? styles.line?.color : null) ?? color_fallback}
|
|
1943
|
-
line_width={(tc(`line.width`) ? styles.line?.width : null) ??
|
|
1710
|
+
line_width={(tc(`line.width`) ? styles.line?.width : null) ??
|
|
1711
|
+
ls?.stroke_width ??
|
|
1712
|
+
2}
|
|
1944
1713
|
line_dash={(tc(`line.dash`) ? styles.line?.dash : null) ?? ls?.line_dash}
|
|
1714
|
+
curve={ls?.curve}
|
|
1945
1715
|
area_color="transparent"
|
|
1946
1716
|
line_tween={effective_line_tween}
|
|
1947
1717
|
/>
|
|
@@ -1962,53 +1732,60 @@
|
|
|
1962
1732
|
{@const series_default_color = get_series_color(series_data.orig_series_idx ?? 0)}
|
|
1963
1733
|
{@const series_default_symbol = get_series_symbol(series_data.orig_series_idx ?? 0)}
|
|
1964
1734
|
<g data-series-id={series_data._id}>
|
|
1965
|
-
{#if series_markers
|
|
1966
|
-
{#each series_data.filtered_data as
|
|
1967
|
-
point
|
|
1968
|
-
(`${point.series_idx}-${point.point_idx}`)
|
|
1969
|
-
}
|
|
1735
|
+
{#if series_markers.includes(`points`)}
|
|
1736
|
+
{#each series_data.filtered_data as point (`${point.series_idx}-${point.point_idx}`)}
|
|
1970
1737
|
{@const label_id = `${point.series_idx}-${point.point_idx}`}
|
|
1971
1738
|
{@const calculated_label_pos = label_positions[label_id]}
|
|
1972
1739
|
{@const point_label = point.point_label ?? {}}
|
|
1973
|
-
{@const label_style =
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1740
|
+
{@const label_style =
|
|
1741
|
+
point_label.auto_placement &&
|
|
1742
|
+
actual_label_config.max_neighbors &&
|
|
1743
|
+
!calculated_label_pos
|
|
1744
|
+
? {}
|
|
1745
|
+
: point_label}
|
|
1978
1746
|
{@const final_label = calculated_label_pos
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1747
|
+
? {
|
|
1748
|
+
...label_style,
|
|
1749
|
+
offset: {
|
|
1750
|
+
x:
|
|
1751
|
+
calculated_label_pos.x -
|
|
1752
|
+
(is_time_x ? x_scale_fn(new Date(point.x)) : x_scale_fn(point.x)),
|
|
1753
|
+
y:
|
|
1754
|
+
calculated_label_pos.y -
|
|
1755
|
+
(series_data.y_axis === `y2`
|
|
1756
|
+
? y2_scale_fn(point.y)
|
|
1757
|
+
: y_scale_fn(point.y)),
|
|
1758
|
+
},
|
|
1759
|
+
}
|
|
1760
|
+
: label_style}
|
|
1990
1761
|
{@const [raw_screen_x, raw_screen_y] = get_screen_coords(point, series_data)}
|
|
1991
|
-
{@const screen_x = isFinite(raw_screen_x)
|
|
1762
|
+
{@const screen_x = isFinite(raw_screen_x)
|
|
1763
|
+
? raw_screen_x
|
|
1764
|
+
: x_scale_fn.range()[0]}
|
|
1992
1765
|
{@const screen_y = isFinite(raw_screen_y)
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
{@const apply_controls =
|
|
1996
|
-
|
|
1997
|
-
|
|
1766
|
+
? raw_screen_y
|
|
1767
|
+
: (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn).range()[0]}
|
|
1768
|
+
{@const apply_controls =
|
|
1769
|
+
controls.show &&
|
|
1770
|
+
(!has_multiple_series ||
|
|
1771
|
+
series_data._id === series_with_ids[selected_series_idx]?._id)}
|
|
1998
1772
|
{@const pt = point.point_style}
|
|
1999
1773
|
{@const tc = (key: string) => apply_controls && touched.has(key)}
|
|
2000
|
-
{@const computed_radius =
|
|
2001
|
-
|
|
2002
|
-
|
|
1774
|
+
{@const computed_radius =
|
|
1775
|
+
point.size_value != null
|
|
1776
|
+
? size_scale_fn(point.size_value)
|
|
1777
|
+
: ((tc(`point.size`) ? styles.point?.size : null) ??
|
|
1778
|
+
pt?.radius ??
|
|
1779
|
+
default_point_radius(series_markers))}
|
|
2003
1780
|
<ScatterPoint
|
|
2004
1781
|
x={screen_x}
|
|
2005
1782
|
y={screen_y}
|
|
2006
1783
|
is_dimmed={hovered_legend_series_idx !== null &&
|
|
2007
1784
|
hovered_legend_series_idx !== point.series_idx}
|
|
2008
1785
|
is_hovered={tooltip_point?.series_idx === point.series_idx &&
|
|
2009
|
-
|
|
1786
|
+
tooltip_point?.point_idx === point.point_idx}
|
|
2010
1787
|
is_selected={selected_point?.series_idx === point.series_idx &&
|
|
2011
|
-
|
|
1788
|
+
selected_point?.point_idx === point.point_idx}
|
|
2012
1789
|
leader_line_threshold={actual_label_config.leader_line_threshold}
|
|
2013
1790
|
style={{
|
|
2014
1791
|
symbol_type: pt?.symbol_type ?? series_default_symbol,
|
|
@@ -2016,18 +1793,21 @@
|
|
|
2016
1793
|
radius: computed_radius,
|
|
2017
1794
|
stroke_width:
|
|
2018
1795
|
(tc(`point.stroke_width`) ? styles.point?.stroke_width : null) ??
|
|
2019
|
-
|
|
1796
|
+
pt?.stroke_width ??
|
|
1797
|
+
1,
|
|
2020
1798
|
stroke:
|
|
2021
1799
|
(tc(`point.stroke_color`) ? styles.point?.stroke_color : null) ??
|
|
2022
|
-
|
|
1800
|
+
pt?.stroke ??
|
|
1801
|
+
`#000`,
|
|
2023
1802
|
stroke_opacity:
|
|
2024
1803
|
(tc(`point.stroke_opacity`) ? styles.point?.stroke_opacity : null) ??
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
1804
|
+
pt?.stroke_opacity ??
|
|
1805
|
+
1,
|
|
1806
|
+
fill_opacity:
|
|
1807
|
+
(tc(`point.opacity`) ? styles.point?.opacity : null) ??
|
|
1808
|
+
pt?.fill_opacity ??
|
|
1809
|
+
1,
|
|
1810
|
+
cursor: on_point_click || point_events?.onclick ? `pointer` : undefined,
|
|
2031
1811
|
}}
|
|
2032
1812
|
hover={point.point_hover ?? {}}
|
|
2033
1813
|
label={final_label}
|
|
@@ -2035,15 +1815,17 @@
|
|
|
2035
1815
|
{point_tween}
|
|
2036
1816
|
origin={{ x: plot_center_x, y: plot_center_y }}
|
|
2037
1817
|
--point-fill-color={point.color_value != null
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
1818
|
+
? color_scale_fn(point.color_value)
|
|
1819
|
+
: ((tc(`point.color`) ? styles.point?.color : null) ??
|
|
1820
|
+
pt?.fill ??
|
|
1821
|
+
series_default_color)}
|
|
2041
1822
|
{...point_events &&
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
1823
|
+
Object.fromEntries(
|
|
1824
|
+
point_event_names.map((name) => [
|
|
1825
|
+
name,
|
|
1826
|
+
(event: Event) => point_events?.[name]?.({ point, event }),
|
|
1827
|
+
]),
|
|
1828
|
+
)}
|
|
2047
1829
|
onclick={(event: MouseEvent) => {
|
|
2048
1830
|
// Call user-provided onclick handler first if it exists
|
|
2049
1831
|
point_events?.onclick?.({ point, event })
|
|
@@ -2063,16 +1845,33 @@
|
|
|
2063
1845
|
{@render fill_regions_layer(fills_by_z.above_all)}
|
|
2064
1846
|
<!-- Reference lines: above all -->
|
|
2065
1847
|
{@render ref_lines_layer(ref_lines_by_z.above_all)}
|
|
1848
|
+
|
|
1849
|
+
<!-- Marginal distribution strips -->
|
|
1850
|
+
<PlotMarginals
|
|
1851
|
+
marginals={resolved_marginals}
|
|
1852
|
+
series={marginal_series}
|
|
1853
|
+
{width}
|
|
1854
|
+
{height}
|
|
1855
|
+
{pad}
|
|
1856
|
+
has_axis={marginal_has_axis}
|
|
1857
|
+
axes={{
|
|
1858
|
+
x1: marginal_axis(x_scale_fn, [x_min, x_max], final_x_axis),
|
|
1859
|
+
x2: marginal_axis(x2_scale_fn, [x2_min, x2_max], final_x2_axis),
|
|
1860
|
+
y1: marginal_axis(y_scale_fn, [y_min, y_max], final_y_axis),
|
|
1861
|
+
y2: marginal_axis(y2_scale_fn, [y2_min, y2_max], final_y2_axis),
|
|
1862
|
+
}}
|
|
1863
|
+
id={component_id}
|
|
1864
|
+
/>
|
|
2066
1865
|
</svg>
|
|
2067
1866
|
|
|
2068
1867
|
<!-- Tooltip overlay above all plot overlays (legend, colorbar) -->
|
|
2069
1868
|
{#if handler_props && hovered && tooltip_point}
|
|
2070
1869
|
{@const { point_label, series_idx } = tooltip_point}
|
|
2071
1870
|
{@const tooltip_bg_color = pick_tooltip_bg(
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
1871
|
+
tooltip_point,
|
|
1872
|
+
series_with_ids[series_idx],
|
|
1873
|
+
color_scale_fn,
|
|
1874
|
+
)}
|
|
2076
1875
|
<PlotTooltip
|
|
2077
1876
|
x={handler_props.cx}
|
|
2078
1877
|
y={handler_props.cy}
|
|
@@ -2090,9 +1889,10 @@
|
|
|
2090
1889
|
{@html sanitize_html(hp.x_axis.label || `x`)}: {hp.x_formatted}<br />
|
|
2091
1890
|
{@html sanitize_html(hp.y_axis.label || `y`)}: {hp.y_formatted}
|
|
2092
1891
|
{#if hp.colorbar?.value != null}
|
|
2093
|
-
<br />{@html sanitize_html(hp.colorbar.title || `Color`)}: {
|
|
2094
|
-
|
|
2095
|
-
|
|
1892
|
+
<br />{@html sanitize_html(hp.colorbar.title || `Color`)}: {format_value(
|
|
1893
|
+
hp.colorbar.value,
|
|
1894
|
+
hp.colorbar.tick_format || `.3~g`,
|
|
1895
|
+
)}
|
|
2096
1896
|
{/if}
|
|
2097
1897
|
{/if}
|
|
2098
1898
|
</PlotTooltip>
|
|
@@ -2103,10 +1903,9 @@
|
|
|
2103
1903
|
<ScatterPlotControls
|
|
2104
1904
|
toggle_props={{
|
|
2105
1905
|
...controls.toggle_props,
|
|
2106
|
-
style:
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
}`,
|
|
1906
|
+
style: `--ctrl-btn-right: var(--fullscreen-btn-offset, 30px); top: var(--ctrl-btn-top, 5pt); ${
|
|
1907
|
+
controls.toggle_props?.style ?? ``
|
|
1908
|
+
}`,
|
|
2110
1909
|
}}
|
|
2111
1910
|
pane_props={controls.pane_props}
|
|
2112
1911
|
bind:x_axis
|
|
@@ -2131,22 +1930,23 @@
|
|
|
2131
1930
|
<!-- Color Bar -->
|
|
2132
1931
|
{#if color_bar && all_color_values.length > 0 && color_bar_placement}
|
|
2133
1932
|
{@const color_domain = [
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
1933
|
+
(typeof color_scale === `string` ? undefined : color_scale.value_range)?.[0] ??
|
|
1934
|
+
auto_color_range[0],
|
|
1935
|
+
(typeof color_scale === `string` ? undefined : color_scale.value_range)?.[1] ??
|
|
1936
|
+
auto_color_range[1],
|
|
1937
|
+
] as Vec2}
|
|
2139
1938
|
<div
|
|
2140
1939
|
bind:this={colorbar_element}
|
|
2141
|
-
onmouseenter={() =>
|
|
2142
|
-
onmouseleave={() =>
|
|
1940
|
+
onmouseenter={() => colorbar_tween.set_locked(true)}
|
|
1941
|
+
onmouseleave={() => colorbar_tween.set_locked(false)}
|
|
2143
1942
|
class="colorbar-wrapper"
|
|
2144
1943
|
role="img"
|
|
2145
1944
|
aria-label="Color scale legend"
|
|
2146
1945
|
style={`${
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
`position: absolute; left: ${
|
|
1946
|
+
// explicit wrapper_style or auto-outside places the colorbar; else auto-placement coords
|
|
1947
|
+
effective_cbar_wrapper_style ??
|
|
1948
|
+
`position: absolute; left: ${colorbar_tween.coords.current.x}px; top: ${colorbar_tween.coords.current.y}px`
|
|
1949
|
+
}; pointer-events: auto;`}
|
|
2150
1950
|
>
|
|
2151
1951
|
<ColorBar
|
|
2152
1952
|
tick_labels={4}
|
|
@@ -2155,7 +1955,8 @@
|
|
|
2155
1955
|
color_scale_domain={color_domain}
|
|
2156
1956
|
scale_type={typeof color_scale === `string` ? undefined : color_scale.type}
|
|
2157
1957
|
range={color_domain?.every((val) => val != null) ? color_domain : undefined}
|
|
2158
|
-
bar_style="width:
|
|
1958
|
+
bar_style="width: {COLOR_BAR_DEFAULTS.width}px; height: {COLOR_BAR_DEFAULTS.horizontal_bar_height}px; {color_bar?.style ??
|
|
1959
|
+
``}"
|
|
2159
1960
|
{...color_bar}
|
|
2160
1961
|
wrapper_style={effective_cbar_wrapper_style ? `height: 100%; width: 100%;` : ``}
|
|
2161
1962
|
/>
|
|
@@ -2164,31 +1965,32 @@
|
|
|
2164
1965
|
|
|
2165
1966
|
<!-- Legend -->
|
|
2166
1967
|
<!-- Only render if multiple series or if legend prop was explicitly provided by user (even if empty object) -->
|
|
2167
|
-
{#if legend != null && legend_data.length > 0 &&
|
|
2168
|
-
(legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)}
|
|
1968
|
+
{#if legend != null && legend_data.length > 0 && (legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)}
|
|
2169
1969
|
{@const default_x = pad.l + 10}
|
|
2170
1970
|
{@const default_y = pad.t + 10}
|
|
2171
|
-
{@const current_x =
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
1971
|
+
{@const current_x =
|
|
1972
|
+
legend_is_dragging && legend_manual_position
|
|
1973
|
+
? legend_manual_position.x
|
|
1974
|
+
: legend_auto_outside
|
|
1975
|
+
? legend_outside_x
|
|
1976
|
+
: legend_placement
|
|
1977
|
+
? legend_tween.coords.current.x
|
|
1978
|
+
: default_x}
|
|
1979
|
+
{@const current_y =
|
|
1980
|
+
legend_is_dragging && legend_manual_position
|
|
1981
|
+
? legend_manual_position.y
|
|
1982
|
+
: legend_auto_outside
|
|
1983
|
+
? legend_outside_y
|
|
1984
|
+
: legend_placement
|
|
1985
|
+
? legend_tween.coords.current.y
|
|
1986
|
+
: default_y}
|
|
2185
1987
|
<PlotLegend
|
|
2186
1988
|
bind:root_element={legend_element}
|
|
2187
1989
|
series_data={legend_data}
|
|
2188
1990
|
on_drag_start={handle_legend_drag_start}
|
|
2189
1991
|
on_drag={handle_legend_drag}
|
|
2190
1992
|
on_drag_end={() => (legend_is_dragging = false)}
|
|
2191
|
-
on_hover_change={
|
|
1993
|
+
on_hover_change={legend_tween.set_locked}
|
|
2192
1994
|
on_item_hover={(item) => {
|
|
2193
1995
|
if (item?.item_type === `fill`) {
|
|
2194
1996
|
// highlight the matching fill in the plot (same state plot fill-hover uses), but skip
|
|
@@ -2197,15 +1999,14 @@
|
|
|
2197
1999
|
hovered_fill_key = fill && fill.visible !== false ? fill.hover_key : null
|
|
2198
2000
|
hovered_legend_series_idx = null
|
|
2199
2001
|
} else {
|
|
2200
|
-
hovered_legend_series_idx =
|
|
2201
|
-
? item.series_idx
|
|
2202
|
-
: null
|
|
2002
|
+
hovered_legend_series_idx =
|
|
2003
|
+
item != null && item.series_idx >= 0 ? item.series_idx : null
|
|
2203
2004
|
hovered_fill_key = null
|
|
2204
2005
|
}
|
|
2205
2006
|
}}
|
|
2206
2007
|
active_series_idx={tooltip_point?.series_idx ?? hovered_legend_series_idx}
|
|
2207
|
-
active_fill_idx={computed_fills.find((fill) => fill.hover_key === hovered_fill_key)
|
|
2208
|
-
null}
|
|
2008
|
+
active_fill_idx={computed_fills.find((fill) => fill.hover_key === hovered_fill_key)
|
|
2009
|
+
?.idx ?? null}
|
|
2209
2010
|
draggable={legend?.draggable ?? true}
|
|
2210
2011
|
{...legend}
|
|
2211
2012
|
on_toggle={legend?.on_toggle ?? legend_vis.on_toggle}
|
|
@@ -2217,7 +2018,7 @@
|
|
|
2217
2018
|
fill_regions = fill_regions.map((region, idx) =>
|
|
2218
2019
|
idx === source_idx
|
|
2219
2020
|
? { ...region, visible: !(region.visible !== false) }
|
|
2220
|
-
: region
|
|
2021
|
+
: region,
|
|
2221
2022
|
)
|
|
2222
2023
|
}
|
|
2223
2024
|
}}
|
|
@@ -2228,7 +2029,9 @@
|
|
|
2228
2029
|
// Only fill_regions can be toggled (error_bands are not bindable)
|
|
2229
2030
|
if (source_type !== `fill_region`) return
|
|
2230
2031
|
// Toggle: if only this fill is visible, show all; otherwise show only this one
|
|
2231
|
-
const visible_count = fill_regions.filter(
|
|
2032
|
+
const visible_count = fill_regions.filter(
|
|
2033
|
+
(region) => region.visible !== false,
|
|
2034
|
+
).length
|
|
2232
2035
|
const this_visible = fill_regions[source_idx]?.visible !== false
|
|
2233
2036
|
if (visible_count === 1 && this_visible) {
|
|
2234
2037
|
// Show all fills
|
|
@@ -2314,7 +2117,9 @@
|
|
|
2314
2117
|
div.scatter :global(.pane-toggle),
|
|
2315
2118
|
div.scatter .header-controls {
|
|
2316
2119
|
opacity: 0;
|
|
2317
|
-
transition:
|
|
2120
|
+
transition:
|
|
2121
|
+
opacity 0.2s,
|
|
2122
|
+
background-color 0.2s;
|
|
2318
2123
|
}
|
|
2319
2124
|
div.scatter:hover :global(.pane-toggle),
|
|
2320
2125
|
div.scatter:hover .header-controls,
|
|
@@ -2341,10 +2146,7 @@
|
|
|
2341
2146
|
color: var(--text-color);
|
|
2342
2147
|
white-space: nowrap;
|
|
2343
2148
|
/* Use line-height to center text vertically without flexbox */
|
|
2344
|
-
line-height: var(
|
|
2345
|
-
--scatter-axis-label-line-height,
|
|
2346
|
-
20px
|
|
2347
|
-
); /* Match foreignObject height */
|
|
2149
|
+
line-height: var(--scatter-axis-label-line-height, 20px); /* Match foreignObject height */
|
|
2348
2150
|
display: block;
|
|
2349
2151
|
}
|
|
2350
2152
|
.current-frame-indicator {
|