matterviz 0.3.7 → 0.4.1
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/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +1 -1
- package/dist/api/optimade.js +32 -26
- package/dist/app.css +0 -3
- package/dist/brillouin/BrillouinZone.svelte +76 -148
- package/dist/brillouin/BrillouinZone.svelte.d.ts +6 -14
- package/dist/brillouin/BrillouinZoneExportPane.svelte +43 -96
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +9 -32
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
- package/dist/brillouin/BrillouinZoneScene.svelte +97 -205
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +4 -23
- package/dist/brillouin/BrillouinZoneTooltip.svelte +16 -25
- 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 +89 -90
- 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 +14 -13
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +109 -203
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +180 -470
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
- package/dist/chempot-diagram/async-compute.svelte.js +3 -1
- package/dist/chempot-diagram/chempot-worker.js +2 -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 +4 -4
- package/dist/chempot-diagram/compute.js +20 -20
- 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.js +8 -7
- package/dist/composition/FormulaFilter.svelte +18 -11
- package/dist/composition/PieChart.svelte +11 -10
- package/dist/composition/chem-sys.d.ts +8 -0
- package/dist/composition/chem-sys.js +86 -0
- package/dist/composition/format.js +7 -4
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/composition/parse.d.ts +0 -1
- package/dist/composition/parse.js +41 -31
- package/dist/controls.d.ts +1 -0
- package/dist/controls.js +0 -1
- package/dist/convex-hull/ConvexHull.svelte +8 -10
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
- package/dist/convex-hull/ConvexHull2D.svelte +106 -185
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +179 -683
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +183 -687
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +268 -0
- package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
- package/dist/convex-hull/ConvexHullControls.svelte +88 -7
- 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 +36 -175
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +11 -2
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
- package/dist/convex-hull/GasPressureControls.svelte +4 -4
- package/dist/convex-hull/TemperatureSlider.svelte +2 -2
- 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 +278 -0
- package/dist/convex-hull/demo-temperature.d.ts +1 -1
- package/dist/convex-hull/demo-temperature.js +20 -22
- package/dist/convex-hull/gas-thermodynamics.d.ts +2 -2
- package/dist/convex-hull/gas-thermodynamics.js +22 -30
- package/dist/convex-hull/helpers.d.ts +42 -7
- package/dist/convex-hull/helpers.js +171 -78
- 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 +10 -8
- package/dist/convex-hull/index.js +7 -2
- package/dist/convex-hull/thermodynamics.js +136 -960
- package/dist/convex-hull/types.d.ts +13 -5
- package/dist/convex-hull/types.js +12 -0
- package/dist/coordination/CoordinationBarPlot.svelte +27 -34
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/element/BohrAtom.svelte +2 -1
- package/dist/element/index.d.ts +4 -0
- package/dist/element/index.js +18 -0
- package/dist/feedback/DragOverlay.svelte +3 -1
- package/dist/feedback/DragOverlay.svelte.d.ts +1 -0
- package/dist/feedback/StatusMessage.svelte +13 -3
- package/dist/fermi-surface/FermiSlice.svelte +13 -5
- package/dist/fermi-surface/FermiSurface.svelte +78 -151
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -14
- package/dist/fermi-surface/FermiSurfaceControls.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +72 -221
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +8 -34
- package/dist/fermi-surface/compute.js +67 -66
- package/dist/fermi-surface/export.js +6 -16
- 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 +71 -79
- package/dist/fermi-surface/types.d.ts +3 -2
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +69 -52
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +4 -3
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +3 -2
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
- package/dist/heatmap-matrix/index.d.ts +3 -2
- package/dist/heatmap-matrix/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/io/ExportPane.svelte +166 -0
- package/dist/io/ExportPane.svelte.d.ts +17 -0
- package/dist/io/decompress.js +5 -4
- package/dist/io/export.d.ts +9 -5
- package/dist/io/export.js +77 -51
- package/dist/io/fetch.d.ts +2 -1
- package/dist/io/fetch.js +5 -1
- package/dist/io/file-drop.d.ts +8 -1
- package/dist/io/file-drop.js +48 -36
- package/dist/io/index.d.ts +2 -0
- package/dist/io/index.js +10 -0
- package/dist/io/types.d.ts +13 -0
- package/dist/io/url-drop.js +64 -33
- package/dist/isosurface/parse.js +52 -51
- package/dist/isosurface/slice.js +5 -4
- package/dist/isosurface/types.js +1 -1
- package/dist/keyboard.d.ts +3 -0
- package/dist/keyboard.js +23 -0
- package/dist/labels.d.ts +1 -1
- package/dist/labels.js +9 -8
- package/dist/layout/FullscreenButton.svelte +33 -0
- package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
- package/dist/layout/FullscreenToggle.svelte +8 -14
- package/dist/layout/PropertyFilter.svelte +3 -2
- package/dist/layout/SettingsSection.svelte +1 -1
- package/dist/layout/ViewerChrome.svelte +116 -0
- package/dist/layout/ViewerChrome.svelte.d.ts +17 -0
- package/dist/layout/fullscreen.d.ts +4 -0
- package/dist/layout/fullscreen.svelte.d.ts +8 -0
- package/dist/layout/fullscreen.svelte.js +37 -0
- package/dist/layout/index.d.ts +3 -0
- package/dist/layout/index.js +3 -0
- package/dist/layout/json-tree/JsonNode.svelte +1 -1
- package/dist/layout/json-tree/JsonTree.svelte +2 -2
- package/dist/layout/json-tree/utils.js +5 -4
- package/dist/marching-cubes.js +8 -13
- package/dist/math.d.ts +12 -4
- package/dist/math.js +42 -30
- package/dist/overlays/DraggablePane.svelte +4 -4
- package/dist/overlays/index.d.ts +4 -0
- package/dist/periodic-table/PeriodicTable.svelte +27 -15
- package/dist/periodic-table/PropertySelect.svelte +1 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +9 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +3 -2
- 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 +47 -132
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +1 -1
- package/dist/phase-diagram/build-diagram.js +2 -2
- package/dist/phase-diagram/colors.js +1 -1
- package/dist/phase-diagram/parse.d.ts +2 -1
- package/dist/phase-diagram/parse.js +6 -5
- package/dist/phase-diagram/types.d.ts +1 -1
- package/dist/phase-diagram/utils.d.ts +3 -3
- package/dist/phase-diagram/utils.js +8 -12
- package/dist/plot/{BarPlot.svelte → bar/BarPlot.svelte} +246 -841
- package/dist/plot/{BarPlot.svelte.d.ts → bar/BarPlot.svelte.d.ts} +8 -16
- package/dist/plot/{BarPlotControls.svelte → bar/BarPlotControls.svelte} +6 -5
- package/dist/plot/{BarPlotControls.svelte.d.ts → bar/BarPlotControls.svelte.d.ts} +3 -3
- package/dist/plot/{SpacegroupBarPlot.svelte → bar/SpacegroupBarPlot.svelte} +8 -7
- package/dist/plot/{SpacegroupBarPlot.svelte.d.ts → bar/SpacegroupBarPlot.svelte.d.ts} +1 -1
- package/dist/plot/bar/data.d.ts +40 -0
- package/dist/plot/bar/data.js +154 -0
- package/dist/plot/bar/geometry.d.ts +39 -0
- package/dist/plot/bar/geometry.js +60 -0
- package/dist/plot/bar/index.d.ts +3 -0
- package/dist/plot/bar/index.js +3 -0
- package/dist/plot/box/BoxPlot.svelte +1292 -0
- package/dist/plot/box/BoxPlot.svelte.d.ts +95 -0
- package/dist/plot/box/BoxPlotControls.svelte +109 -0
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
- package/dist/plot/box/Violin.svelte +14 -0
- package/dist/plot/box/Violin.svelte.d.ts +70 -0
- package/dist/plot/box/box-plot.d.ts +56 -0
- package/dist/plot/box/box-plot.js +129 -0
- package/dist/plot/box/index.d.ts +5 -0
- package/dist/plot/box/index.js +5 -0
- package/dist/plot/box/kde.d.ts +17 -0
- package/dist/plot/box/kde.js +160 -0
- package/dist/plot/box/quantile.d.ts +3 -0
- package/dist/plot/box/quantile.js +53 -0
- package/dist/plot/{auto-place.d.ts → core/auto-place.d.ts} +1 -1
- package/dist/plot/{auto-place.js → core/auto-place.js} +6 -3
- package/dist/plot/core/axis-utils.d.ts +46 -0
- package/dist/plot/core/axis-utils.js +110 -0
- package/dist/plot/{AxisLabel.svelte → core/components/AxisLabel.svelte} +2 -2
- package/dist/plot/{AxisLabel.svelte.d.ts → core/components/AxisLabel.svelte.d.ts} +1 -1
- package/dist/plot/{ColorBar.svelte → core/components/ColorBar.svelte} +41 -38
- package/dist/plot/{ColorBar.svelte.d.ts → core/components/ColorBar.svelte.d.ts} +7 -6
- package/dist/plot/{ColorScaleSelect.svelte → core/components/ColorScaleSelect.svelte} +4 -3
- package/dist/plot/{ColorScaleSelect.svelte.d.ts → core/components/ColorScaleSelect.svelte.d.ts} +2 -2
- package/dist/plot/core/components/ControlPane.svelte +46 -0
- package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
- package/dist/plot/{FillArea.svelte → core/components/FillArea.svelte} +17 -6
- package/dist/plot/{FillArea.svelte.d.ts → core/components/FillArea.svelte.d.ts} +1 -1
- package/dist/plot/{InteractiveAxisLabel.svelte → core/components/InteractiveAxisLabel.svelte} +3 -3
- package/dist/plot/{InteractiveAxisLabel.svelte.d.ts → core/components/InteractiveAxisLabel.svelte.d.ts} +2 -2
- package/dist/plot/{Line.svelte → core/components/Line.svelte} +33 -15
- package/dist/plot/{Line.svelte.d.ts → core/components/Line.svelte.d.ts} +3 -2
- package/dist/plot/{PlotAxis.svelte → core/components/PlotAxis.svelte} +9 -6
- package/dist/plot/{PlotAxis.svelte.d.ts → core/components/PlotAxis.svelte.d.ts} +5 -3
- package/dist/plot/{PlotControls.svelte → core/components/PlotControls.svelte} +17 -29
- package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
- package/dist/plot/{PlotLegend.svelte → core/components/PlotLegend.svelte} +21 -10
- package/dist/plot/{PlotLegend.svelte.d.ts → core/components/PlotLegend.svelte.d.ts} +3 -2
- package/dist/plot/{PlotTooltip.svelte → core/components/PlotTooltip.svelte} +17 -1
- package/dist/plot/{PlotTooltip.svelte.d.ts → core/components/PlotTooltip.svelte.d.ts} +8 -0
- package/dist/plot/{PortalSelect.svelte → core/components/PortalSelect.svelte} +11 -7
- package/dist/plot/{ReferenceLine.svelte → core/components/ReferenceLine.svelte} +3 -3
- package/dist/plot/{ReferenceLine.svelte.d.ts → core/components/ReferenceLine.svelte.d.ts} +1 -1
- package/dist/plot/{ReferenceLine3D.svelte → core/components/ReferenceLine3D.svelte} +5 -5
- package/dist/plot/{ReferenceLine3D.svelte.d.ts → core/components/ReferenceLine3D.svelte.d.ts} +5 -5
- package/dist/plot/{ReferencePlane.svelte → core/components/ReferencePlane.svelte} +8 -8
- package/dist/plot/{ReferencePlane.svelte.d.ts → core/components/ReferencePlane.svelte.d.ts} +5 -5
- package/dist/plot/{ZeroLines.svelte → core/components/ZeroLines.svelte} +3 -3
- package/dist/plot/{ZeroLines.svelte.d.ts → core/components/ZeroLines.svelte.d.ts} +3 -3
- package/dist/plot/{ZoomRect.svelte → core/components/ZoomRect.svelte} +1 -1
- package/dist/plot/{ZoomRect.svelte.d.ts → core/components/ZoomRect.svelte.d.ts} +1 -1
- package/dist/plot/core/components/index.d.ts +17 -0
- package/dist/plot/core/components/index.js +17 -0
- package/dist/plot/{data-cleaning.d.ts → core/data-cleaning.d.ts} +71 -1
- package/dist/plot/{data-cleaning.js → core/data-cleaning.js} +21 -23
- package/dist/plot/{data-transform.d.ts → core/data-transform.d.ts} +2 -2
- package/dist/plot/{data-transform.js → core/data-transform.js} +3 -3
- package/dist/plot/core/fill-utils.d.ts +34 -0
- package/dist/plot/core/fill-utils.js +391 -0
- package/dist/plot/core/index.d.ts +10 -0
- package/dist/plot/core/index.js +11 -0
- package/dist/plot/core/interactions.d.ts +39 -0
- package/dist/plot/core/interactions.js +209 -0
- package/dist/plot/{layout.d.ts → core/layout.d.ts} +1 -0
- package/dist/plot/{layout.js → core/layout.js} +16 -8
- 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/{reference-line.d.ts → core/reference-line.d.ts} +11 -11
- package/dist/plot/{reference-line.js → core/reference-line.js} +29 -42
- package/dist/plot/core/scales.d.ts +40 -0
- package/dist/plot/{scales.js → core/scales.js} +94 -93
- package/dist/plot/core/svg.d.ts +3 -0
- package/dist/plot/core/svg.js +41 -0
- package/dist/plot/{types.d.ts → core/types.d.ts} +36 -85
- package/dist/plot/{types.js → core/types.js} +1 -1
- package/dist/plot/{utils → core/utils}/label-placement.d.ts +3 -3
- package/dist/plot/{utils → core/utils}/label-placement.js +3 -3
- package/dist/plot/core/utils/series-visibility.d.ts +26 -0
- package/dist/plot/{utils → core/utils}/series-visibility.js +29 -2
- package/dist/plot/core/utils.d.ts +12 -0
- package/dist/plot/core/utils.js +27 -0
- package/dist/plot/{Histogram.svelte → histogram/Histogram.svelte} +174 -551
- package/dist/plot/{Histogram.svelte.d.ts → histogram/Histogram.svelte.d.ts} +2 -2
- package/dist/plot/{HistogramControls.svelte → histogram/HistogramControls.svelte} +6 -6
- package/dist/plot/{HistogramControls.svelte.d.ts → histogram/HistogramControls.svelte.d.ts} +4 -4
- package/dist/plot/histogram/index.d.ts +2 -0
- package/dist/plot/histogram/index.js +2 -0
- package/dist/plot/index.d.ts +8 -41
- package/dist/plot/index.js +10 -39
- package/dist/plot/sankey/Sankey.svelte +697 -0
- package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
- package/dist/plot/sankey/SankeyControls.svelte +98 -0
- package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
- package/dist/plot/sankey/index.d.ts +4 -0
- package/dist/plot/sankey/index.js +3 -0
- package/dist/plot/sankey/sankey-types.d.ts +42 -0
- package/dist/plot/sankey/sankey-types.js +4 -0
- package/dist/plot/sankey/sankey.d.ts +52 -0
- package/dist/plot/sankey/sankey.js +189 -0
- package/dist/plot/{BinnedScatterPlot.svelte → scatter/BinnedScatterPlot.svelte} +64 -64
- package/dist/plot/{BinnedScatterPlot.svelte.d.ts → scatter/BinnedScatterPlot.svelte.d.ts} +6 -6
- package/dist/plot/{ElementScatter.svelte → scatter/ElementScatter.svelte} +6 -6
- package/dist/plot/{ElementScatter.svelte.d.ts → scatter/ElementScatter.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPlot.svelte → scatter/ScatterPlot.svelte} +297 -1008
- package/dist/plot/{ScatterPlot.svelte.d.ts → scatter/ScatterPlot.svelte.d.ts} +10 -18
- package/dist/plot/{ScatterPlotControls.svelte → scatter/ScatterPlotControls.svelte} +6 -5
- package/dist/plot/{ScatterPlotControls.svelte.d.ts → scatter/ScatterPlotControls.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPoint.svelte → scatter/ScatterPoint.svelte} +7 -7
- package/dist/plot/{ScatterPoint.svelte.d.ts → scatter/ScatterPoint.svelte.d.ts} +3 -3
- package/dist/plot/{adaptive-density.d.ts → scatter/adaptive-density.d.ts} +14 -4
- package/dist/plot/{adaptive-density.js → scatter/adaptive-density.js} +46 -20
- package/dist/plot/{binned-scatter-types.d.ts → scatter/binned-scatter-types.d.ts} +5 -12
- package/dist/plot/scatter/index.d.ts +7 -0
- package/dist/plot/scatter/index.js +5 -0
- package/dist/plot/scatter/scatter-data.d.ts +19 -0
- package/dist/plot/scatter/scatter-data.js +212 -0
- package/dist/plot/{ScatterPlot3D.svelte → scatter-3d/ScatterPlot3D.svelte} +25 -34
- package/dist/plot/{ScatterPlot3D.svelte.d.ts → scatter-3d/ScatterPlot3D.svelte.d.ts} +9 -17
- package/dist/plot/{ScatterPlot3DControls.svelte → scatter-3d/ScatterPlot3DControls.svelte} +14 -14
- package/dist/plot/{ScatterPlot3DControls.svelte.d.ts → scatter-3d/ScatterPlot3DControls.svelte.d.ts} +6 -6
- package/dist/plot/{ScatterPlot3DScene.svelte → scatter-3d/ScatterPlot3DScene.svelte} +129 -128
- package/dist/plot/{ScatterPlot3DScene.svelte.d.ts → scatter-3d/ScatterPlot3DScene.svelte.d.ts} +6 -15
- package/dist/plot/{Surface3D.svelte → scatter-3d/Surface3D.svelte} +7 -6
- package/dist/plot/{Surface3D.svelte.d.ts → scatter-3d/Surface3D.svelte.d.ts} +5 -4
- package/dist/plot/scatter-3d/index.d.ts +4 -0
- package/dist/plot/scatter-3d/index.js +4 -0
- package/dist/plot/sunburst/Sunburst.svelte +1041 -0
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +97 -0
- package/dist/plot/sunburst/SunburstControls.svelte +200 -0
- package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
- package/dist/plot/sunburst/index.d.ts +4 -0
- package/dist/plot/sunburst/index.js +4 -0
- package/dist/plot/sunburst/render.d.ts +34 -0
- package/dist/plot/sunburst/render.js +122 -0
- package/dist/plot/sunburst/sunburst.d.ts +62 -0
- package/dist/plot/sunburst/sunburst.js +269 -0
- package/dist/rdf/RdfPlot.svelte +2 -1
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/calc-rdf.js +11 -24
- 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 +14 -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 +79 -3
- package/dist/settings.js +321 -1
- package/dist/spectral/Bands.svelte +47 -36
- package/dist/spectral/Bands.svelte.d.ts +6 -6
- package/dist/spectral/BandsAndDos.svelte +23 -25
- package/dist/spectral/BrillouinBandsDos.svelte +42 -30
- package/dist/spectral/Dos.svelte +15 -23
- package/dist/spectral/Dos.svelte.d.ts +4 -3
- package/dist/spectral/helpers.d.ts +8 -6
- package/dist/spectral/helpers.js +137 -65
- package/dist/state.svelte.d.ts +0 -7
- package/dist/state.svelte.js +0 -6
- package/dist/structure/Arrow.svelte +2 -4
- package/dist/structure/AtomLegend.svelte +8 -9
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/CanvasTooltip.svelte +1 -0
- package/dist/structure/CellSelect.svelte +12 -5
- package/dist/structure/CellSelect.svelte.d.ts +2 -1
- package/dist/structure/Cylinder.svelte +12 -8
- package/dist/structure/Cylinder.svelte.d.ts +4 -1
- package/dist/structure/Lattice.svelte +2 -2
- package/dist/structure/Structure.svelte +365 -423
- package/dist/structure/Structure.svelte.d.ts +5 -15
- package/dist/structure/StructureControls.svelte +217 -2
- package/dist/structure/StructureControls.svelte.d.ts +5 -3
- package/dist/structure/StructureExportPane.svelte +54 -156
- package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
- package/dist/structure/StructureInfoPane.svelte +10 -9
- package/dist/structure/StructureInfoPane.svelte.d.ts +5 -5
- package/dist/structure/StructureScene.svelte +376 -208
- package/dist/structure/StructureScene.svelte.d.ts +22 -20
- package/dist/structure/{label-placement.d.ts → atom-label-placement.d.ts} +3 -3
- package/dist/structure/{label-placement.js → atom-label-placement.js} +15 -5
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +17 -22
- package/dist/structure/bond-order-perception.js +3 -5
- package/dist/structure/bonding.d.ts +4 -0
- package/dist/structure/bonding.js +134 -63
- package/dist/structure/export.d.ts +24 -4
- package/dist/structure/export.js +89 -143
- package/dist/structure/index.d.ts +4 -4
- package/dist/structure/index.js +3 -3
- package/dist/structure/measure.d.ts +3 -2
- package/dist/structure/measure.js +6 -5
- package/dist/structure/parse.d.ts +3 -2
- package/dist/structure/parse.js +419 -438
- package/dist/structure/partial-occupancy.d.ts +0 -1
- package/dist/structure/partial-occupancy.js +1 -1
- package/dist/structure/pbc.d.ts +1 -1
- package/dist/structure/pbc.js +190 -13
- package/dist/structure/polyhedra.d.ts +41 -0
- package/dist/structure/polyhedra.js +602 -0
- package/dist/structure/site.d.ts +4 -0
- package/dist/structure/site.js +1 -0
- package/dist/structure/supercell.js +3 -2
- 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 +354 -0
- package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
- package/dist/symmetry/SymmetryStats.svelte +113 -8
- package/dist/symmetry/WyckoffTable.svelte +68 -7
- package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
- package/dist/symmetry/cell-transform.js +7 -14
- package/dist/symmetry/index.d.ts +14 -4
- package/dist/symmetry/index.js +291 -72
- package/dist/symmetry/spacegroups.d.ts +12 -1
- package/dist/symmetry/spacegroups.js +63 -14
- 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 +66 -25
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/index.d.ts +1 -3
- package/dist/table/index.js +1 -1
- package/dist/theme/index.js +8 -8
- package/dist/tooltip/KCoords.svelte +45 -0
- package/dist/tooltip/KCoords.svelte.d.ts +8 -0
- package/dist/tooltip/index.d.ts +1 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/trajectory/Trajectory.svelte +123 -100
- package/dist/trajectory/Trajectory.svelte.d.ts +11 -22
- package/dist/trajectory/TrajectoryExportPane.svelte +17 -25
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
- package/dist/trajectory/TrajectoryInfoPane.svelte +5 -3
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
- package/dist/trajectory/constants.js +6 -2
- package/dist/trajectory/extract.js +17 -37
- package/dist/trajectory/format-detect.d.ts +1 -1
- package/dist/trajectory/format-detect.js +27 -19
- package/dist/trajectory/frame-reader.d.ts +0 -1
- package/dist/trajectory/frame-reader.js +63 -162
- package/dist/trajectory/helpers.d.ts +10 -2
- package/dist/trajectory/helpers.js +56 -36
- package/dist/trajectory/index.js +1 -1
- 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/hdf5.js +1 -1
- package/dist/trajectory/parse/index.d.ts +1 -1
- package/dist/trajectory/parse/index.js +65 -105
- package/dist/trajectory/parse/lammps.d.ts +0 -2
- package/dist/trajectory/parse/lammps.js +8 -6
- package/dist/trajectory/parse/pymatgen.d.ts +2 -0
- package/dist/trajectory/parse/pymatgen.js +74 -0
- package/dist/trajectory/parse/vasp.js +38 -18
- package/dist/trajectory/parse/xyz.d.ts +13 -1
- package/dist/trajectory/parse/xyz.js +102 -94
- package/dist/trajectory/plotting.d.ts +1 -2
- package/dist/trajectory/plotting.js +16 -113
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +7 -5
- package/dist/xrd/XrdPlot.svelte +16 -30
- package/dist/xrd/broadening.d.ts +2 -1
- package/dist/xrd/calc-xrd.js +18 -20
- package/dist/xrd/index.d.ts +2 -2
- package/dist/xrd/parse.js +2 -2
- package/package.json +43 -26
- 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/PlotControls.svelte.d.ts +0 -4
- package/dist/plot/axis-utils.d.ts +0 -19
- package/dist/plot/axis-utils.js +0 -78
- package/dist/plot/defaults.d.ts +0 -19
- package/dist/plot/defaults.js +0 -9
- package/dist/plot/fill-utils.d.ts +0 -46
- package/dist/plot/fill-utils.js +0 -322
- package/dist/plot/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/hover-lock.svelte.js +0 -46
- package/dist/plot/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -101
- package/dist/plot/scales.d.ts +0 -48
- package/dist/plot/svg.d.ts +0 -1
- package/dist/plot/svg.js +0 -11
- package/dist/plot/utils/series-visibility.d.ts +0 -15
- package/dist/plot/utils.d.ts +0 -1
- package/dist/plot/utils.js +0 -14
- /package/dist/plot/{PortalSelect.svelte.d.ts → core/components/PortalSelect.svelte.d.ts} +0 -0
- /package/dist/plot/{binned-scatter-types.js → scatter/binned-scatter-types.js} +0 -0
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
lang="ts"
|
|
3
3
|
generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
|
|
4
4
|
>
|
|
5
|
-
import type {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import type { Point2D, Vec2 } from '../math'
|
|
5
|
+
import type { D3InterpolateName } from '../../colors'
|
|
6
|
+
import { format_value } from '../../labels'
|
|
7
|
+
import { sanitize_html } from '../../sanitize'
|
|
8
|
+
import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
|
|
9
|
+
import type { Point2D, Vec2 } from '../../math'
|
|
11
10
|
import type {
|
|
12
11
|
AxisLoadError,
|
|
13
12
|
BasePlotProps,
|
|
13
|
+
ColorScaleConfig,
|
|
14
14
|
ControlsConfig,
|
|
15
15
|
DataLoaderFn,
|
|
16
16
|
DataSeries,
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
FillHandlerEvent,
|
|
19
19
|
FillRegion,
|
|
20
20
|
HoverConfig,
|
|
21
|
-
InitialRanges,
|
|
22
21
|
InternalPoint,
|
|
23
22
|
LabelPlacementConfig,
|
|
24
23
|
LegendConfig,
|
|
@@ -27,12 +26,12 @@
|
|
|
27
26
|
Point,
|
|
28
27
|
RefLine,
|
|
29
28
|
RefLineEvent,
|
|
30
|
-
ScaleType,
|
|
31
29
|
ScatterHandlerEvent,
|
|
32
30
|
ScatterHandlerProps,
|
|
31
|
+
SizeScaleConfig,
|
|
33
32
|
StyleOverrides,
|
|
34
33
|
UserContentProps,
|
|
35
|
-
} from '
|
|
34
|
+
} from '..'
|
|
36
35
|
import {
|
|
37
36
|
ColorBar,
|
|
38
37
|
compute_element_placement,
|
|
@@ -47,86 +46,71 @@
|
|
|
47
46
|
ScatterPoint,
|
|
48
47
|
ZeroLines,
|
|
49
48
|
ZoomRect,
|
|
50
|
-
} from '
|
|
49
|
+
} from '..'
|
|
51
50
|
import {
|
|
52
51
|
build_obstacles_norm,
|
|
53
52
|
has_explicit_position,
|
|
54
53
|
measured_footprint,
|
|
55
54
|
place_decorations,
|
|
56
|
-
} from '
|
|
57
|
-
import type { AxisChangeState } from '
|
|
58
|
-
import {
|
|
59
|
-
import {
|
|
60
|
-
|
|
61
|
-
get_series_symbol,
|
|
62
|
-
process_prop,
|
|
63
|
-
} from './data-transform'
|
|
64
|
-
import { AXIS_DEFAULTS } from './defaults'
|
|
65
|
-
import {
|
|
66
|
-
create_dimension_tracker,
|
|
67
|
-
create_hover_lock,
|
|
68
|
-
} from './hover-lock.svelte'
|
|
55
|
+
} 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
|
+
import { create_placed_tween } from '../core/placed-tween.svelte'
|
|
69
60
|
import {
|
|
70
61
|
DEFAULT_MARKERS,
|
|
71
62
|
get_scale_type_name,
|
|
72
63
|
is_time_scale,
|
|
73
|
-
} from '
|
|
74
|
-
import { compute_label_positions } from '
|
|
75
|
-
import
|
|
76
|
-
import {
|
|
77
|
-
handle_legend_double_click,
|
|
78
|
-
toggle_group_visibility,
|
|
79
|
-
toggle_series_visibility,
|
|
80
|
-
} from './utils/series-visibility'
|
|
81
|
-
import { DEFAULTS } from '../settings'
|
|
64
|
+
} from '../core/types'
|
|
65
|
+
import { compute_label_positions } from '../core/utils/label-placement'
|
|
66
|
+
import { create_legend_visibility } from '../core/utils/series-visibility'
|
|
67
|
+
import { DEFAULTS } from '../../settings'
|
|
82
68
|
import { extent } from 'd3-array'
|
|
83
69
|
import { scaleTime } from 'd3-scale'
|
|
84
70
|
import type { ComponentProps, Snippet } from 'svelte'
|
|
85
|
-
import { untrack } from 'svelte'
|
|
71
|
+
import { onDestroy, untrack } from 'svelte'
|
|
86
72
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
87
|
-
import {
|
|
73
|
+
import type { TweenOptions } from 'svelte/motion'
|
|
88
74
|
import { SvelteSet } from 'svelte/reactivity'
|
|
89
|
-
import type {
|
|
75
|
+
import type { Pt } from '../core/fill-utils'
|
|
90
76
|
import {
|
|
91
|
-
|
|
92
|
-
apply_where_condition,
|
|
93
|
-
clamp_for_log_scale,
|
|
77
|
+
compute_fill_segments,
|
|
94
78
|
convert_error_band_to_fill_region,
|
|
95
79
|
generate_fill_path,
|
|
96
|
-
|
|
97
|
-
resolve_boundary,
|
|
98
|
-
} from './fill-utils'
|
|
80
|
+
} from '../core/fill-utils'
|
|
99
81
|
import {
|
|
100
82
|
expand_range_if_needed,
|
|
101
83
|
get_relative_coords,
|
|
84
|
+
invert_rect_range,
|
|
102
85
|
normalize_y2_sync,
|
|
103
|
-
pan_range,
|
|
104
|
-
PINCH_ZOOM_THRESHOLD,
|
|
105
|
-
pixels_to_data_delta,
|
|
106
86
|
sync_y2_range,
|
|
107
|
-
} from '
|
|
108
|
-
import
|
|
87
|
+
} from '../core/interactions'
|
|
88
|
+
import { create_pan_zoom } from '../core/pan-zoom.svelte'
|
|
89
|
+
import type { Rect, Sides } from '../core/layout'
|
|
109
90
|
import {
|
|
110
91
|
calc_auto_padding,
|
|
111
|
-
constrain_tooltip_position,
|
|
112
92
|
filter_padding,
|
|
113
93
|
LABEL_GAP_DEFAULT,
|
|
94
|
+
y2_axis_label_x,
|
|
114
95
|
measure_full_footprint,
|
|
115
96
|
measure_max_tick_width,
|
|
116
97
|
sample_series_obstacle_points,
|
|
117
|
-
} from '
|
|
118
|
-
import type { IndexedRefLine } from '
|
|
119
|
-
import { group_ref_lines_by_z, index_ref_lines } from '
|
|
98
|
+
} from '../core/layout'
|
|
99
|
+
import type { IndexedRefLine } from '../core/reference-line'
|
|
100
|
+
import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
|
|
120
101
|
import {
|
|
121
102
|
create_color_scale,
|
|
122
103
|
create_scale,
|
|
123
104
|
create_size_scale,
|
|
124
105
|
generate_ticks,
|
|
125
106
|
get_nice_data_range,
|
|
126
|
-
} from '
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
107
|
+
} from '../core/scales'
|
|
108
|
+
import { resolve_line_tween, unique_id } from '../core/utils'
|
|
109
|
+
import {
|
|
110
|
+
build_legend_data,
|
|
111
|
+
filter_series_to_ranges,
|
|
112
|
+
pick_tooltip_bg,
|
|
113
|
+
} from './scatter-data'
|
|
130
114
|
|
|
131
115
|
let {
|
|
132
116
|
series = $bindable([]),
|
|
@@ -201,16 +185,8 @@
|
|
|
201
185
|
change?: (
|
|
202
186
|
data: (Point<Metadata> & { series: DataSeries<Metadata> }) | null,
|
|
203
187
|
) => void
|
|
204
|
-
color_scale?:
|
|
205
|
-
|
|
206
|
-
scheme?: D3ColorSchemeName | D3InterpolateName
|
|
207
|
-
value_range?: [number, number]
|
|
208
|
-
} | D3InterpolateName
|
|
209
|
-
size_scale?: {
|
|
210
|
-
type?: ScaleType
|
|
211
|
-
radius_range?: [number, number]
|
|
212
|
-
value_range?: [number, number]
|
|
213
|
-
}
|
|
188
|
+
color_scale?: ColorScaleConfig | D3InterpolateName
|
|
189
|
+
size_scale?: SizeScaleConfig
|
|
214
190
|
color_bar?:
|
|
215
191
|
| (ComponentProps<typeof ColorBar> & {
|
|
216
192
|
margin?: number | Sides
|
|
@@ -285,13 +261,12 @@
|
|
|
285
261
|
|
|
286
262
|
let [width, height] = $state([0, 0])
|
|
287
263
|
let svg_element: SVGElement | null = $state(null) // Bind the SVG element
|
|
288
|
-
let svg_bounding_box: DOMRect | null = $state(null) // Store SVG bounds during drag
|
|
289
264
|
|
|
290
265
|
// Track which specific control properties user has modified
|
|
291
266
|
let touched = new SvelteSet<string>()
|
|
292
267
|
|
|
293
268
|
// Unique component ID to avoid clipPath conflicts between multiple instances
|
|
294
|
-
let component_id = $state(`scatter
|
|
269
|
+
let component_id = $state(unique_id(`scatter`))
|
|
295
270
|
let clip_path_id = $derived(`plot-area-clip-${component_id}`)
|
|
296
271
|
|
|
297
272
|
// Assign stable IDs to series for keying
|
|
@@ -304,20 +279,15 @@
|
|
|
304
279
|
}),
|
|
305
280
|
)
|
|
306
281
|
|
|
307
|
-
// State for rectangle zoom selection
|
|
308
|
-
let drag_start_coords = $state<Point2D | null>(null)
|
|
309
|
-
let drag_current_coords = $state<Point2D | null>(null)
|
|
310
|
-
|
|
311
282
|
// Zoom/pan state - track both initial (data-driven) and current (after pan/zoom) ranges
|
|
312
|
-
let
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
let prev_series_visibility: SeriesVisibilitySnapshot | null = $state(null)
|
|
283
|
+
let ranges = $state<{
|
|
284
|
+
initial: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
|
|
285
|
+
current: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
|
|
286
|
+
}>({
|
|
287
|
+
initial: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
|
|
288
|
+
current: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
|
|
289
|
+
})
|
|
290
|
+
const legend_vis = create_legend_visibility(() => series, (next) => (series = next))
|
|
321
291
|
|
|
322
292
|
// Y2 axis sync configuration
|
|
323
293
|
let y2_sync_config = $derived(normalize_y2_sync(y2_axis?.sync))
|
|
@@ -327,7 +297,7 @@
|
|
|
327
297
|
// Helper to compute synced y2 range or return fallback when sync disabled
|
|
328
298
|
const get_synced_y2 = (y1_range: Vec2, fallback: Vec2): Vec2 =>
|
|
329
299
|
y2_sync_config.mode !== `none`
|
|
330
|
-
? sync_y2_range(y1_range,
|
|
300
|
+
? sync_y2_range(y1_range, ranges.initial.y2, y2_sync_config)
|
|
331
301
|
: fallback
|
|
332
302
|
|
|
333
303
|
// Effect to update y2 range when sync mode changes - use $effect.pre to capture
|
|
@@ -338,25 +308,15 @@
|
|
|
338
308
|
if (mode !== prev_sync_mode) {
|
|
339
309
|
// When sync mode becomes enabled (or changes), apply sync immediately
|
|
340
310
|
if (mode !== `none`) {
|
|
341
|
-
|
|
311
|
+
ranges.current.y2 = sync_y2_range(ranges.current.y, ranges.initial.y2, y2_sync_config)
|
|
342
312
|
} else {
|
|
343
313
|
// When switching to independent mode, reset Y2 to its data range
|
|
344
|
-
|
|
314
|
+
ranges.current.y2 = [...ranges.initial.y2] as Vec2
|
|
345
315
|
}
|
|
346
316
|
prev_sync_mode = mode
|
|
347
317
|
}
|
|
348
318
|
})
|
|
349
319
|
|
|
350
|
-
// Pan state
|
|
351
|
-
let is_focused = $state(false)
|
|
352
|
-
let shift_held = $state(false)
|
|
353
|
-
let pan_drag_state = $state<
|
|
354
|
-
InitialRanges & { start: { x: number; y: number } } | null
|
|
355
|
-
>(null)
|
|
356
|
-
let touch_state = $state<
|
|
357
|
-
InitialRanges & { start_touches: { x: number; y: number }[] } | null
|
|
358
|
-
>(null)
|
|
359
|
-
|
|
360
320
|
// Fill region hover state
|
|
361
321
|
let hovered_fill_key = $state<string | null>(null)
|
|
362
322
|
|
|
@@ -378,20 +338,6 @@
|
|
|
378
338
|
// State for legend/colorbar placement stability
|
|
379
339
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
380
340
|
let colorbar_element = $state<HTMLDivElement | undefined>()
|
|
381
|
-
const legend_hover = create_hover_lock()
|
|
382
|
-
const colorbar_hover = create_hover_lock()
|
|
383
|
-
const dim_tracker = create_dimension_tracker()
|
|
384
|
-
let has_initial_legend_placement = $state(false)
|
|
385
|
-
let has_initial_colorbar_placement = $state(false)
|
|
386
|
-
|
|
387
|
-
// Clear pending hover lock timeouts on unmount
|
|
388
|
-
$effect(() => () => {
|
|
389
|
-
legend_hover.cleanup()
|
|
390
|
-
colorbar_hover.cleanup()
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
// Tooltip element reference for dynamic sizing
|
|
394
|
-
let tooltip_el = $state<HTMLDivElement | undefined>()
|
|
395
341
|
|
|
396
342
|
// Module-level constants to avoid repeated allocations
|
|
397
343
|
// Create and categorize points in a single pass (instead of 3 separate iterations)
|
|
@@ -431,7 +377,7 @@
|
|
|
431
377
|
// Update padding when format or ticks change
|
|
432
378
|
$effect(() => {
|
|
433
379
|
const new_pad = width && height &&
|
|
434
|
-
(y_tick_values.length || y2_tick_values.length || x2_tick_values.length)
|
|
380
|
+
(y_tick_values.length > 0 || y2_tick_values.length > 0 || x2_tick_values.length > 0)
|
|
435
381
|
? calc_auto_padding({
|
|
436
382
|
padding,
|
|
437
383
|
default_padding,
|
|
@@ -607,60 +553,52 @@
|
|
|
607
553
|
return { explicit, range }
|
|
608
554
|
}
|
|
609
555
|
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// X axis: explicit → direct, auto → lazy expand
|
|
616
|
-
if (x.explicit) {
|
|
617
|
-
zoom_x_range = x.range
|
|
618
|
-
} else {
|
|
619
|
-
const result = expand_range_if_needed(initial_x_range, x.range)
|
|
620
|
-
if (result.changed) {
|
|
621
|
-
;[initial_x_range, zoom_x_range] = [result.range, result.range]
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// X2 axis: explicit → direct, auto → lazy expand
|
|
626
|
-
if (x2.explicit) {
|
|
627
|
-
zoom_x2_range = x2.range
|
|
628
|
-
} else {
|
|
629
|
-
const result = expand_range_if_needed(initial_x2_range, x2.range)
|
|
630
|
-
if (result.changed) {
|
|
631
|
-
;[initial_x2_range, zoom_x2_range] = [result.range, result.range]
|
|
632
|
-
}
|
|
556
|
+
const resolved = {
|
|
557
|
+
x: get_range(final_x_axis, auto_x_range),
|
|
558
|
+
x2: get_range(final_x2_axis, auto_x2_range),
|
|
559
|
+
y: get_range(final_y_axis, auto_y_range),
|
|
560
|
+
y2: get_range(final_y2_axis, auto_y2_range),
|
|
633
561
|
}
|
|
634
562
|
|
|
635
|
-
//
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
563
|
+
// untrack reads of `ranges`: this effect also writes it, and tracked reads of the
|
|
564
|
+
// deep proxy would re-trigger the effect on every current/initial write
|
|
565
|
+
for (const axis of [`x`, `x2`, `y`] as const) {
|
|
566
|
+
const { explicit, range } = resolved[axis]
|
|
567
|
+
if (explicit) {
|
|
568
|
+
ranges.current[axis] = range
|
|
569
|
+
} else {
|
|
570
|
+
const result = expand_range_if_needed(untrack(() => ranges.initial[axis]), range)
|
|
571
|
+
if (result.changed) {
|
|
572
|
+
ranges.initial[axis] = result.range
|
|
573
|
+
ranges.current[axis] = result.range
|
|
574
|
+
}
|
|
642
575
|
}
|
|
643
576
|
}
|
|
644
577
|
|
|
645
578
|
// Y2 axis: explicit → direct, else expand initial range then optionally sync
|
|
646
|
-
if (y2.explicit) {
|
|
647
|
-
|
|
579
|
+
if (resolved.y2.explicit) {
|
|
580
|
+
ranges.current.y2 = resolved.y2.range
|
|
648
581
|
} else {
|
|
649
|
-
const result = expand_range_if_needed(
|
|
650
|
-
if (result.changed)
|
|
582
|
+
const result = expand_range_if_needed(untrack(() => ranges.initial.y2), resolved.y2.range)
|
|
583
|
+
if (result.changed) ranges.initial.y2 = result.range
|
|
651
584
|
// Apply sync if enabled, otherwise use expanded range (or keep current if unchanged)
|
|
652
585
|
if (y2_sync_config.mode !== `none`) {
|
|
653
|
-
|
|
586
|
+
// Pan/zoom handlers sync y2 themselves.
|
|
587
|
+
ranges.current.y2 = sync_y2_range(
|
|
588
|
+
untrack(() => ranges.current.y),
|
|
589
|
+
untrack(() => ranges.initial.y2),
|
|
590
|
+
y2_sync_config,
|
|
591
|
+
)
|
|
654
592
|
} else if (result.changed) {
|
|
655
|
-
|
|
593
|
+
ranges.current.y2 = result.range
|
|
656
594
|
}
|
|
657
595
|
}
|
|
658
596
|
})
|
|
659
597
|
|
|
660
|
-
let [x_min, x_max] = $derived(
|
|
661
|
-
let [x2_min, x2_max] = $derived(
|
|
662
|
-
let [y_min, y_max] = $derived(
|
|
663
|
-
let [y2_min, y2_max] = $derived(
|
|
598
|
+
let [x_min, x_max] = $derived(ranges.current.x)
|
|
599
|
+
let [x2_min, x2_max] = $derived(ranges.current.x2)
|
|
600
|
+
let [y_min, y_max] = $derived(ranges.current.y)
|
|
601
|
+
let [y2_min, y2_max] = $derived(ranges.current.y2)
|
|
664
602
|
|
|
665
603
|
// Create auto color range
|
|
666
604
|
let auto_color_range = $derived(
|
|
@@ -723,80 +661,29 @@
|
|
|
723
661
|
|
|
724
662
|
// Filter series data to only include points within bounds and augment with internal data
|
|
725
663
|
let filtered_series = $derived(
|
|
726
|
-
series_with_ids
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
y: [],
|
|
733
|
-
visible: true,
|
|
734
|
-
filtered_data: [],
|
|
735
|
-
_id: series_idx,
|
|
736
|
-
orig_series_idx: series_idx,
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// Handle explicitly hidden series
|
|
741
|
-
if (!(data_series.visible ?? true)) {
|
|
742
|
-
return {
|
|
743
|
-
...data_series,
|
|
744
|
-
visible: false,
|
|
745
|
-
filtered_data: [],
|
|
746
|
-
orig_series_idx: series_idx,
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
const { x: xs, y: ys, color_values, size_values, ...series_rest } = data_series
|
|
751
|
-
|
|
752
|
-
// Process points internally, adding properties beyond the base Point type
|
|
753
|
-
const processed_points: InternalPoint<Metadata>[] = xs.map(
|
|
754
|
-
(x_val: number, point_idx: number) => ({
|
|
755
|
-
x: x_val,
|
|
756
|
-
y: ys[point_idx],
|
|
757
|
-
color_value: color_values?.[point_idx],
|
|
758
|
-
metadata: process_prop(series_rest.metadata, point_idx) as Metadata | undefined,
|
|
759
|
-
point_style: process_prop(series_rest.point_style, point_idx),
|
|
760
|
-
point_hover: process_prop(series_rest.point_hover, point_idx),
|
|
761
|
-
point_label: process_prop(series_rest.point_label, point_idx),
|
|
762
|
-
point_offset: process_prop(series_rest.point_offset, point_idx),
|
|
763
|
-
series_idx,
|
|
764
|
-
point_idx,
|
|
765
|
-
size_value: size_values?.[point_idx],
|
|
766
|
-
}),
|
|
767
|
-
)
|
|
768
|
-
|
|
769
|
-
// Filter to points within the plot bounds (handles inverted ranges like [3.5, 1.4])
|
|
770
|
-
// Determine which ranges to use based on series axis properties
|
|
771
|
-
const [series_x_min, series_x_max] = (data_series.x_axis ?? `x1`) === `x2`
|
|
772
|
-
? [x2_min, x2_max]
|
|
773
|
-
: [x_min, x_max]
|
|
774
|
-
const [series_y_min, series_y_max] = (data_series.y_axis ?? `y1`) === `y2`
|
|
775
|
-
? [y2_min, y2_max]
|
|
776
|
-
: [y_min, y_max]
|
|
777
|
-
|
|
778
|
-
const filtered_data_with_extras = processed_points.filter(
|
|
779
|
-
({ x, y }) =>
|
|
780
|
-
in_range(x, series_x_min, series_x_max) &&
|
|
781
|
-
in_range(y, series_y_min, series_y_max),
|
|
782
|
-
)
|
|
783
|
-
|
|
784
|
-
// Return structure consistent with DataSeries but acknowledge internal data structure (filtered_data)
|
|
785
|
-
return {
|
|
786
|
-
...data_series,
|
|
787
|
-
visible: true, // Mark series as visible here
|
|
788
|
-
filtered_data: filtered_data_with_extras,
|
|
789
|
-
orig_series_idx: series_idx, // Store original index for auto-cycling colors/symbols
|
|
790
|
-
}
|
|
791
|
-
})
|
|
792
|
-
// Filter series end up completely empty after point filtering
|
|
793
|
-
.filter((
|
|
794
|
-
srs,
|
|
795
|
-
): srs is DataSeries<Metadata> & { filtered_data: InternalPoint<Metadata>[] } =>
|
|
796
|
-
!!srs.filtered_data && srs.filtered_data.length > 0
|
|
797
|
-
),
|
|
664
|
+
filter_series_to_ranges(series_with_ids, {
|
|
665
|
+
x: [x_min, x_max],
|
|
666
|
+
x2: [x2_min, x2_max],
|
|
667
|
+
y: [y_min, y_max],
|
|
668
|
+
y2: [y2_min, y2_max],
|
|
669
|
+
}),
|
|
798
670
|
)
|
|
799
671
|
|
|
672
|
+
// Tally line series/points to budget path-morph tweens (see resolve_line_tween).
|
|
673
|
+
// Disabling the morph for high-cardinality plots (e.g. phonon bands) keeps them
|
|
674
|
+
// snappy; Line.svelte short-circuits the Tween when duration <= 0.
|
|
675
|
+
let line_tween_load = $derived.by(() => {
|
|
676
|
+
if (!styles.show_lines) return { series: 0, points: 0 }
|
|
677
|
+
let [n_series, n_points] = [0, 0]
|
|
678
|
+
for (const srs of filtered_series ?? []) {
|
|
679
|
+
if (!(srs.markers ?? DEFAULT_MARKERS).includes(`line`)) continue
|
|
680
|
+
n_series += 1
|
|
681
|
+
n_points += srs.x.length
|
|
682
|
+
}
|
|
683
|
+
return { series: n_series, points: n_points }
|
|
684
|
+
})
|
|
685
|
+
let effective_line_tween = $derived(resolve_line_tween(line_tween, line_tween_load))
|
|
686
|
+
|
|
800
687
|
// Obstacle field for legend/colorbar auto-placement. Sampling only data points lets the
|
|
801
688
|
// legend land on top of a steep connecting line whose markers are sparse (e.g. y=x^2), so
|
|
802
689
|
// sample_series_obstacle_points also walks each drawn segment at a fixed pixel cadence.
|
|
@@ -824,14 +711,6 @@
|
|
|
824
711
|
return points
|
|
825
712
|
})
|
|
826
713
|
|
|
827
|
-
// Explicitly define the type for display_style matching PlotLegend expectations
|
|
828
|
-
type LegendDisplayStyle = {
|
|
829
|
-
symbol_type?: D3SymbolName
|
|
830
|
-
symbol_color?: string
|
|
831
|
-
line_color?: string
|
|
832
|
-
line_dash?: string
|
|
833
|
-
}
|
|
834
|
-
|
|
835
714
|
const fill_hover_key = (
|
|
836
715
|
source_type: `fill_region` | `error_band`,
|
|
837
716
|
source_idx: number,
|
|
@@ -894,19 +773,22 @@
|
|
|
894
773
|
})),
|
|
895
774
|
]
|
|
896
775
|
|
|
897
|
-
//
|
|
898
|
-
//
|
|
899
|
-
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
const unique_x = [...x_set].sort((val_a, val_b) => val_a - val_b)
|
|
776
|
+
// On log axes, clamp non-positive coords to the scale's domain floor (x_min/y_min) before
|
|
777
|
+
// scaling. A fixed tiny epsilon can sit far below the domain and map to extreme pixel coords.
|
|
778
|
+
const x_scale_type = final_x_axis.scale_type ?? `linear`
|
|
779
|
+
const y_scale_type = final_y_axis.scale_type ?? `linear`
|
|
780
|
+
const to_px = (pt: Pt): Pt => ({
|
|
781
|
+
x: x_scale_fn(x_scale_type === `log` && pt.x <= 0 ? x_min : pt.x),
|
|
782
|
+
y: y_scale_fn(y_scale_type === `log` && pt.y <= 0 ? y_min : pt.y),
|
|
783
|
+
})
|
|
908
784
|
|
|
909
|
-
|
|
785
|
+
// Each boundary is traced through its own points with the same curve the series line uses,
|
|
786
|
+
// so fill edges coincide exactly with the lines they border (x_domain anchors flat boundaries).
|
|
787
|
+
const domains = {
|
|
788
|
+
x_domain: [x_min, x_max] as Vec2,
|
|
789
|
+
y_domain: [y_min, y_max] as Vec2,
|
|
790
|
+
y2_domain: [y2_min, y2_max] as Vec2,
|
|
791
|
+
}
|
|
910
792
|
|
|
911
793
|
return all_regions
|
|
912
794
|
.filter((
|
|
@@ -918,71 +800,24 @@
|
|
|
918
800
|
hover_key: string
|
|
919
801
|
} => entry.region !== null)
|
|
920
802
|
.map(({ region, source_type, source_idx, hover_key }, idx) => {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const lower_values = resolve_boundary(
|
|
937
|
-
region.lower,
|
|
938
|
-
series_with_ids,
|
|
939
|
-
unique_x,
|
|
940
|
-
domains,
|
|
941
|
-
)
|
|
942
|
-
|
|
943
|
-
if (!upper_values || !lower_values) return null
|
|
944
|
-
|
|
945
|
-
// Apply range constraints
|
|
946
|
-
const range_filtered = apply_range_constraints(
|
|
947
|
-
unique_x,
|
|
948
|
-
lower_values,
|
|
949
|
-
upper_values,
|
|
950
|
-
region,
|
|
951
|
-
)
|
|
952
|
-
|
|
953
|
-
// Clamp for log scale if needed
|
|
954
|
-
const y_scale_type = final_y_axis.scale_type ?? `linear`
|
|
955
|
-
const x_scale_type = final_x_axis.scale_type ?? `linear`
|
|
956
|
-
const clamped = clamp_for_log_scale(
|
|
957
|
-
range_filtered.x,
|
|
958
|
-
range_filtered.y1,
|
|
959
|
-
range_filtered.y2,
|
|
960
|
-
y_scale_type,
|
|
961
|
-
x_scale_type,
|
|
962
|
-
)
|
|
963
|
-
|
|
964
|
-
// Apply where condition (splits into segments)
|
|
965
|
-
const conditioned = apply_where_condition(
|
|
966
|
-
clamped.x,
|
|
967
|
-
clamped.y1,
|
|
968
|
-
clamped.y2,
|
|
969
|
-
region,
|
|
970
|
-
)
|
|
971
|
-
|
|
972
|
-
// Generate paths for each segment (convert to pixel coordinates)
|
|
973
|
-
const path_segments = conditioned.segments
|
|
974
|
-
.filter((segment) => segment.length > 1)
|
|
975
|
-
.map((segment) => {
|
|
976
|
-
const pixel_data: FillPathPoint[] = segment.map((point) => ({
|
|
977
|
-
x: x_scale_fn(point.x),
|
|
978
|
-
y1: y_scale_fn(point.y1),
|
|
979
|
-
y2: y_scale_fn(point.y2),
|
|
980
|
-
}))
|
|
981
|
-
return generate_fill_path(pixel_data, region.curve ?? `monotoneX`)
|
|
982
|
-
})
|
|
983
|
-
.filter((path) => path.length > 0)
|
|
803
|
+
// Hidden fills keep their entry (with empty path_segments -> nothing renders) so the
|
|
804
|
+
// legend item persists greyed-out and can be toggled back on.
|
|
805
|
+
const hidden = region.visible === false
|
|
806
|
+
const path_segments = hidden
|
|
807
|
+
? []
|
|
808
|
+
: compute_fill_segments(region, series_with_ids, domains)
|
|
809
|
+
.map((seg) =>
|
|
810
|
+
generate_fill_path(
|
|
811
|
+
seg.upper.map(to_px),
|
|
812
|
+
seg.lower.map(to_px),
|
|
813
|
+
seg.upper_curve,
|
|
814
|
+
seg.lower_curve,
|
|
815
|
+
)
|
|
816
|
+
)
|
|
817
|
+
.filter((path) => path.length > 0)
|
|
984
818
|
|
|
985
|
-
|
|
819
|
+
// Drop only visible fills with no geometry; keep hidden ones for the legend
|
|
820
|
+
if (!hidden && path_segments.length === 0) return null
|
|
986
821
|
|
|
987
822
|
return { ...region, idx, source_type, source_idx, hover_key, path_segments }
|
|
988
823
|
})
|
|
@@ -990,161 +825,9 @@
|
|
|
990
825
|
})
|
|
991
826
|
|
|
992
827
|
// Prepare data needed for the legend component
|
|
993
|
-
let legend_data = $derived
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
const is_visible = data_series?.visible ?? true
|
|
997
|
-
// Prefer top-level label, fallback to metadata label
|
|
998
|
-
const explicit_label = data_series?.label ??
|
|
999
|
-
(typeof data_series?.metadata === `object` &&
|
|
1000
|
-
data_series.metadata !== null &&
|
|
1001
|
-
`label` in data_series.metadata &&
|
|
1002
|
-
typeof data_series.metadata.label === `string`
|
|
1003
|
-
? data_series.metadata.label
|
|
1004
|
-
: null)
|
|
1005
|
-
// Use explicit label or generate default
|
|
1006
|
-
const label = explicit_label ?? `Series ${series_idx + 1}`
|
|
1007
|
-
const has_explicit_label = explicit_label != null
|
|
1008
|
-
|
|
1009
|
-
// Use series-specific defaults for auto-differentiation
|
|
1010
|
-
const series_default_color = get_series_color(series_idx)
|
|
1011
|
-
const series_default_symbol = get_series_symbol(series_idx)
|
|
1012
|
-
|
|
1013
|
-
const display_style: LegendDisplayStyle = {
|
|
1014
|
-
symbol_type: series_default_symbol,
|
|
1015
|
-
symbol_color: series_default_color,
|
|
1016
|
-
line_color: series_default_color,
|
|
1017
|
-
}
|
|
1018
|
-
const series_markers = data_series?.markers ?? DEFAULT_MARKERS
|
|
1019
|
-
|
|
1020
|
-
// Check point_style (could be object or array)
|
|
1021
|
-
const first_point_style = Array.isArray(data_series?.point_style)
|
|
1022
|
-
? data_series.point_style[0]
|
|
1023
|
-
: data_series?.point_style
|
|
1024
|
-
|
|
1025
|
-
if (series_markers?.includes(`points`)) {
|
|
1026
|
-
if (first_point_style) {
|
|
1027
|
-
// Use explicit symbol_type if provided and valid, otherwise keep series default
|
|
1028
|
-
if (
|
|
1029
|
-
typeof first_point_style.symbol_type === `string` &&
|
|
1030
|
-
symbol_names.includes(first_point_style.symbol_type as D3SymbolName)
|
|
1031
|
-
) {
|
|
1032
|
-
display_style.symbol_type = first_point_style
|
|
1033
|
-
.symbol_type as D3SymbolName
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
// Use explicit fill color if provided
|
|
1037
|
-
if (first_point_style.fill) {
|
|
1038
|
-
display_style.symbol_color = first_point_style.fill
|
|
1039
|
-
}
|
|
1040
|
-
if (first_point_style.stroke) {
|
|
1041
|
-
// Use stroke color if fill is none or transparent
|
|
1042
|
-
if (
|
|
1043
|
-
!display_style.symbol_color ||
|
|
1044
|
-
display_style.symbol_color === `none` ||
|
|
1045
|
-
display_style.symbol_color.startsWith(`rgba(`, 0) // Check if transparent
|
|
1046
|
-
) display_style.symbol_color = first_point_style.stroke
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
// else: keep series-specific defaults for symbol_type and symbol_color
|
|
1050
|
-
} else {
|
|
1051
|
-
// If no points marker, explicitly remove marker style for legend
|
|
1052
|
-
display_style.symbol_type = undefined
|
|
1053
|
-
display_style.symbol_color = undefined
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
// Check line_style
|
|
1057
|
-
if (series_markers?.includes(`line`)) {
|
|
1058
|
-
// Prefer explicit line stroke, then other explicit colors, then series default
|
|
1059
|
-
let legend_line_color = data_series?.line_style?.stroke
|
|
1060
|
-
if (!legend_line_color) {
|
|
1061
|
-
// Try color scale if available
|
|
1062
|
-
const first_cv = Array.isArray(data_series?.color_values)
|
|
1063
|
-
? data_series?.color_values?.find((color_val: number | null) =>
|
|
1064
|
-
color_val != null
|
|
1065
|
-
)
|
|
1066
|
-
: undefined
|
|
1067
|
-
legend_line_color =
|
|
1068
|
-
(first_cv != null ? color_scale_fn(first_cv) : undefined) ||
|
|
1069
|
-
first_point_style?.fill ||
|
|
1070
|
-
first_point_style?.stroke ||
|
|
1071
|
-
series_default_color
|
|
1072
|
-
}
|
|
1073
|
-
display_style.line_color = legend_line_color
|
|
1074
|
-
display_style.line_dash = data_series?.line_style?.line_dash
|
|
1075
|
-
} else {
|
|
1076
|
-
// If no line marker, explicitly remove line style for legend
|
|
1077
|
-
display_style.line_dash = undefined
|
|
1078
|
-
display_style.line_color = undefined
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
return {
|
|
1082
|
-
series_idx,
|
|
1083
|
-
label,
|
|
1084
|
-
visible: is_visible,
|
|
1085
|
-
display_style,
|
|
1086
|
-
has_explicit_label,
|
|
1087
|
-
legend_group: data_series?.legend_group,
|
|
1088
|
-
}
|
|
1089
|
-
},
|
|
1090
|
-
)
|
|
1091
|
-
|
|
1092
|
-
// Deduplicate by label+legend_group - keep first occurrence of each unique combination
|
|
1093
|
-
const seen_labels = new SvelteSet<string>()
|
|
1094
|
-
const series_items = items.filter(
|
|
1095
|
-
(
|
|
1096
|
-
legend_item: {
|
|
1097
|
-
label: string
|
|
1098
|
-
series_idx: number
|
|
1099
|
-
visible: boolean
|
|
1100
|
-
display_style: LegendDisplayStyle
|
|
1101
|
-
has_explicit_label: boolean
|
|
1102
|
-
legend_group?: string
|
|
1103
|
-
},
|
|
1104
|
-
) => {
|
|
1105
|
-
// Use label+group as unique key (group may be undefined)
|
|
1106
|
-
const unique_key = `${legend_item.legend_group ?? ``}::${legend_item.label}`
|
|
1107
|
-
if (seen_labels.has(unique_key)) return false
|
|
1108
|
-
seen_labels.add(unique_key)
|
|
1109
|
-
return true
|
|
1110
|
-
},
|
|
1111
|
-
)
|
|
1112
|
-
|
|
1113
|
-
// Add fill region items to legend (deduplicated using same key format as series)
|
|
1114
|
-
const fill_items = computed_fills
|
|
1115
|
-
.filter((fill) => fill.show_in_legend !== false && fill.label)
|
|
1116
|
-
.filter((fill) => {
|
|
1117
|
-
// Use same composite key as series: legend_group::label
|
|
1118
|
-
const unique_key = `${fill.legend_group ?? ``}::${fill.label ?? ``}`
|
|
1119
|
-
if (seen_labels.has(unique_key)) return false
|
|
1120
|
-
seen_labels.add(unique_key)
|
|
1121
|
-
return true
|
|
1122
|
-
})
|
|
1123
|
-
.map((fill) => {
|
|
1124
|
-
// Pass gradient for swatch rendering, or solid color as fallback
|
|
1125
|
-
const fill_gradient = is_fill_gradient(fill.fill) ? fill.fill : undefined
|
|
1126
|
-
const fill_color = typeof fill.fill === `string` ? fill.fill : undefined
|
|
1127
|
-
|
|
1128
|
-
return {
|
|
1129
|
-
series_idx: -1, // Not a series
|
|
1130
|
-
fill_idx: fill.idx,
|
|
1131
|
-
fill_source_type: fill.source_type,
|
|
1132
|
-
fill_source_idx: fill.source_idx,
|
|
1133
|
-
item_type: `fill` as const,
|
|
1134
|
-
label: fill.label ?? ``,
|
|
1135
|
-
visible: fill.visible !== false,
|
|
1136
|
-
legend_group: fill.legend_group,
|
|
1137
|
-
display_style: {
|
|
1138
|
-
fill_color,
|
|
1139
|
-
fill_opacity: fill.fill_opacity ?? 0.3,
|
|
1140
|
-
edge_color: fill.edge_upper?.color,
|
|
1141
|
-
fill_gradient,
|
|
1142
|
-
},
|
|
1143
|
-
}
|
|
1144
|
-
})
|
|
1145
|
-
|
|
1146
|
-
return [...series_items, ...fill_items]
|
|
1147
|
-
})
|
|
828
|
+
let legend_data = $derived(
|
|
829
|
+
build_legend_data(series_with_ids, computed_fills, color_scale_fn),
|
|
830
|
+
)
|
|
1148
831
|
|
|
1149
832
|
// Group fills by z-index for ordered rendering (single pass instead of 4 filters)
|
|
1150
833
|
let fills_by_z = $derived.by(() => {
|
|
@@ -1171,7 +854,7 @@
|
|
|
1171
854
|
// Calculate best legend placement using continuous grid sampling
|
|
1172
855
|
let legend_placement = $derived.by(() => {
|
|
1173
856
|
const should_place = legend != null &&
|
|
1174
|
-
(legend_data.length > 1 || Object.keys(legend).length > 0)
|
|
857
|
+
(legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)
|
|
1175
858
|
|
|
1176
859
|
if (!should_place || !width || !height) return null
|
|
1177
860
|
|
|
@@ -1192,7 +875,7 @@
|
|
|
1192
875
|
|
|
1193
876
|
// Calculate color bar placement (coordinates with legend to avoid overlap)
|
|
1194
877
|
let color_bar_placement = $derived.by(() => {
|
|
1195
|
-
if (!color_bar ||
|
|
878
|
+
if (!color_bar || all_color_values.length === 0 || !width || !height) return null
|
|
1196
879
|
|
|
1197
880
|
const plot_width = width - pad.l - pad.r
|
|
1198
881
|
const plot_height = height - pad.t - pad.b
|
|
@@ -1242,60 +925,23 @@
|
|
|
1242
925
|
return legend_placement
|
|
1243
926
|
})
|
|
1244
927
|
|
|
1245
|
-
//
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
{
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
const dims_changed = dim_tracker.has_changed(width, height)
|
|
1263
|
-
if (dims_changed) dim_tracker.update(width, height)
|
|
1264
|
-
|
|
1265
|
-
// Update colorbar position (stable after initial placement unless responsive)
|
|
1266
|
-
if (color_bar_placement) {
|
|
1267
|
-
const is_responsive = color_bar?.responsive ?? false
|
|
1268
|
-
const should_update = dims_changed || (!colorbar_hover.is_locked.current &&
|
|
1269
|
-
(is_responsive || !has_initial_colorbar_placement))
|
|
1270
|
-
|
|
1271
|
-
if (should_update) {
|
|
1272
|
-
tweened_colorbar_coords.set(
|
|
1273
|
-
{ x: color_bar_placement.x, y: color_bar_placement.y },
|
|
1274
|
-
has_initial_colorbar_placement ? undefined : { duration: 0 },
|
|
1275
|
-
)
|
|
1276
|
-
if (colorbar_element && !has_initial_colorbar_placement) {
|
|
1277
|
-
has_initial_colorbar_placement = true
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
// Update legend position (stable after initial placement unless responsive)
|
|
1283
|
-
if (legend_manual_position && !legend_is_dragging) {
|
|
1284
|
-
// Immediate update (no animation) for manually dragged positions
|
|
1285
|
-
tweened_legend_coords.set(legend_manual_position, { duration: 0 })
|
|
1286
|
-
} else if (active_legend_placement && !legend_is_dragging) {
|
|
1287
|
-
const is_responsive = legend?.responsive ?? false
|
|
1288
|
-
const should_update = dims_changed || (!legend_hover.is_locked.current &&
|
|
1289
|
-
(is_responsive || !has_initial_legend_placement))
|
|
1290
|
-
|
|
1291
|
-
if (should_update) {
|
|
1292
|
-
tweened_legend_coords.set(
|
|
1293
|
-
{ x: active_legend_placement.x, y: active_legend_placement.y },
|
|
1294
|
-
has_initial_legend_placement ? undefined : { duration: 0 },
|
|
1295
|
-
)
|
|
1296
|
-
if (legend_element) has_initial_legend_placement = true
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
928
|
+
// Tweened colorbar/legend coordinates with shared placement stability gating
|
|
929
|
+
const colorbar_tween = create_placed_tween({
|
|
930
|
+
placement: () => color_bar_placement,
|
|
931
|
+
dims: () => ({ width, height }),
|
|
932
|
+
responsive: () => color_bar?.responsive ?? false,
|
|
933
|
+
element: () => colorbar_element,
|
|
934
|
+
tween: () => color_bar?.tween,
|
|
935
|
+
})
|
|
936
|
+
const legend_tween = create_placed_tween({
|
|
937
|
+
placement: () => active_legend_placement,
|
|
938
|
+
dims: () => ({ width, height }),
|
|
939
|
+
responsive: () => legend?.responsive ?? false,
|
|
940
|
+
element: () => legend_element,
|
|
941
|
+
tween: () => legend?.tween,
|
|
942
|
+
// Leave coords alone mid-drag; once dragged, the manual position wins permanently
|
|
943
|
+
suspended: () => legend_is_dragging,
|
|
944
|
+
manual_position: () => legend_manual_position,
|
|
1299
945
|
})
|
|
1300
946
|
|
|
1301
947
|
// Generate axis ticks - consolidated into single derived for efficiency
|
|
@@ -1361,346 +1007,76 @@
|
|
|
1361
1007
|
y2_max: measure_max_tick_width(y2_tick_values, final_y2_axis.format ?? ``),
|
|
1362
1008
|
})
|
|
1363
1009
|
|
|
1364
|
-
//
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
// Optional: update tooltip only if inside SVG bounds
|
|
1374
|
-
const is_inside_svg = current_x >= 0 &&
|
|
1375
|
-
current_x <= svg_bounding_box.width &&
|
|
1376
|
-
current_y >= 0 &&
|
|
1377
|
-
current_y <= svg_bounding_box.height
|
|
1378
|
-
|
|
1379
|
-
if (is_inside_svg) {
|
|
1380
|
-
// Use the already calculated relative coordinates
|
|
1381
|
-
update_tooltip_point(current_x, current_y)
|
|
1382
|
-
} else tooltip_point = null // Clear tooltip if outside
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
const on_window_mouse_up = (_evt: MouseEvent) => {
|
|
1386
|
-
if (drag_start_coords && drag_current_coords) {
|
|
1387
|
-
// Use current scales to invert screen coords to data coords
|
|
1388
|
-
const start_data_x_val = x_scale_fn.invert(drag_start_coords.x)
|
|
1389
|
-
const end_data_x_val = x_scale_fn.invert(drag_current_coords.x)
|
|
1390
|
-
const start_data_y_val = y_scale_fn.invert(drag_start_coords.y)
|
|
1391
|
-
const end_data_y_val = y_scale_fn.invert(drag_current_coords.y)
|
|
1392
|
-
|
|
1393
|
-
// Ensure range is not zero and order is correct
|
|
1394
|
-
let x1: number, x2: number
|
|
1395
|
-
if (start_data_x_val instanceof Date && end_data_x_val instanceof Date) {
|
|
1396
|
-
x1 = start_data_x_val.getTime()
|
|
1397
|
-
x2 = end_data_x_val.getTime()
|
|
1398
|
-
} else if (
|
|
1399
|
-
typeof start_data_x_val === `number` &&
|
|
1400
|
-
typeof end_data_x_val === `number`
|
|
1401
|
-
) {
|
|
1402
|
-
x1 = start_data_x_val
|
|
1403
|
-
x2 = end_data_x_val
|
|
1404
|
-
} else {
|
|
1405
|
-
console.error(`Mismatched types for x-axis zoom calculation`)
|
|
1406
|
-
// Reset states without zooming if types are wrong
|
|
1407
|
-
drag_start_coords = null
|
|
1408
|
-
drag_current_coords = null
|
|
1409
|
-
window.removeEventListener(`mousemove`, on_window_mouse_move)
|
|
1410
|
-
window.removeEventListener(`mouseup`, on_window_mouse_up)
|
|
1411
|
-
return
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
const next_x_range: [number, number] = [Math.min(x1, x2), Math.max(x1, x2)]
|
|
1415
|
-
// Y axis is always number
|
|
1416
|
-
const next_y_range: [number, number] = [
|
|
1417
|
-
Math.min(start_data_y_val, end_data_y_val),
|
|
1418
|
-
Math.max(start_data_y_val, end_data_y_val),
|
|
1419
|
-
]
|
|
1420
|
-
|
|
1421
|
-
// Check for minuscule zoom box (e.g. accidental click)
|
|
1422
|
-
const min_zoom_size = 5 // Minimum pixels to trigger zoom
|
|
1423
|
-
const dx = Math.abs(drag_start_coords.x - drag_current_coords.x)
|
|
1424
|
-
const dy = Math.abs(drag_start_coords.y - drag_current_coords.y)
|
|
1425
|
-
|
|
1426
|
-
if (
|
|
1427
|
-
dx > min_zoom_size &&
|
|
1428
|
-
dy > min_zoom_size &&
|
|
1429
|
-
next_x_range[0] !== next_x_range[1] &&
|
|
1430
|
-
next_y_range[0] !== next_y_range[1]
|
|
1431
|
-
) {
|
|
1432
|
-
// Update axis ranges to trigger reactivity (like BarPlot/Histogram do)
|
|
1433
|
-
// Y2 sync is handled by the effect that reacts to y_axis changes
|
|
1434
|
-
x_axis = { ...x_axis, range: next_x_range }
|
|
1435
|
-
y_axis = { ...y_axis, range: next_y_range }
|
|
1436
|
-
|
|
1437
|
-
// X2 axis: invert screen coords using x2 scale
|
|
1438
|
-
if (x2_points.length > 0) {
|
|
1439
|
-
const start_x2_val = x2_scale_fn.invert(drag_start_coords.x)
|
|
1440
|
-
const end_x2_val = x2_scale_fn.invert(drag_current_coords.x)
|
|
1441
|
-
const x2_a = start_x2_val instanceof Date
|
|
1442
|
-
? start_x2_val.getTime()
|
|
1443
|
-
: start_x2_val as number
|
|
1444
|
-
const x2_b = end_x2_val instanceof Date
|
|
1445
|
-
? end_x2_val.getTime()
|
|
1446
|
-
: end_x2_val as number
|
|
1447
|
-
x2_axis = {
|
|
1448
|
-
...x2_axis,
|
|
1449
|
-
range: [Math.min(x2_a, x2_b), Math.max(x2_a, x2_b)],
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
// Reset states and remove listeners
|
|
1456
|
-
drag_start_coords = null
|
|
1457
|
-
drag_current_coords = null
|
|
1458
|
-
svg_bounding_box = null
|
|
1459
|
-
window.removeEventListener(`mousemove`, on_window_mouse_move)
|
|
1460
|
-
window.removeEventListener(`mouseup`, on_window_mouse_up)
|
|
1461
|
-
document.body.style.cursor = `default`
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
// Pan drag handlers
|
|
1465
|
-
const on_pan_move = (evt: MouseEvent) => {
|
|
1466
|
-
if (!pan_drag_state) return
|
|
1467
|
-
const dx = evt.clientX - pan_drag_state.start.x
|
|
1468
|
-
const dy = evt.clientY - pan_drag_state.start.y
|
|
1469
|
-
|
|
1470
|
-
// Convert pixel delta to data delta (note: drag direction is inverted for natural pan feel)
|
|
1010
|
+
// Shared pan/zoom/touch/drag-rect interaction controller. set_range routes y2
|
|
1011
|
+
// writes through get_synced_y2 (write-order contract: y is written before y2, so
|
|
1012
|
+
// the sync reads the just-updated y range).
|
|
1013
|
+
const pan_zoom = create_pan_zoom({
|
|
1014
|
+
ranges: () => ranges.current,
|
|
1015
|
+
scale_type: (axis) =>
|
|
1016
|
+
({ x: final_x_axis, x2: final_x2_axis, y: final_y_axis, y2: final_y2_axis })[axis]
|
|
1017
|
+
.scale_type,
|
|
1471
1018
|
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
function handle_mouse_down(evt: MouseEvent) {
|
|
1514
|
-
if (!svg_element) return
|
|
1515
|
-
|
|
1516
|
-
// Check if pan is enabled and shift is held for pan mode
|
|
1517
|
-
const pan_enabled = pan?.enabled !== false
|
|
1518
|
-
if (pan_enabled && evt.shiftKey) {
|
|
1519
|
-
evt.preventDefault()
|
|
1520
|
-
pan_drag_state = {
|
|
1521
|
-
start: { x: evt.clientX, y: evt.clientY },
|
|
1522
|
-
initial_x_range: [...zoom_x_range] as [number, number],
|
|
1523
|
-
initial_x2_range: [...zoom_x2_range] as [number, number],
|
|
1524
|
-
initial_y_range: [...zoom_y_range] as [number, number],
|
|
1525
|
-
initial_y2_range: [...zoom_y2_range] as [number, number],
|
|
1019
|
+
plot_dims: () => ({
|
|
1020
|
+
width: Math.max(1, width - pad.l - pad.r),
|
|
1021
|
+
height: Math.max(1, height - pad.t - pad.b),
|
|
1022
|
+
}),
|
|
1023
|
+
pan: () => pan,
|
|
1024
|
+
set_range: (axis, range) => {
|
|
1025
|
+
if (axis === `y2`) ranges.current.y2 = get_synced_y2(ranges.current.y, range)
|
|
1026
|
+
else ranges.current[axis] = range
|
|
1027
|
+
},
|
|
1028
|
+
svg: () => svg_element,
|
|
1029
|
+
on_rect_zoom: (start, current) => {
|
|
1030
|
+
// Update axis ranges to trigger reactivity; both x and y must invert to valid
|
|
1031
|
+
// (finite, non-degenerate) ranges or the rect zoom is discarded entirely
|
|
1032
|
+
const next_x = invert_rect_range(x_scale_fn, start.x, current.x)
|
|
1033
|
+
const next_y = invert_rect_range(y_scale_fn, start.y, current.y)
|
|
1034
|
+
if (!next_x || !next_y) return
|
|
1035
|
+
x_axis = { ...x_axis, range: next_x }
|
|
1036
|
+
y_axis = { ...y_axis, range: next_y }
|
|
1037
|
+
|
|
1038
|
+
// X2 axis: invert screen coords using x2 scale
|
|
1039
|
+
const next_x2 = x2_points.length > 0
|
|
1040
|
+
? invert_rect_range(x2_scale_fn, start.x, current.x)
|
|
1041
|
+
: null
|
|
1042
|
+
if (next_x2) x2_axis = { ...x2_axis, range: next_x2 }
|
|
1043
|
+
|
|
1044
|
+
// Y2 axis: when sync is enabled the y_axis effect derives y2; with sync 'none'
|
|
1045
|
+
// y2 must zoom from the rect directly (parity with BarPlot/Histogram/BoxPlot)
|
|
1046
|
+
const next_y2 = y2_points.length > 0 && y2_sync_config.mode === `none`
|
|
1047
|
+
? invert_rect_range(y2_scale_fn, start.y, current.y)
|
|
1048
|
+
: null
|
|
1049
|
+
if (next_y2) y2_axis = { ...y2_axis, range: next_y2 }
|
|
1050
|
+
},
|
|
1051
|
+
on_reset: () => {
|
|
1052
|
+
// Reset to current auto ranges (not stale initial ranges which may have expanded)
|
|
1053
|
+
// This ensures lazy expansion restarts fresh from current data bounds
|
|
1054
|
+
ranges.initial = {
|
|
1055
|
+
x: [...auto_x_range] as Vec2,
|
|
1056
|
+
x2: [...auto_x2_range] as Vec2,
|
|
1057
|
+
y: [...auto_y_range] as Vec2,
|
|
1058
|
+
y2: [...auto_y2_range] as Vec2,
|
|
1526
1059
|
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
evt.preventDefault()
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
// Wheel handler for pan (requires focus and shift)
|
|
1551
|
-
function handle_wheel(evt: WheelEvent) {
|
|
1552
|
-
const pan_enabled = pan?.enabled !== false
|
|
1553
|
-
// Only capture wheel when focused AND Shift is held
|
|
1554
|
-
// Use shift_held state (tracked via keydown/keyup) for compatibility with synthetic events
|
|
1555
|
-
if (!pan_enabled || !is_focused || !shift_held) return
|
|
1556
|
-
|
|
1557
|
-
evt.preventDefault()
|
|
1558
|
-
|
|
1559
|
-
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
1560
|
-
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
1561
|
-
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
1562
|
-
const sensitivity = pan?.wheel_sensitivity ?? 1
|
|
1563
|
-
|
|
1564
|
-
// Determine pan direction based on wheel delta
|
|
1565
|
-
// deltaX for horizontal scroll (trackpad), deltaY for vertical
|
|
1566
|
-
const x_delta = pixels_to_data_delta(
|
|
1567
|
-
evt.deltaX * sensitivity,
|
|
1568
|
-
zoom_x_range,
|
|
1569
|
-
plot_width,
|
|
1570
|
-
)
|
|
1571
|
-
const x2_delta = pixels_to_data_delta(
|
|
1572
|
-
evt.deltaX * sensitivity,
|
|
1573
|
-
zoom_x2_range,
|
|
1574
|
-
plot_width,
|
|
1575
|
-
)
|
|
1576
|
-
const y_delta = pixels_to_data_delta(
|
|
1577
|
-
evt.deltaY * sensitivity,
|
|
1578
|
-
zoom_y_range,
|
|
1579
|
-
plot_height,
|
|
1580
|
-
)
|
|
1581
|
-
const y2_delta = pixels_to_data_delta(
|
|
1582
|
-
evt.deltaY * sensitivity,
|
|
1583
|
-
zoom_y2_range,
|
|
1584
|
-
plot_height,
|
|
1585
|
-
)
|
|
1586
|
-
|
|
1587
|
-
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
1588
|
-
zoom_x_range = pan_range(zoom_x_range, x_delta)
|
|
1589
|
-
zoom_x2_range = pan_range(zoom_x2_range, x2_delta)
|
|
1590
|
-
} else {
|
|
1591
|
-
zoom_y_range = pan_range(zoom_y_range, y_delta)
|
|
1592
|
-
zoom_y2_range = get_synced_y2(zoom_y_range, pan_range(zoom_y2_range, y2_delta))
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
// Touch handlers for pinch-zoom and two-finger pan
|
|
1597
|
-
function handle_touch_start(evt: TouchEvent) {
|
|
1598
|
-
const touch_enabled = pan?.enabled !== false && pan?.touch_enabled !== false
|
|
1599
|
-
if (!touch_enabled || evt.touches.length !== 2) return
|
|
1600
|
-
|
|
1601
|
-
evt.preventDefault()
|
|
1602
|
-
const touches = Array.from(evt.touches)
|
|
1603
|
-
touch_state = {
|
|
1604
|
-
start_touches: touches.map((touch) => ({ x: touch.clientX, y: touch.clientY })),
|
|
1605
|
-
initial_x_range: [...zoom_x_range] as [number, number],
|
|
1606
|
-
initial_x2_range: [...zoom_x2_range] as [number, number],
|
|
1607
|
-
initial_y_range: [...zoom_y_range] as [number, number],
|
|
1608
|
-
initial_y2_range: [...zoom_y2_range] as [number, number],
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
function handle_touch_move(evt: TouchEvent) {
|
|
1613
|
-
if (!touch_state || evt.touches.length !== 2) return
|
|
1614
|
-
evt.preventDefault()
|
|
1615
|
-
|
|
1616
|
-
const [t1, t2] = Array.from(evt.touches)
|
|
1617
|
-
const [s1, s2] = touch_state.start_touches
|
|
1618
|
-
|
|
1619
|
-
// Calculate center movement for pan
|
|
1620
|
-
const start_center = { x: (s1.x + s2.x) / 2, y: (s1.y + s2.y) / 2 }
|
|
1621
|
-
const curr_center = {
|
|
1622
|
-
x: (t1.clientX + t2.clientX) / 2,
|
|
1623
|
-
y: (t1.clientY + t2.clientY) / 2,
|
|
1624
|
-
}
|
|
1625
|
-
const dx = curr_center.x - start_center.x
|
|
1626
|
-
const dy = curr_center.y - start_center.y
|
|
1627
|
-
|
|
1628
|
-
// Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
|
|
1629
|
-
const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
|
|
1630
|
-
// Guard against zero-distance pinch to avoid Infinity scale
|
|
1631
|
-
if (start_dist < Number.EPSILON) return
|
|
1632
|
-
const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
|
|
1633
|
-
const scale = curr_dist / start_dist
|
|
1634
|
-
|
|
1635
|
-
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
1636
|
-
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
1637
|
-
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
1638
|
-
|
|
1639
|
-
// If scale changed significantly, treat as pinch-zoom
|
|
1640
|
-
// Also guard against scale being too small to avoid division by zero
|
|
1641
|
-
if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
|
|
1642
|
-
// Pinch zoom centered on gesture center
|
|
1643
|
-
// Divide by scale so spread (scale > 1) = smaller span (zoom in)
|
|
1644
|
-
const x_span = touch_state.initial_x_range[1] - touch_state.initial_x_range[0]
|
|
1645
|
-
const x2_span = touch_state.initial_x2_range[1] -
|
|
1646
|
-
touch_state.initial_x2_range[0]
|
|
1647
|
-
const y_span = touch_state.initial_y_range[1] - touch_state.initial_y_range[0]
|
|
1648
|
-
const y2_span = touch_state.initial_y2_range[1] -
|
|
1649
|
-
touch_state.initial_y2_range[0]
|
|
1650
|
-
const x_center =
|
|
1651
|
-
(touch_state.initial_x_range[0] + touch_state.initial_x_range[1]) / 2
|
|
1652
|
-
const x2_center =
|
|
1653
|
-
(touch_state.initial_x2_range[0] + touch_state.initial_x2_range[1]) / 2
|
|
1654
|
-
const y_center =
|
|
1655
|
-
(touch_state.initial_y_range[0] + touch_state.initial_y_range[1]) / 2
|
|
1656
|
-
const y2_center =
|
|
1657
|
-
(touch_state.initial_y2_range[0] + touch_state.initial_y2_range[1]) / 2
|
|
1658
|
-
|
|
1659
|
-
zoom_x_range = [x_center - x_span / scale / 2, x_center + x_span / scale / 2]
|
|
1660
|
-
zoom_x2_range = [
|
|
1661
|
-
x2_center - x2_span / scale / 2,
|
|
1662
|
-
x2_center + x2_span / scale / 2,
|
|
1663
|
-
]
|
|
1664
|
-
zoom_y_range = [y_center - y_span / scale / 2, y_center + y_span / scale / 2]
|
|
1665
|
-
zoom_y2_range = get_synced_y2(zoom_y_range, [
|
|
1666
|
-
y2_center - y2_span / scale / 2,
|
|
1667
|
-
y2_center + y2_span / scale / 2,
|
|
1668
|
-
])
|
|
1669
|
-
} else {
|
|
1670
|
-
// Pan
|
|
1671
|
-
const x_delta = pixels_to_data_delta(
|
|
1672
|
-
-dx,
|
|
1673
|
-
touch_state.initial_x_range,
|
|
1674
|
-
plot_width,
|
|
1675
|
-
)
|
|
1676
|
-
const x2_delta = pixels_to_data_delta(
|
|
1677
|
-
-dx,
|
|
1678
|
-
touch_state.initial_x2_range,
|
|
1679
|
-
plot_width,
|
|
1680
|
-
)
|
|
1681
|
-
const y_delta = pixels_to_data_delta(
|
|
1682
|
-
dy,
|
|
1683
|
-
touch_state.initial_y_range,
|
|
1684
|
-
plot_height,
|
|
1685
|
-
)
|
|
1686
|
-
const y2_delta = pixels_to_data_delta(
|
|
1687
|
-
dy,
|
|
1688
|
-
touch_state.initial_y2_range,
|
|
1689
|
-
plot_height,
|
|
1690
|
-
)
|
|
1691
|
-
zoom_x_range = pan_range(touch_state.initial_x_range, x_delta)
|
|
1692
|
-
zoom_x2_range = pan_range(touch_state.initial_x2_range, x2_delta)
|
|
1693
|
-
zoom_y_range = pan_range(touch_state.initial_y_range, y_delta)
|
|
1694
|
-
zoom_y2_range = get_synced_y2(
|
|
1695
|
-
zoom_y_range,
|
|
1696
|
-
pan_range(touch_state.initial_y2_range, y2_delta),
|
|
1697
|
-
)
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
function handle_touch_end() {
|
|
1702
|
-
touch_state = null
|
|
1703
|
-
}
|
|
1060
|
+
ranges.current = {
|
|
1061
|
+
x: [...auto_x_range] as Vec2,
|
|
1062
|
+
x2: [...auto_x2_range] as Vec2,
|
|
1063
|
+
y: [...auto_y_range] as Vec2,
|
|
1064
|
+
y2: get_synced_y2(auto_y_range, [...auto_y2_range] as Vec2),
|
|
1065
|
+
}
|
|
1066
|
+
// Also reset axis props so future data changes recalculate auto ranges
|
|
1067
|
+
x_axis = { ...x_axis, range: [null, null] }
|
|
1068
|
+
x2_axis = { ...x2_axis, range: [null, null] }
|
|
1069
|
+
y_axis = { ...y_axis, range: [null, null] }
|
|
1070
|
+
y2_axis = { ...y2_axis, range: [null, null] }
|
|
1071
|
+
},
|
|
1072
|
+
// Live tooltip while rect-dragging: update for the closest point inside the
|
|
1073
|
+
// plot bounds, clear when the cursor leaves the svg
|
|
1074
|
+
on_drag_move: (coords, inside_svg) => {
|
|
1075
|
+
if (inside_svg) update_tooltip_point(coords.x, coords.y)
|
|
1076
|
+
else tooltip_point = null
|
|
1077
|
+
},
|
|
1078
|
+
})
|
|
1079
|
+
onDestroy(() => pan_zoom.destroy())
|
|
1704
1080
|
|
|
1705
1081
|
// tooltip logic: find closest point and update tooltip state
|
|
1706
1082
|
function update_tooltip_point(x_rel: number, y_rel: number, evt?: MouseEvent) {
|
|
@@ -1832,7 +1208,7 @@
|
|
|
1832
1208
|
legend_manual_position = { x: constrained_x, y: constrained_y }
|
|
1833
1209
|
}
|
|
1834
1210
|
|
|
1835
|
-
function get_screen_coords(point: Point, data_series?: DataSeries):
|
|
1211
|
+
function get_screen_coords(point: Point, data_series?: DataSeries): Vec2 {
|
|
1836
1212
|
// convert data coordinates to potentially non-finite screen coordinates
|
|
1837
1213
|
const use_x2 = data_series?.x_axis === `x2`
|
|
1838
1214
|
const active_x_scale = use_x2 ? x2_scale_fn : x_scale_fn
|
|
@@ -1906,7 +1282,6 @@
|
|
|
1906
1282
|
return construct_handler_props(tooltip_point)
|
|
1907
1283
|
})
|
|
1908
1284
|
|
|
1909
|
-
let using_controls = $derived(controls.show)
|
|
1910
1285
|
let has_multiple_series = $derived(series_with_ids.filter(Boolean).length > 1)
|
|
1911
1286
|
|
|
1912
1287
|
// Precompute non-click event names from point_events so we don't rebuild
|
|
@@ -1943,32 +1318,12 @@
|
|
|
1943
1318
|
set_loading: (axis) => (axis_loading = axis),
|
|
1944
1319
|
}
|
|
1945
1320
|
|
|
1946
|
-
//
|
|
1947
|
-
|
|
1948
|
-
const handle_axis_change = $derived(create_axis_change_handler(
|
|
1321
|
+
// Shared handler + one-shot auto-load bound to this component's state
|
|
1322
|
+
const { handle_axis_change, try_auto_load } = create_axis_loader(
|
|
1949
1323
|
axis_state,
|
|
1950
|
-
data_loader,
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
))
|
|
1954
|
-
|
|
1955
|
-
let auto_load_attempted = false // prevent infinite retries on failure
|
|
1956
|
-
|
|
1957
|
-
// Auto-load data if series is empty but options exist (runs once)
|
|
1958
|
-
$effect(() => {
|
|
1959
|
-
if (series.length === 0 && data_loader && !auto_load_attempted) {
|
|
1960
|
-
// Check x-axis first, then y-axis
|
|
1961
|
-
if (x_axis.options?.length) {
|
|
1962
|
-
auto_load_attempted = true
|
|
1963
|
-
const first_key = x_axis.selected_key ?? x_axis.options[0].key
|
|
1964
|
-
handle_axis_change(`x`, first_key).catch(() => {})
|
|
1965
|
-
} else if (y_axis.options?.length) {
|
|
1966
|
-
auto_load_attempted = true
|
|
1967
|
-
const first_key = y_axis.selected_key ?? y_axis.options[0].key
|
|
1968
|
-
handle_axis_change(`y`, first_key).catch(() => {})
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
})
|
|
1324
|
+
() => ({ data_loader, on_axis_change, on_error }),
|
|
1325
|
+
)
|
|
1326
|
+
$effect(try_auto_load)
|
|
1972
1327
|
</script>
|
|
1973
1328
|
|
|
1974
1329
|
{#snippet fill_regions_layer(fills: typeof computed_fills)}
|
|
@@ -2034,11 +1389,9 @@
|
|
|
2034
1389
|
evt.preventDefault()
|
|
2035
1390
|
fullscreen = false
|
|
2036
1391
|
}
|
|
2037
|
-
|
|
2038
|
-
}}
|
|
2039
|
-
onkeyup={(evt) => {
|
|
2040
|
-
if (evt.key === `Shift`) shift_held = false
|
|
1392
|
+
pan_zoom.on_window_key_down(evt)
|
|
2041
1393
|
}}
|
|
1394
|
+
onkeyup={pan_zoom.on_window_key_up}
|
|
2042
1395
|
/>
|
|
2043
1396
|
|
|
2044
1397
|
<div
|
|
@@ -2065,45 +1418,27 @@
|
|
|
2065
1418
|
([final_x_axis.label, final_y_axis.label].filter(Boolean).join(` vs `) ||
|
|
2066
1419
|
`Scatter plot`)}
|
|
2067
1420
|
tabindex="0"
|
|
2068
|
-
onfocusin={() => (
|
|
2069
|
-
onfocusout={() => (
|
|
1421
|
+
onfocusin={() => pan_zoom.set_focused(true)}
|
|
1422
|
+
onfocusout={() => pan_zoom.set_focused(false)}
|
|
2070
1423
|
onmouseenter={() => (hovered = true)}
|
|
2071
|
-
onmousedown={
|
|
1424
|
+
onmousedown={pan_zoom.on_mouse_down}
|
|
2072
1425
|
onmousemove={(evt: MouseEvent) => {
|
|
2073
1426
|
// Only find closest point if not actively dragging
|
|
2074
|
-
if (!
|
|
1427
|
+
if (!pan_zoom.drag_start && !pan_zoom.is_pan_dragging) on_mouse_move(evt)
|
|
2075
1428
|
}}
|
|
2076
1429
|
onmouseleave={() => {
|
|
2077
1430
|
hovered = false
|
|
2078
1431
|
tooltip_point = null
|
|
2079
1432
|
on_point_hover?.(null)
|
|
2080
1433
|
}}
|
|
2081
|
-
ondblclick={
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
zoom_x2_range = [...auto_x2_range] as [number, number]
|
|
2090
|
-
zoom_y_range = [...auto_y_range] as [number, number]
|
|
2091
|
-
zoom_y2_range = get_synced_y2(auto_y_range, [...auto_y2_range] as Vec2)
|
|
2092
|
-
// Also reset axis props so future data changes recalculate auto ranges
|
|
2093
|
-
x_axis = { ...x_axis, range: [null, null] }
|
|
2094
|
-
x2_axis = { ...x2_axis, range: [null, null] }
|
|
2095
|
-
y_axis = { ...y_axis, range: [null, null] }
|
|
2096
|
-
y2_axis = { ...y2_axis, range: [null, null] }
|
|
2097
|
-
}}
|
|
2098
|
-
onwheel={handle_wheel}
|
|
2099
|
-
ontouchstart={handle_touch_start}
|
|
2100
|
-
ontouchmove={handle_touch_move}
|
|
2101
|
-
ontouchend={handle_touch_end}
|
|
2102
|
-
style:cursor={pan_drag_state
|
|
2103
|
-
? `grabbing`
|
|
2104
|
-
: shift_held && pan?.enabled !== false
|
|
2105
|
-
? `grab`
|
|
2106
|
-
: `crosshair`}
|
|
1434
|
+
ondblclick={pan_zoom.reset_view}
|
|
1435
|
+
onkeydown={pan_zoom.on_key_down}
|
|
1436
|
+
onwheel={pan_zoom.on_wheel}
|
|
1437
|
+
ontouchstart={pan_zoom.on_touch_start}
|
|
1438
|
+
ontouchmove={pan_zoom.on_touch_move}
|
|
1439
|
+
ontouchend={pan_zoom.on_touch_end}
|
|
1440
|
+
ontouchcancel={pan_zoom.on_touch_end}
|
|
1441
|
+
style:cursor={pan_zoom.cursor}
|
|
2107
1442
|
>
|
|
2108
1443
|
{@render user_content?.({
|
|
2109
1444
|
height,
|
|
@@ -2191,9 +1526,6 @@
|
|
|
2191
1526
|
|
|
2192
1527
|
<!-- Y2-axis (Right) -->
|
|
2193
1528
|
{#if y2_points.length > 0}
|
|
2194
|
-
{@const y2_inside = final_y2_axis.tick?.label?.inside ?? false}
|
|
2195
|
-
{@const y2_tick_shift = y2_inside ? 0 : (final_y2_axis.tick?.label?.shift?.x ?? 0) + 8}
|
|
2196
|
-
{@const y2_tick_width = y2_inside ? 0 : tick_label_widths.y2_max}
|
|
2197
1529
|
<PlotAxis
|
|
2198
1530
|
side="y2"
|
|
2199
1531
|
ticks={y2_tick_values}
|
|
@@ -2207,8 +1539,7 @@
|
|
|
2207
1539
|
domain={[y2_min, y2_max]}
|
|
2208
1540
|
unit_on_first_tick
|
|
2209
1541
|
tick_label={(tick) => get_tick_label(tick, final_y2_axis.ticks)}
|
|
2210
|
-
label_x={width
|
|
2211
|
-
(final_y2_axis.label_shift?.x ?? 0)}
|
|
1542
|
+
label_x={y2_axis_label_x(final_y2_axis, width, pad.r, tick_label_widths.y2_max)}
|
|
2212
1543
|
label_y={pad.t + (height - pad.t - pad.b) / 2 + (final_y2_axis.label_shift?.y ?? 0)}
|
|
2213
1544
|
axis_loading={axis_loading === `y2`}
|
|
2214
1545
|
on_axis_change={(key) => handle_axis_change(`y2`, key)}
|
|
@@ -2238,7 +1569,7 @@
|
|
|
2238
1569
|
|
|
2239
1570
|
<!-- Tooltip rendered inside overlay (moved outside SVG for stacking above colorbar) -->
|
|
2240
1571
|
|
|
2241
|
-
<ZoomRect start={
|
|
1572
|
+
<ZoomRect start={pan_zoom.drag_start} current={pan_zoom.drag_current} />
|
|
2242
1573
|
|
|
2243
1574
|
<ZeroLines
|
|
2244
1575
|
display={final_display}
|
|
@@ -2246,10 +1577,10 @@
|
|
|
2246
1577
|
{x2_scale_fn}
|
|
2247
1578
|
{y_scale_fn}
|
|
2248
1579
|
{y2_scale_fn}
|
|
2249
|
-
x_range={
|
|
2250
|
-
x2_range={
|
|
2251
|
-
y_range={
|
|
2252
|
-
y2_range={
|
|
1580
|
+
x_range={ranges.current.x}
|
|
1581
|
+
x2_range={ranges.current.x2}
|
|
1582
|
+
y_range={ranges.current.y}
|
|
1583
|
+
y2_range={ranges.current.y2}
|
|
2253
1584
|
x_scale_type={final_x_axis.scale_type}
|
|
2254
1585
|
x2_scale_type={final_x2_axis.scale_type}
|
|
2255
1586
|
y_scale_type={final_y_axis.scale_type}
|
|
@@ -2300,7 +1631,7 @@
|
|
|
2300
1631
|
{@const finite_screen_points = all_line_points
|
|
2301
1632
|
.map((point) => get_screen_coords(point, series_data))
|
|
2302
1633
|
.filter(([sx, sy]) => isFinite(sx) && isFinite(sy))}
|
|
2303
|
-
{@const apply_line_controls =
|
|
1634
|
+
{@const apply_line_controls = controls.show &&
|
|
2304
1635
|
(!has_multiple_series ||
|
|
2305
1636
|
series_data._id === series_with_ids[selected_series_idx]?._id)}
|
|
2306
1637
|
{@const ls = series_data.line_style}
|
|
@@ -2322,7 +1653,7 @@
|
|
|
2322
1653
|
line_width={(tc(`line.width`) ? styles.line?.width : null) ?? ls?.stroke_width ?? 2}
|
|
2323
1654
|
line_dash={(tc(`line.dash`) ? styles.line?.dash : null) ?? ls?.line_dash}
|
|
2324
1655
|
area_color="transparent"
|
|
2325
|
-
{
|
|
1656
|
+
line_tween={effective_line_tween}
|
|
2326
1657
|
/>
|
|
2327
1658
|
{/if}
|
|
2328
1659
|
</g>
|
|
@@ -2371,7 +1702,7 @@
|
|
|
2371
1702
|
{@const screen_y = isFinite(raw_screen_y)
|
|
2372
1703
|
? raw_screen_y
|
|
2373
1704
|
: (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn).range()[0]}
|
|
2374
|
-
{@const apply_controls =
|
|
1705
|
+
{@const apply_controls = controls.show &&
|
|
2375
1706
|
(!has_multiple_series ||
|
|
2376
1707
|
series_data._id === series_with_ids[selected_series_idx]?._id)}
|
|
2377
1708
|
{@const pt = point.point_style}
|
|
@@ -2446,60 +1777,19 @@
|
|
|
2446
1777
|
|
|
2447
1778
|
<!-- Tooltip overlay above all plot overlays (legend, colorbar) -->
|
|
2448
1779
|
{#if handler_props && hovered && tooltip_point}
|
|
2449
|
-
{@const {
|
|
2450
|
-
{@const
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
color === `none` ||
|
|
2455
|
-
color === `transparent` ||
|
|
2456
|
-
/rgba\([^)]+[,/]\s*0(\.0*)?\s*\)$/.test(color)}
|
|
2457
|
-
{@const tooltip_bg_color = (() => {
|
|
2458
|
-
const scale_color = color_value != null
|
|
2459
|
-
? color_scale_fn(color_value)
|
|
2460
|
-
: undefined
|
|
2461
|
-
if (!is_transparent_or_none(scale_color)) return scale_color
|
|
2462
|
-
const fill_color = point_style?.fill
|
|
2463
|
-
if (!is_transparent_or_none(fill_color)) return fill_color
|
|
2464
|
-
if (series_markers?.includes(`points`)) {
|
|
2465
|
-
const stroke_color = point_style?.stroke
|
|
2466
|
-
if (!is_transparent_or_none(stroke_color)) return stroke_color
|
|
2467
|
-
}
|
|
2468
|
-
if (series_markers?.includes(`line`)) {
|
|
2469
|
-
const line_style = hovered_series?.line_style ?? {}
|
|
2470
|
-
const first_point_style = Array.isArray(hovered_series?.point_style)
|
|
2471
|
-
? hovered_series?.point_style[0]
|
|
2472
|
-
: hovered_series?.point_style
|
|
2473
|
-
const first_color_value = hovered_series?.color_values?.[0]
|
|
2474
|
-
let line_color_candidate = line_style.stroke
|
|
2475
|
-
if (is_transparent_or_none(line_color_candidate)) {line_color_candidate =
|
|
2476
|
-
first_point_style?.fill}
|
|
2477
|
-
if (
|
|
2478
|
-
is_transparent_or_none(line_color_candidate) && first_color_value != null
|
|
2479
|
-
) line_color_candidate = color_scale_fn(first_color_value)
|
|
2480
|
-
if (
|
|
2481
|
-
is_transparent_or_none(line_color_candidate) &&
|
|
2482
|
-
series_markers?.includes(`points`)
|
|
2483
|
-
) line_color_candidate = first_point_style?.stroke
|
|
2484
|
-
if (!is_transparent_or_none(line_color_candidate)) return line_color_candidate
|
|
2485
|
-
}
|
|
2486
|
-
return `rgba(0, 0, 0, 0.7)`
|
|
2487
|
-
})()}
|
|
2488
|
-
{@const tooltip_pos = constrain_tooltip_position(
|
|
2489
|
-
handler_props.cx,
|
|
2490
|
-
handler_props.cy,
|
|
2491
|
-
tooltip_el?.offsetWidth ?? 120,
|
|
2492
|
-
tooltip_el?.offsetHeight ?? 50,
|
|
2493
|
-
width,
|
|
2494
|
-
height,
|
|
2495
|
-
{ offset_x: 10, offset_y: 5 },
|
|
1780
|
+
{@const { point_label, series_idx } = tooltip_point}
|
|
1781
|
+
{@const tooltip_bg_color = pick_tooltip_bg(
|
|
1782
|
+
tooltip_point,
|
|
1783
|
+
series_with_ids[series_idx],
|
|
1784
|
+
color_scale_fn,
|
|
2496
1785
|
)}
|
|
2497
1786
|
<PlotTooltip
|
|
2498
|
-
x={
|
|
2499
|
-
y={
|
|
2500
|
-
offset={{ x:
|
|
1787
|
+
x={handler_props.cx}
|
|
1788
|
+
y={handler_props.cy}
|
|
1789
|
+
offset={{ x: 10, y: 5 }}
|
|
1790
|
+
constrain_to={{ width, height }}
|
|
1791
|
+
fallback_size={{ width: 120, height: 50 }}
|
|
2501
1792
|
bg_color={tooltip_bg_color}
|
|
2502
|
-
bind:wrapper={tooltip_el}
|
|
2503
1793
|
>
|
|
2504
1794
|
{#if tooltip}
|
|
2505
1795
|
{@render tooltip(handler_props)}
|
|
@@ -2558,15 +1848,15 @@
|
|
|
2558
1848
|
] as Vec2}
|
|
2559
1849
|
<div
|
|
2560
1850
|
bind:this={colorbar_element}
|
|
2561
|
-
onmouseenter={() =>
|
|
2562
|
-
onmouseleave={() =>
|
|
1851
|
+
onmouseenter={() => colorbar_tween.set_locked(true)}
|
|
1852
|
+
onmouseleave={() => colorbar_tween.set_locked(false)}
|
|
2563
1853
|
class="colorbar-wrapper"
|
|
2564
1854
|
role="img"
|
|
2565
1855
|
aria-label="Color scale legend"
|
|
2566
1856
|
style={`${
|
|
2567
1857
|
// explicit wrapper_style or auto-outside places the colorbar; else auto-placement coords
|
|
2568
1858
|
effective_cbar_wrapper_style ??
|
|
2569
|
-
`position: absolute; left: ${
|
|
1859
|
+
`position: absolute; left: ${colorbar_tween.coords.current.x}px; top: ${colorbar_tween.coords.current.y}px`}; pointer-events: auto;`}
|
|
2570
1860
|
>
|
|
2571
1861
|
<ColorBar
|
|
2572
1862
|
tick_labels={4}
|
|
@@ -2585,7 +1875,7 @@
|
|
|
2585
1875
|
<!-- Legend -->
|
|
2586
1876
|
<!-- Only render if multiple series or if legend prop was explicitly provided by user (even if empty object) -->
|
|
2587
1877
|
{#if legend != null && legend_data.length > 0 &&
|
|
2588
|
-
(legend_data.length > 1 || Object.keys(legend).length > 0)}
|
|
1878
|
+
(legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)}
|
|
2589
1879
|
{@const default_x = pad.l + 10}
|
|
2590
1880
|
{@const default_y = pad.t + 10}
|
|
2591
1881
|
{@const current_x = legend_is_dragging && legend_manual_position
|
|
@@ -2593,14 +1883,14 @@
|
|
|
2593
1883
|
: legend_auto_outside
|
|
2594
1884
|
? legend_outside_x
|
|
2595
1885
|
: legend_placement
|
|
2596
|
-
?
|
|
1886
|
+
? legend_tween.coords.current.x
|
|
2597
1887
|
: default_x}
|
|
2598
1888
|
{@const current_y = legend_is_dragging && legend_manual_position
|
|
2599
1889
|
? legend_manual_position.y
|
|
2600
1890
|
: legend_auto_outside
|
|
2601
1891
|
? legend_outside_y
|
|
2602
1892
|
: legend_placement
|
|
2603
|
-
?
|
|
1893
|
+
? legend_tween.coords.current.y
|
|
2604
1894
|
: default_y}
|
|
2605
1895
|
<PlotLegend
|
|
2606
1896
|
bind:root_element={legend_element}
|
|
@@ -2608,32 +1898,29 @@
|
|
|
2608
1898
|
on_drag_start={handle_legend_drag_start}
|
|
2609
1899
|
on_drag={handle_legend_drag}
|
|
2610
1900
|
on_drag_end={() => (legend_is_dragging = false)}
|
|
2611
|
-
on_hover_change={
|
|
2612
|
-
on_item_hover={(
|
|
2613
|
-
(
|
|
2614
|
-
|
|
2615
|
-
|
|
1901
|
+
on_hover_change={legend_tween.set_locked}
|
|
1902
|
+
on_item_hover={(item) => {
|
|
1903
|
+
if (item?.item_type === `fill`) {
|
|
1904
|
+
// highlight the matching fill in the plot (same state plot fill-hover uses), but skip
|
|
1905
|
+
// hidden fills since they render nothing and would mark the legend item active for naught
|
|
1906
|
+
const fill = computed_fills.find((entry) => entry.idx === item.fill_idx)
|
|
1907
|
+
hovered_fill_key = fill && fill.visible !== false ? fill.hover_key : null
|
|
1908
|
+
hovered_legend_series_idx = null
|
|
1909
|
+
} else {
|
|
1910
|
+
hovered_legend_series_idx = item != null && item.series_idx >= 0
|
|
1911
|
+
? item.series_idx
|
|
1912
|
+
: null
|
|
1913
|
+
hovered_fill_key = null
|
|
1914
|
+
}
|
|
1915
|
+
}}
|
|
2616
1916
|
active_series_idx={tooltip_point?.series_idx ?? hovered_legend_series_idx}
|
|
1917
|
+
active_fill_idx={computed_fills.find((fill) => fill.hover_key === hovered_fill_key)?.idx ??
|
|
1918
|
+
null}
|
|
2617
1919
|
draggable={legend?.draggable ?? true}
|
|
2618
1920
|
{...legend}
|
|
2619
|
-
on_toggle={legend?.on_toggle ??
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
})}
|
|
2623
|
-
on_double_click={legend?.on_double_click ??
|
|
2624
|
-
((double_clicked_idx: number) => {
|
|
2625
|
-
const result = handle_legend_double_click(
|
|
2626
|
-
series,
|
|
2627
|
-
double_clicked_idx,
|
|
2628
|
-
prev_series_visibility,
|
|
2629
|
-
)
|
|
2630
|
-
series = result.series
|
|
2631
|
-
prev_series_visibility = result.prev_visibility
|
|
2632
|
-
})}
|
|
2633
|
-
on_group_toggle={legend?.on_group_toggle ??
|
|
2634
|
-
((_group_name: string, series_indices: number[]) => {
|
|
2635
|
-
series = toggle_group_visibility(series, series_indices)
|
|
2636
|
-
})}
|
|
1921
|
+
on_toggle={legend?.on_toggle ?? legend_vis.on_toggle}
|
|
1922
|
+
on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
|
|
1923
|
+
on_group_toggle={legend?.on_group_toggle ?? legend_vis.on_group_toggle}
|
|
2637
1924
|
on_fill_toggle={(source_type: `fill_region` | `error_band`, source_idx: number) => {
|
|
2638
1925
|
// Only fill_regions can be toggled (error_bands are not bindable)
|
|
2639
1926
|
if (source_type === `fill_region`) {
|
|
@@ -2707,8 +1994,10 @@
|
|
|
2707
1994
|
background: var(--scatter-fullscreen-bg, var(--scatter-bg, var(--plot-bg)));
|
|
2708
1995
|
max-height: none !important;
|
|
2709
1996
|
overflow: hidden;
|
|
2710
|
-
/*
|
|
2711
|
-
|
|
1997
|
+
/* border-top (not padding-top): bind:clientHeight includes padding but excludes
|
|
1998
|
+
borders - padding made the chart overflow + clip its bottom 2em (x-axis title) */
|
|
1999
|
+
border-top: var(--plot-fullscreen-padding-top, 2em) solid
|
|
2000
|
+
var(--scatter-fullscreen-bg, var(--scatter-bg, var(--plot-bg, transparent)));
|
|
2712
2001
|
box-sizing: border-box;
|
|
2713
2002
|
}
|
|
2714
2003
|
/* Center the colorbar within its wrapper when shorter than it (e.g. capped by --cbar-max-height
|