matterviz 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +8 -3
- package/dist/brillouin/BrillouinZone.svelte.d.ts +2 -1
- package/dist/brillouin/BrillouinZoneScene.svelte +52 -6
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -0
- package/dist/brillouin/BrillouinZoneTooltip.svelte +16 -25
- package/dist/brillouin/compute.js +10 -14
- package/dist/chempot-diagram/ChemPotDiagram.svelte +14 -13
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +12 -15
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +8 -10
- package/dist/chempot-diagram/async-compute.svelte.js +3 -1
- package/dist/chempot-diagram/chempot-worker.js +2 -1
- package/dist/chempot-diagram/compute.d.ts +1 -1
- package/dist/chempot-diagram/compute.js +17 -19
- package/dist/colors/index.js +6 -5
- package/dist/composition/FormulaFilter.svelte +12 -6
- package/dist/composition/PieChart.svelte +6 -5
- package/dist/composition/chem-sys.d.ts +8 -0
- package/dist/composition/chem-sys.js +85 -0
- package/dist/composition/format.js +4 -2
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/composition/parse.js +25 -13
- package/dist/convex-hull/ConvexHull2D.svelte +12 -10
- package/dist/convex-hull/ConvexHull3D.svelte +5 -5
- package/dist/convex-hull/ConvexHull4D.svelte +5 -9
- package/dist/convex-hull/ConvexHullStats.svelte +12 -12
- package/dist/convex-hull/GasPressureControls.svelte +4 -4
- package/dist/convex-hull/TemperatureSlider.svelte +2 -2
- 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 +3 -0
- package/dist/convex-hull/helpers.js +17 -9
- package/dist/convex-hull/index.d.ts +1 -1
- package/dist/convex-hull/thermodynamics.js +83 -78
- package/dist/convex-hull/types.d.ts +1 -1
- package/dist/coordination/CoordinationBarPlot.svelte +23 -23
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSlice.svelte +13 -5
- package/dist/fermi-surface/FermiSurface.svelte +11 -5
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +3 -0
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +8 -34
- package/dist/fermi-surface/compute.js +59 -59
- package/dist/fermi-surface/export.js +3 -2
- package/dist/fermi-surface/parse.js +7 -4
- package/dist/fermi-surface/types.d.ts +1 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +23 -21
- package/dist/heatmap-matrix/index.js +1 -1
- package/dist/io/decompress.js +4 -2
- package/dist/io/export.d.ts +4 -4
- package/dist/io/export.js +47 -25
- package/dist/io/fetch.js +5 -1
- package/dist/io/file-drop.d.ts +1 -1
- package/dist/io/file-drop.js +35 -36
- package/dist/io/url-drop.js +64 -33
- package/dist/isosurface/parse.js +6 -7
- 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 +8 -7
- package/dist/layout/PropertyFilter.svelte +3 -2
- package/dist/layout/SettingsSection.svelte +1 -1
- 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 +5 -1
- package/dist/math.js +24 -9
- package/dist/overlays/DraggablePane.svelte +4 -4
- package/dist/periodic-table/PeriodicTable.svelte +20 -9
- 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.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +1 -1
- package/dist/phase-diagram/build-diagram.js +2 -2
- 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} +229 -587
- package/dist/plot/{BarPlot.svelte.d.ts → bar/BarPlot.svelte.d.ts} +5 -5
- 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} +6 -6
- 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 +1462 -0
- package/dist/plot/box/BoxPlot.svelte.d.ts +94 -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 +55 -0
- package/dist/plot/box/box-plot.js +126 -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 +16 -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.js → core/auto-place.js} +2 -2
- 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} +36 -33
- package/dist/plot/{ColorBar.svelte.d.ts → core/components/ColorBar.svelte.d.ts} +2 -2
- 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} +30 -13
- package/dist/plot/{PlotAxis.svelte → core/components/PlotAxis.svelte} +7 -5
- package/dist/plot/{PlotAxis.svelte.d.ts → core/components/PlotAxis.svelte.d.ts} +3 -2
- 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} +4 -4
- package/dist/plot/{ReferenceLine3D.svelte.d.ts → core/components/ReferenceLine3D.svelte.d.ts} +2 -2
- package/dist/plot/{ReferencePlane.svelte → core/components/ReferencePlane.svelte} +7 -7
- package/dist/plot/{ReferencePlane.svelte.d.ts → core/components/ReferencePlane.svelte.d.ts} +2 -2
- 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} +3 -5
- 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 +33 -0
- package/dist/plot/core/fill-utils.js +388 -0
- package/dist/plot/{hover-lock.svelte.js → core/hover-lock.svelte.js} +5 -6
- package/dist/plot/core/index.d.ts +10 -0
- package/dist/plot/core/index.js +11 -0
- package/dist/plot/core/interactions.d.ts +35 -0
- package/dist/plot/core/interactions.js +195 -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/{reference-line.d.ts → core/reference-line.d.ts} +1 -1
- package/dist/plot/{reference-line.js → core/reference-line.js} +23 -36
- package/dist/plot/{scales.d.ts → core/scales.d.ts} +2 -2
- package/dist/plot/{scales.js → core/scales.js} +84 -85
- package/dist/plot/core/svg.d.ts +2 -0
- package/dist/plot/core/svg.js +41 -0
- package/dist/plot/{types.d.ts → core/types.d.ts} +19 -79
- package/dist/plot/{types.js → core/types.js} +1 -1
- package/dist/plot/{utils → core/utils}/label-placement.d.ts +2 -2
- 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 +11 -0
- package/dist/plot/core/utils.js +27 -0
- package/dist/plot/{Histogram.svelte → histogram/Histogram.svelte} +154 -294
- 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 +700 -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 +187 -0
- package/dist/plot/{BinnedScatterPlot.svelte → scatter/BinnedScatterPlot.svelte} +61 -59
- package/dist/plot/{BinnedScatterPlot.svelte.d.ts → scatter/BinnedScatterPlot.svelte.d.ts} +4 -4
- 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} +221 -642
- package/dist/plot/{ScatterPlot.svelte.d.ts → scatter/ScatterPlot.svelte.d.ts} +7 -7
- package/dist/plot/{ScatterPlotControls.svelte → scatter/ScatterPlotControls.svelte} +6 -5
- package/dist/plot/{ScatterPlotControls.svelte.d.ts → scatter/ScatterPlotControls.svelte.d.ts} +1 -1
- 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} +3 -3
- 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} +12 -10
- package/dist/plot/{ScatterPlot3D.svelte.d.ts → scatter-3d/ScatterPlot3D.svelte.d.ts} +7 -7
- package/dist/plot/{ScatterPlot3DControls.svelte → scatter-3d/ScatterPlot3DControls.svelte} +5 -4
- package/dist/plot/{ScatterPlot3DControls.svelte.d.ts → scatter-3d/ScatterPlot3DControls.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPlot3DScene.svelte → scatter-3d/ScatterPlot3DScene.svelte} +11 -11
- package/dist/plot/{ScatterPlot3DScene.svelte.d.ts → scatter-3d/ScatterPlot3DScene.svelte.d.ts} +3 -3
- package/dist/plot/{Surface3D.svelte → scatter-3d/Surface3D.svelte} +1 -1
- package/dist/plot/{Surface3D.svelte.d.ts → scatter-3d/Surface3D.svelte.d.ts} +1 -1
- 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 +1045 -0
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -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 +266 -0
- package/dist/rdf/RdfPlot.svelte +2 -1
- package/dist/rdf/calc-rdf.js +11 -24
- package/dist/sanitize.js +1 -1
- package/dist/settings.d.ts +65 -1
- package/dist/settings.js +262 -0
- package/dist/spectral/Bands.svelte +39 -29
- package/dist/spectral/Bands.svelte.d.ts +3 -4
- package/dist/spectral/BandsAndDos.svelte +1 -1
- package/dist/spectral/BrillouinBandsDos.svelte +39 -27
- package/dist/spectral/Dos.svelte +10 -19
- package/dist/spectral/Dos.svelte.d.ts +2 -2
- package/dist/spectral/helpers.d.ts +3 -1
- package/dist/spectral/helpers.js +95 -29
- package/dist/structure/AtomLegend.svelte +8 -9
- package/dist/structure/CellSelect.svelte +1 -2
- package/dist/structure/Cylinder.svelte +12 -8
- package/dist/structure/Cylinder.svelte.d.ts +4 -1
- package/dist/structure/Structure.svelte +78 -72
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +5 -6
- package/dist/structure/StructureScene.svelte +11 -10
- package/dist/structure/atom-properties.js +6 -6
- package/dist/structure/bond-order-perception.js +1 -1
- package/dist/structure/bonding.d.ts +1 -0
- package/dist/structure/bonding.js +43 -15
- package/dist/structure/export.js +27 -23
- package/dist/structure/index.d.ts +2 -4
- package/dist/structure/index.js +1 -3
- package/dist/structure/label-placement.js +4 -4
- package/dist/structure/measure.d.ts +3 -2
- package/dist/structure/measure.js +6 -5
- package/dist/structure/parse.js +121 -103
- package/dist/structure/pbc.js +4 -0
- package/dist/symmetry/SymmetryStats.svelte +2 -2
- package/dist/symmetry/index.d.ts +1 -1
- package/dist/symmetry/index.js +22 -24
- package/dist/symmetry/spacegroups.d.ts +7 -0
- package/dist/symmetry/spacegroups.js +48 -13
- package/dist/table/HeatmapTable.svelte +63 -11
- 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 +66 -40
- package/dist/trajectory/Trajectory.svelte.d.ts +2 -1
- package/dist/trajectory/TrajectoryExportPane.svelte +2 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +2 -1
- package/dist/trajectory/format-detect.d.ts +1 -0
- package/dist/trajectory/format-detect.js +25 -11
- package/dist/trajectory/frame-reader.js +17 -50
- package/dist/trajectory/helpers.js +1 -1
- package/dist/trajectory/index.js +1 -1
- package/dist/trajectory/parse/hdf5.js +1 -1
- package/dist/trajectory/parse/index.js +14 -6
- package/dist/trajectory/parse/vasp.js +36 -17
- package/dist/trajectory/parse/xyz.d.ts +24 -0
- package/dist/trajectory/parse/xyz.js +102 -89
- package/dist/trajectory/plotting.d.ts +1 -1
- package/dist/trajectory/plotting.js +15 -15
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +6 -4
- package/dist/xrd/XrdPlot.svelte +2 -1
- package/dist/xrd/calc-xrd.js +15 -12
- package/dist/xrd/parse.js +2 -2
- package/package.json +22 -18
- 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/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -101
- 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/{auto-place.d.ts → core/auto-place.d.ts} +0 -0
- /package/dist/plot/{Line.svelte.d.ts → core/components/Line.svelte.d.ts} +0 -0
- /package/dist/plot/{PortalSelect.svelte.d.ts → core/components/PortalSelect.svelte.d.ts} +0 -0
- /package/dist/plot/{hover-lock.svelte.d.ts → core/hover-lock.svelte.d.ts} +0 -0
- /package/dist/plot/{utils → core/utils}/label-placement.js +0 -0
- /package/dist/plot/{binned-scatter-types.js → scatter/binned-scatter-types.js} +0 -0
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
lang="ts"
|
|
3
3
|
generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
|
|
4
4
|
>
|
|
5
|
-
import type { D3ColorSchemeName, D3InterpolateName } from '
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import type { Point2D, Vec2 } from '../math'
|
|
5
|
+
import type { D3ColorSchemeName, 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,
|
|
@@ -32,7 +31,7 @@
|
|
|
32
31
|
ScatterHandlerProps,
|
|
33
32
|
StyleOverrides,
|
|
34
33
|
UserContentProps,
|
|
35
|
-
} from '
|
|
34
|
+
} from '..'
|
|
36
35
|
import {
|
|
37
36
|
ColorBar,
|
|
38
37
|
compute_element_placement,
|
|
@@ -47,86 +46,79 @@
|
|
|
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
|
-
get_series_color,
|
|
61
|
-
get_series_symbol,
|
|
62
|
-
process_prop,
|
|
63
|
-
} from './data-transform'
|
|
64
|
-
import { AXIS_DEFAULTS } from './defaults'
|
|
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'
|
|
65
59
|
import {
|
|
66
60
|
create_dimension_tracker,
|
|
67
61
|
create_hover_lock,
|
|
68
|
-
} from '
|
|
62
|
+
} from '../core/hover-lock.svelte'
|
|
69
63
|
import {
|
|
70
64
|
DEFAULT_MARKERS,
|
|
71
65
|
get_scale_type_name,
|
|
72
66
|
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'
|
|
67
|
+
} from '../core/types'
|
|
68
|
+
import { compute_label_positions } from '../core/utils/label-placement'
|
|
69
|
+
import { create_legend_visibility } from '../core/utils/series-visibility'
|
|
70
|
+
import { DEFAULTS } from '../../settings'
|
|
82
71
|
import { extent } from 'd3-array'
|
|
83
72
|
import { scaleTime } from 'd3-scale'
|
|
84
73
|
import type { ComponentProps, Snippet } from 'svelte'
|
|
85
|
-
import { untrack } from 'svelte'
|
|
74
|
+
import { onDestroy, untrack } from 'svelte'
|
|
86
75
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
87
76
|
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
88
77
|
import { SvelteSet } from 'svelte/reactivity'
|
|
89
|
-
import type {
|
|
78
|
+
import type { Pt } from '../core/fill-utils'
|
|
90
79
|
import {
|
|
91
|
-
|
|
92
|
-
apply_where_condition,
|
|
93
|
-
clamp_for_log_scale,
|
|
80
|
+
compute_fill_segments,
|
|
94
81
|
convert_error_band_to_fill_region,
|
|
95
82
|
generate_fill_path,
|
|
96
|
-
|
|
97
|
-
resolve_boundary,
|
|
98
|
-
} from './fill-utils'
|
|
83
|
+
} from '../core/fill-utils'
|
|
99
84
|
import {
|
|
100
85
|
expand_range_if_needed,
|
|
101
86
|
get_relative_coords,
|
|
87
|
+
MIN_TOUCH_DISTANCE_PIXELS,
|
|
102
88
|
normalize_y2_sync,
|
|
103
|
-
|
|
89
|
+
pan_range_by_pixels,
|
|
104
90
|
PINCH_ZOOM_THRESHOLD,
|
|
105
|
-
|
|
91
|
+
remove_drag_listeners,
|
|
92
|
+
sorted_range,
|
|
106
93
|
sync_y2_range,
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
to_epoch_num,
|
|
95
|
+
zoom_range_by_factor,
|
|
96
|
+
} from '../core/interactions'
|
|
97
|
+
import type { Rect, Sides } from '../core/layout'
|
|
109
98
|
import {
|
|
110
99
|
calc_auto_padding,
|
|
111
|
-
constrain_tooltip_position,
|
|
112
100
|
filter_padding,
|
|
113
101
|
LABEL_GAP_DEFAULT,
|
|
102
|
+
y2_axis_label_x,
|
|
114
103
|
measure_full_footprint,
|
|
115
104
|
measure_max_tick_width,
|
|
116
105
|
sample_series_obstacle_points,
|
|
117
|
-
} from '
|
|
118
|
-
import type { IndexedRefLine } from '
|
|
119
|
-
import { group_ref_lines_by_z, index_ref_lines } from '
|
|
106
|
+
} from '../core/layout'
|
|
107
|
+
import type { IndexedRefLine } from '../core/reference-line'
|
|
108
|
+
import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
|
|
120
109
|
import {
|
|
121
110
|
create_color_scale,
|
|
122
111
|
create_scale,
|
|
123
112
|
create_size_scale,
|
|
124
113
|
generate_ticks,
|
|
125
114
|
get_nice_data_range,
|
|
126
|
-
} from '
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
115
|
+
} from '../core/scales'
|
|
116
|
+
import { resolve_line_tween, unique_id } from '../core/utils'
|
|
117
|
+
import {
|
|
118
|
+
build_legend_data,
|
|
119
|
+
filter_series_to_ranges,
|
|
120
|
+
pick_tooltip_bg,
|
|
121
|
+
} from './scatter-data'
|
|
130
122
|
|
|
131
123
|
let {
|
|
132
124
|
series = $bindable([]),
|
|
@@ -291,7 +283,7 @@
|
|
|
291
283
|
let touched = new SvelteSet<string>()
|
|
292
284
|
|
|
293
285
|
// Unique component ID to avoid clipPath conflicts between multiple instances
|
|
294
|
-
let component_id = $state(`scatter
|
|
286
|
+
let component_id = $state(unique_id(`scatter`))
|
|
295
287
|
let clip_path_id = $derived(`plot-area-clip-${component_id}`)
|
|
296
288
|
|
|
297
289
|
// Assign stable IDs to series for keying
|
|
@@ -317,7 +309,7 @@
|
|
|
317
309
|
let zoom_x2_range = $state<[number, number]>([0, 1])
|
|
318
310
|
let zoom_y_range = $state<[number, number]>([0, 1])
|
|
319
311
|
let zoom_y2_range = $state<[number, number]>([0, 1])
|
|
320
|
-
|
|
312
|
+
const legend_vis = create_legend_visibility(() => series, (next) => (series = next))
|
|
321
313
|
|
|
322
314
|
// Y2 axis sync configuration
|
|
323
315
|
let y2_sync_config = $derived(normalize_y2_sync(y2_axis?.sync))
|
|
@@ -390,9 +382,6 @@
|
|
|
390
382
|
colorbar_hover.cleanup()
|
|
391
383
|
})
|
|
392
384
|
|
|
393
|
-
// Tooltip element reference for dynamic sizing
|
|
394
|
-
let tooltip_el = $state<HTMLDivElement | undefined>()
|
|
395
|
-
|
|
396
385
|
// Module-level constants to avoid repeated allocations
|
|
397
386
|
// Create and categorize points in a single pass (instead of 3 separate iterations)
|
|
398
387
|
type SimplePoint = { x: number; y: number }
|
|
@@ -431,7 +420,7 @@
|
|
|
431
420
|
// Update padding when format or ticks change
|
|
432
421
|
$effect(() => {
|
|
433
422
|
const new_pad = width && height &&
|
|
434
|
-
(y_tick_values.length || y2_tick_values.length || x2_tick_values.length)
|
|
423
|
+
(y_tick_values.length > 0 || y2_tick_values.length > 0 || x2_tick_values.length > 0)
|
|
435
424
|
? calc_auto_padding({
|
|
436
425
|
padding,
|
|
437
426
|
default_padding,
|
|
@@ -650,7 +639,14 @@
|
|
|
650
639
|
if (result.changed) initial_y2_range = result.range
|
|
651
640
|
// Apply sync if enabled, otherwise use expanded range (or keep current if unchanged)
|
|
652
641
|
if (y2_sync_config.mode !== `none`) {
|
|
653
|
-
|
|
642
|
+
// untrack the read of zoom_y_range: this effect also writes it (fresh array per
|
|
643
|
+
// run when y.explicit), so a tracked read would loop until
|
|
644
|
+
// effect_update_depth_exceeded. Pan/zoom handlers sync y2 themselves.
|
|
645
|
+
zoom_y2_range = sync_y2_range(
|
|
646
|
+
untrack(() => zoom_y_range),
|
|
647
|
+
initial_y2_range,
|
|
648
|
+
y2_sync_config,
|
|
649
|
+
)
|
|
654
650
|
} else if (result.changed) {
|
|
655
651
|
zoom_y2_range = result.range
|
|
656
652
|
}
|
|
@@ -723,80 +719,29 @@
|
|
|
723
719
|
|
|
724
720
|
// Filter series data to only include points within bounds and augment with internal data
|
|
725
721
|
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
|
-
),
|
|
722
|
+
filter_series_to_ranges(series_with_ids, {
|
|
723
|
+
x: [x_min, x_max],
|
|
724
|
+
x2: [x2_min, x2_max],
|
|
725
|
+
y: [y_min, y_max],
|
|
726
|
+
y2: [y2_min, y2_max],
|
|
727
|
+
}),
|
|
798
728
|
)
|
|
799
729
|
|
|
730
|
+
// Tally line series/points to budget path-morph tweens (see resolve_line_tween).
|
|
731
|
+
// Disabling the morph for high-cardinality plots (e.g. phonon bands) keeps them
|
|
732
|
+
// snappy; Line.svelte short-circuits the Tween when duration <= 0.
|
|
733
|
+
let line_tween_load = $derived.by(() => {
|
|
734
|
+
if (!styles.show_lines) return { series: 0, points: 0 }
|
|
735
|
+
let [n_series, n_points] = [0, 0]
|
|
736
|
+
for (const srs of filtered_series ?? []) {
|
|
737
|
+
if (!(srs.markers ?? DEFAULT_MARKERS).includes(`line`)) continue
|
|
738
|
+
n_series += 1
|
|
739
|
+
n_points += srs.x.length
|
|
740
|
+
}
|
|
741
|
+
return { series: n_series, points: n_points }
|
|
742
|
+
})
|
|
743
|
+
let effective_line_tween = $derived(resolve_line_tween(line_tween, line_tween_load))
|
|
744
|
+
|
|
800
745
|
// Obstacle field for legend/colorbar auto-placement. Sampling only data points lets the
|
|
801
746
|
// legend land on top of a steep connecting line whose markers are sparse (e.g. y=x^2), so
|
|
802
747
|
// sample_series_obstacle_points also walks each drawn segment at a fixed pixel cadence.
|
|
@@ -824,14 +769,6 @@
|
|
|
824
769
|
return points
|
|
825
770
|
})
|
|
826
771
|
|
|
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
772
|
const fill_hover_key = (
|
|
836
773
|
source_type: `fill_region` | `error_band`,
|
|
837
774
|
source_idx: number,
|
|
@@ -894,19 +831,22 @@
|
|
|
894
831
|
})),
|
|
895
832
|
]
|
|
896
833
|
|
|
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)
|
|
834
|
+
// On log axes, clamp non-positive coords to the scale's domain floor (x_min/y_min) before
|
|
835
|
+
// scaling. A fixed tiny epsilon can sit far below the domain and map to extreme pixel coords.
|
|
836
|
+
const x_scale_type = final_x_axis.scale_type ?? `linear`
|
|
837
|
+
const y_scale_type = final_y_axis.scale_type ?? `linear`
|
|
838
|
+
const to_px = (pt: Pt): Pt => ({
|
|
839
|
+
x: x_scale_fn(x_scale_type === `log` && pt.x <= 0 ? x_min : pt.x),
|
|
840
|
+
y: y_scale_fn(y_scale_type === `log` && pt.y <= 0 ? y_min : pt.y),
|
|
841
|
+
})
|
|
908
842
|
|
|
909
|
-
|
|
843
|
+
// Each boundary is traced through its own points with the same curve the series line uses,
|
|
844
|
+
// so fill edges coincide exactly with the lines they border (x_domain anchors flat boundaries).
|
|
845
|
+
const domains = {
|
|
846
|
+
x_domain: [x_min, x_max] as Vec2,
|
|
847
|
+
y_domain: [y_min, y_max] as Vec2,
|
|
848
|
+
y2_domain: [y2_min, y2_max] as Vec2,
|
|
849
|
+
}
|
|
910
850
|
|
|
911
851
|
return all_regions
|
|
912
852
|
.filter((
|
|
@@ -918,71 +858,24 @@
|
|
|
918
858
|
hover_key: string
|
|
919
859
|
} => entry.region !== null)
|
|
920
860
|
.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)
|
|
861
|
+
// Hidden fills keep their entry (with empty path_segments -> nothing renders) so the
|
|
862
|
+
// legend item persists greyed-out and can be toggled back on.
|
|
863
|
+
const hidden = region.visible === false
|
|
864
|
+
const path_segments = hidden
|
|
865
|
+
? []
|
|
866
|
+
: compute_fill_segments(region, series_with_ids, domains)
|
|
867
|
+
.map((seg) =>
|
|
868
|
+
generate_fill_path(
|
|
869
|
+
seg.upper.map(to_px),
|
|
870
|
+
seg.lower.map(to_px),
|
|
871
|
+
seg.upper_curve,
|
|
872
|
+
seg.lower_curve,
|
|
873
|
+
)
|
|
874
|
+
)
|
|
875
|
+
.filter((path) => path.length > 0)
|
|
984
876
|
|
|
985
|
-
|
|
877
|
+
// Drop only visible fills with no geometry; keep hidden ones for the legend
|
|
878
|
+
if (!hidden && path_segments.length === 0) return null
|
|
986
879
|
|
|
987
880
|
return { ...region, idx, source_type, source_idx, hover_key, path_segments }
|
|
988
881
|
})
|
|
@@ -990,161 +883,9 @@
|
|
|
990
883
|
})
|
|
991
884
|
|
|
992
885
|
// 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
|
-
})
|
|
886
|
+
let legend_data = $derived(
|
|
887
|
+
build_legend_data(series_with_ids, computed_fills, color_scale_fn),
|
|
888
|
+
)
|
|
1148
889
|
|
|
1149
890
|
// Group fills by z-index for ordered rendering (single pass instead of 4 filters)
|
|
1150
891
|
let fills_by_z = $derived.by(() => {
|
|
@@ -1171,7 +912,7 @@
|
|
|
1171
912
|
// Calculate best legend placement using continuous grid sampling
|
|
1172
913
|
let legend_placement = $derived.by(() => {
|
|
1173
914
|
const should_place = legend != null &&
|
|
1174
|
-
(legend_data.length > 1 || Object.keys(legend).length > 0)
|
|
915
|
+
(legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)
|
|
1175
916
|
|
|
1176
917
|
if (!should_place || !width || !height) return null
|
|
1177
918
|
|
|
@@ -1192,7 +933,7 @@
|
|
|
1192
933
|
|
|
1193
934
|
// Calculate color bar placement (coordinates with legend to avoid overlap)
|
|
1194
935
|
let color_bar_placement = $derived.by(() => {
|
|
1195
|
-
if (!color_bar ||
|
|
936
|
+
if (!color_bar || all_color_values.length === 0 || !width || !height) return null
|
|
1196
937
|
|
|
1197
938
|
const plot_width = width - pad.l - pad.r
|
|
1198
939
|
const plot_height = height - pad.t - pad.b
|
|
@@ -1390,33 +1131,11 @@
|
|
|
1390
1131
|
const start_data_y_val = y_scale_fn.invert(drag_start_coords.y)
|
|
1391
1132
|
const end_data_y_val = y_scale_fn.invert(drag_current_coords.y)
|
|
1392
1133
|
|
|
1393
|
-
//
|
|
1394
|
-
|
|
1395
|
-
|
|
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)]
|
|
1134
|
+
// Same scale inverts both coords, so both are numbers or both are Dates
|
|
1135
|
+
const [x1, x2] = [to_epoch_num(start_data_x_val), to_epoch_num(end_data_x_val)]
|
|
1136
|
+
const next_x_range = sorted_range(x1, x2)
|
|
1415
1137
|
// Y axis is always number
|
|
1416
|
-
const next_y_range
|
|
1417
|
-
Math.min(start_data_y_val, end_data_y_val),
|
|
1418
|
-
Math.max(start_data_y_val, end_data_y_val),
|
|
1419
|
-
]
|
|
1138
|
+
const next_y_range = sorted_range(start_data_y_val, end_data_y_val)
|
|
1420
1139
|
|
|
1421
1140
|
// Check for minuscule zoom box (e.g. accidental click)
|
|
1422
1141
|
const min_zoom_size = 5 // Minimum pixels to trigger zoom
|
|
@@ -1430,24 +1149,22 @@
|
|
|
1430
1149
|
next_y_range[0] !== next_y_range[1]
|
|
1431
1150
|
) {
|
|
1432
1151
|
// 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
1152
|
x_axis = { ...x_axis, range: next_x_range }
|
|
1435
1153
|
y_axis = { ...y_axis, range: next_y_range }
|
|
1436
1154
|
|
|
1437
1155
|
// X2 axis: invert screen coords using x2 scale
|
|
1438
1156
|
if (x2_points.length > 0) {
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
}
|
|
1157
|
+
const x2_a = to_epoch_num(x2_scale_fn.invert(drag_start_coords.x))
|
|
1158
|
+
const x2_b = to_epoch_num(x2_scale_fn.invert(drag_current_coords.x))
|
|
1159
|
+
x2_axis = { ...x2_axis, range: sorted_range(x2_a, x2_b) }
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Y2 axis: when sync is enabled the y_axis effect derives y2; with sync 'none'
|
|
1163
|
+
// y2 must zoom from the rect directly (parity with BarPlot/Histogram/BoxPlot)
|
|
1164
|
+
if (y2_points.length > 0 && y2_sync_config.mode === `none`) {
|
|
1165
|
+
const y2_a = y2_scale_fn.invert(drag_start_coords.y)
|
|
1166
|
+
const y2_b = y2_scale_fn.invert(drag_current_coords.y)
|
|
1167
|
+
y2_axis = { ...y2_axis, range: sorted_range(y2_a, y2_b) }
|
|
1451
1168
|
}
|
|
1452
1169
|
}
|
|
1453
1170
|
}
|
|
@@ -1461,45 +1178,38 @@
|
|
|
1461
1178
|
document.body.style.cursor = `default`
|
|
1462
1179
|
}
|
|
1463
1180
|
|
|
1464
|
-
// Pan
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
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)
|
|
1471
|
-
// Clamp to at least 1 to avoid Infinity deltas when padding equals container size
|
|
1181
|
+
// Pan/zoom all four axes from an interaction-start snapshot, each in its own
|
|
1182
|
+
// scale's transform space (log axes pan by a constant factor, linear by a shift).
|
|
1183
|
+
// Plot dims clamped to 1px so degenerate containers can't produce Infinity deltas.
|
|
1184
|
+
const pan_all_axes = (init: InitialRanges, dx_px: number, dy_px: number) => {
|
|
1472
1185
|
const plot_width = Math.max(1, width - pad.l - pad.r)
|
|
1473
1186
|
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
)
|
|
1481
|
-
const x2_delta = pixels_to_data_delta(
|
|
1482
|
-
-dx * sensitivity,
|
|
1483
|
-
pan_drag_state.initial_x2_range,
|
|
1484
|
-
plot_width,
|
|
1485
|
-
)
|
|
1486
|
-
const y_delta = pixels_to_data_delta(
|
|
1487
|
-
dy * sensitivity,
|
|
1488
|
-
pan_drag_state.initial_y_range,
|
|
1489
|
-
plot_height,
|
|
1490
|
-
)
|
|
1491
|
-
const y2_delta = pixels_to_data_delta(
|
|
1492
|
-
dy * sensitivity,
|
|
1493
|
-
pan_drag_state.initial_y2_range,
|
|
1494
|
-
plot_height,
|
|
1187
|
+
zoom_x_range = pan_range_by_pixels(init.initial_x_range, dx_px, plot_width, final_x_axis.scale_type)
|
|
1188
|
+
zoom_x2_range = pan_range_by_pixels(init.initial_x2_range, dx_px, plot_width, final_x2_axis.scale_type)
|
|
1189
|
+
zoom_y_range = pan_range_by_pixels(init.initial_y_range, dy_px, plot_height, final_y_axis.scale_type)
|
|
1190
|
+
zoom_y2_range = get_synced_y2(
|
|
1191
|
+
zoom_y_range,
|
|
1192
|
+
pan_range_by_pixels(init.initial_y2_range, dy_px, plot_height, final_y2_axis.scale_type),
|
|
1495
1193
|
)
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1194
|
+
}
|
|
1195
|
+
const zoom_all_axes = (init: InitialRanges, factor: number) => {
|
|
1196
|
+
zoom_x_range = zoom_range_by_factor(init.initial_x_range, factor, final_x_axis.scale_type)
|
|
1197
|
+
zoom_x2_range = zoom_range_by_factor(init.initial_x2_range, factor, final_x2_axis.scale_type)
|
|
1198
|
+
zoom_y_range = zoom_range_by_factor(init.initial_y_range, factor, final_y_axis.scale_type)
|
|
1500
1199
|
zoom_y2_range = get_synced_y2(
|
|
1501
1200
|
zoom_y_range,
|
|
1502
|
-
|
|
1201
|
+
zoom_range_by_factor(init.initial_y2_range, factor, final_y2_axis.scale_type),
|
|
1202
|
+
)
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// Pan drag handler (drag direction inverted on x for natural pan feel)
|
|
1206
|
+
const on_pan_move = (evt: MouseEvent) => {
|
|
1207
|
+
if (!pan_drag_state) return
|
|
1208
|
+
const sensitivity = pan?.drag_sensitivity ?? 1
|
|
1209
|
+
pan_all_axes(
|
|
1210
|
+
pan_drag_state,
|
|
1211
|
+
-(evt.clientX - pan_drag_state.start.x) * sensitivity,
|
|
1212
|
+
(evt.clientY - pan_drag_state.start.y) * sensitivity,
|
|
1503
1213
|
)
|
|
1504
1214
|
}
|
|
1505
1215
|
|
|
@@ -1510,6 +1220,17 @@
|
|
|
1510
1220
|
window.removeEventListener(`mouseup`, on_pan_end)
|
|
1511
1221
|
}
|
|
1512
1222
|
|
|
1223
|
+
// Tear down any window listeners + cursor override if the component unmounts mid-drag
|
|
1224
|
+
// (mouseup/panend would otherwise never fire, leaking listeners and a stuck cursor).
|
|
1225
|
+
// onDestroy also runs during SSR teardown, where window/document don't exist.
|
|
1226
|
+
onDestroy(() => {
|
|
1227
|
+
remove_drag_listeners([on_window_mouse_move, on_pan_move], [on_window_mouse_up, on_pan_end])
|
|
1228
|
+
drag_start_coords = null
|
|
1229
|
+
drag_current_coords = null
|
|
1230
|
+
svg_bounding_box = null
|
|
1231
|
+
pan_drag_state = null
|
|
1232
|
+
})
|
|
1233
|
+
|
|
1513
1234
|
function handle_mouse_down(evt: MouseEvent) {
|
|
1514
1235
|
if (!svg_element) return
|
|
1515
1236
|
|
|
@@ -1561,35 +1282,19 @@
|
|
|
1561
1282
|
const plot_height = Math.max(1, height - pad.t - pad.b)
|
|
1562
1283
|
const sensitivity = pan?.wheel_sensitivity ?? 1
|
|
1563
1284
|
|
|
1564
|
-
//
|
|
1565
|
-
// deltaX for horizontal scroll
|
|
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
|
-
|
|
1285
|
+
// Pan along the dominant wheel direction
|
|
1286
|
+
// (deltaX for horizontal scroll on trackpads, deltaY for vertical)
|
|
1587
1287
|
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
1588
|
-
|
|
1589
|
-
|
|
1288
|
+
const dx = evt.deltaX * sensitivity
|
|
1289
|
+
zoom_x_range = pan_range_by_pixels(zoom_x_range, dx, plot_width, final_x_axis.scale_type)
|
|
1290
|
+
zoom_x2_range = pan_range_by_pixels(zoom_x2_range, dx, plot_width, final_x2_axis.scale_type)
|
|
1590
1291
|
} else {
|
|
1591
|
-
|
|
1592
|
-
|
|
1292
|
+
const dy = evt.deltaY * sensitivity
|
|
1293
|
+
zoom_y_range = pan_range_by_pixels(zoom_y_range, dy, plot_height, final_y_axis.scale_type)
|
|
1294
|
+
zoom_y2_range = get_synced_y2(
|
|
1295
|
+
zoom_y_range,
|
|
1296
|
+
pan_range_by_pixels(zoom_y2_range, dy, plot_height, final_y2_axis.scale_type),
|
|
1297
|
+
)
|
|
1593
1298
|
}
|
|
1594
1299
|
}
|
|
1595
1300
|
|
|
@@ -1627,75 +1332,15 @@
|
|
|
1627
1332
|
|
|
1628
1333
|
// Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
|
|
1629
1334
|
const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
|
|
1630
|
-
//
|
|
1631
|
-
if (start_dist <
|
|
1335
|
+
// ignore near-coincident touches so curr_dist / start_dist can't blow up the scale
|
|
1336
|
+
if (start_dist < MIN_TOUCH_DISTANCE_PIXELS) return
|
|
1632
1337
|
const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
|
|
1633
1338
|
const scale = curr_dist / start_dist
|
|
1634
1339
|
|
|
1635
|
-
//
|
|
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
|
|
1340
|
+
// Pinch zoom about the view center (spread = zoom in, pinch = zoom out)
|
|
1641
1341
|
if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
|
|
1642
|
-
|
|
1643
|
-
|
|
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
|
-
}
|
|
1342
|
+
zoom_all_axes(touch_state, scale)
|
|
1343
|
+
} else pan_all_axes(touch_state, -dx, dy)
|
|
1699
1344
|
}
|
|
1700
1345
|
|
|
1701
1346
|
function handle_touch_end() {
|
|
@@ -1906,7 +1551,6 @@
|
|
|
1906
1551
|
return construct_handler_props(tooltip_point)
|
|
1907
1552
|
})
|
|
1908
1553
|
|
|
1909
|
-
let using_controls = $derived(controls.show)
|
|
1910
1554
|
let has_multiple_series = $derived(series_with_ids.filter(Boolean).length > 1)
|
|
1911
1555
|
|
|
1912
1556
|
// Precompute non-click event names from point_events so we don't rebuild
|
|
@@ -1943,32 +1587,12 @@
|
|
|
1943
1587
|
set_loading: (axis) => (axis_loading = axis),
|
|
1944
1588
|
}
|
|
1945
1589
|
|
|
1946
|
-
//
|
|
1947
|
-
|
|
1948
|
-
const handle_axis_change = $derived(create_axis_change_handler(
|
|
1590
|
+
// Shared handler + one-shot auto-load bound to this component's state
|
|
1591
|
+
const { handle_axis_change, try_auto_load } = create_axis_loader(
|
|
1949
1592
|
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
|
-
})
|
|
1593
|
+
() => ({ data_loader, on_axis_change, on_error }),
|
|
1594
|
+
)
|
|
1595
|
+
$effect(try_auto_load)
|
|
1972
1596
|
</script>
|
|
1973
1597
|
|
|
1974
1598
|
{#snippet fill_regions_layer(fills: typeof computed_fills)}
|
|
@@ -2099,6 +1723,7 @@
|
|
|
2099
1723
|
ontouchstart={handle_touch_start}
|
|
2100
1724
|
ontouchmove={handle_touch_move}
|
|
2101
1725
|
ontouchend={handle_touch_end}
|
|
1726
|
+
ontouchcancel={handle_touch_end}
|
|
2102
1727
|
style:cursor={pan_drag_state
|
|
2103
1728
|
? `grabbing`
|
|
2104
1729
|
: shift_held && pan?.enabled !== false
|
|
@@ -2191,9 +1816,6 @@
|
|
|
2191
1816
|
|
|
2192
1817
|
<!-- Y2-axis (Right) -->
|
|
2193
1818
|
{#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
1819
|
<PlotAxis
|
|
2198
1820
|
side="y2"
|
|
2199
1821
|
ticks={y2_tick_values}
|
|
@@ -2207,8 +1829,7 @@
|
|
|
2207
1829
|
domain={[y2_min, y2_max]}
|
|
2208
1830
|
unit_on_first_tick
|
|
2209
1831
|
tick_label={(tick) => get_tick_label(tick, final_y2_axis.ticks)}
|
|
2210
|
-
label_x={width
|
|
2211
|
-
(final_y2_axis.label_shift?.x ?? 0)}
|
|
1832
|
+
label_x={y2_axis_label_x(final_y2_axis, width, pad.r, tick_label_widths.y2_max)}
|
|
2212
1833
|
label_y={pad.t + (height - pad.t - pad.b) / 2 + (final_y2_axis.label_shift?.y ?? 0)}
|
|
2213
1834
|
axis_loading={axis_loading === `y2`}
|
|
2214
1835
|
on_axis_change={(key) => handle_axis_change(`y2`, key)}
|
|
@@ -2300,7 +1921,7 @@
|
|
|
2300
1921
|
{@const finite_screen_points = all_line_points
|
|
2301
1922
|
.map((point) => get_screen_coords(point, series_data))
|
|
2302
1923
|
.filter(([sx, sy]) => isFinite(sx) && isFinite(sy))}
|
|
2303
|
-
{@const apply_line_controls =
|
|
1924
|
+
{@const apply_line_controls = controls.show &&
|
|
2304
1925
|
(!has_multiple_series ||
|
|
2305
1926
|
series_data._id === series_with_ids[selected_series_idx]?._id)}
|
|
2306
1927
|
{@const ls = series_data.line_style}
|
|
@@ -2322,7 +1943,7 @@
|
|
|
2322
1943
|
line_width={(tc(`line.width`) ? styles.line?.width : null) ?? ls?.stroke_width ?? 2}
|
|
2323
1944
|
line_dash={(tc(`line.dash`) ? styles.line?.dash : null) ?? ls?.line_dash}
|
|
2324
1945
|
area_color="transparent"
|
|
2325
|
-
{
|
|
1946
|
+
line_tween={effective_line_tween}
|
|
2326
1947
|
/>
|
|
2327
1948
|
{/if}
|
|
2328
1949
|
</g>
|
|
@@ -2371,7 +1992,7 @@
|
|
|
2371
1992
|
{@const screen_y = isFinite(raw_screen_y)
|
|
2372
1993
|
? raw_screen_y
|
|
2373
1994
|
: (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn).range()[0]}
|
|
2374
|
-
{@const apply_controls =
|
|
1995
|
+
{@const apply_controls = controls.show &&
|
|
2375
1996
|
(!has_multiple_series ||
|
|
2376
1997
|
series_data._id === series_with_ids[selected_series_idx]?._id)}
|
|
2377
1998
|
{@const pt = point.point_style}
|
|
@@ -2446,60 +2067,19 @@
|
|
|
2446
2067
|
|
|
2447
2068
|
<!-- Tooltip overlay above all plot overlays (legend, colorbar) -->
|
|
2448
2069
|
{#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 },
|
|
2070
|
+
{@const { point_label, series_idx } = tooltip_point}
|
|
2071
|
+
{@const tooltip_bg_color = pick_tooltip_bg(
|
|
2072
|
+
tooltip_point,
|
|
2073
|
+
series_with_ids[series_idx],
|
|
2074
|
+
color_scale_fn,
|
|
2496
2075
|
)}
|
|
2497
2076
|
<PlotTooltip
|
|
2498
|
-
x={
|
|
2499
|
-
y={
|
|
2500
|
-
offset={{ x:
|
|
2077
|
+
x={handler_props.cx}
|
|
2078
|
+
y={handler_props.cy}
|
|
2079
|
+
offset={{ x: 10, y: 5 }}
|
|
2080
|
+
constrain_to={{ width, height }}
|
|
2081
|
+
fallback_size={{ width: 120, height: 50 }}
|
|
2501
2082
|
bg_color={tooltip_bg_color}
|
|
2502
|
-
bind:wrapper={tooltip_el}
|
|
2503
2083
|
>
|
|
2504
2084
|
{#if tooltip}
|
|
2505
2085
|
{@render tooltip(handler_props)}
|
|
@@ -2585,7 +2165,7 @@
|
|
|
2585
2165
|
<!-- Legend -->
|
|
2586
2166
|
<!-- Only render if multiple series or if legend prop was explicitly provided by user (even if empty object) -->
|
|
2587
2167
|
{#if legend != null && legend_data.length > 0 &&
|
|
2588
|
-
(legend_data.length > 1 || Object.keys(legend).length > 0)}
|
|
2168
|
+
(legend_data.length > 1 || Object.keys(legend ?? {}).length > 0)}
|
|
2589
2169
|
{@const default_x = pad.l + 10}
|
|
2590
2170
|
{@const default_y = pad.t + 10}
|
|
2591
2171
|
{@const current_x = legend_is_dragging && legend_manual_position
|
|
@@ -2609,31 +2189,28 @@
|
|
|
2609
2189
|
on_drag={handle_legend_drag}
|
|
2610
2190
|
on_drag_end={() => (legend_is_dragging = false)}
|
|
2611
2191
|
on_hover_change={legend_hover.set_locked}
|
|
2612
|
-
on_item_hover={(
|
|
2613
|
-
(
|
|
2614
|
-
|
|
2615
|
-
|
|
2192
|
+
on_item_hover={(item) => {
|
|
2193
|
+
if (item?.item_type === `fill`) {
|
|
2194
|
+
// highlight the matching fill in the plot (same state plot fill-hover uses), but skip
|
|
2195
|
+
// hidden fills since they render nothing and would mark the legend item active for naught
|
|
2196
|
+
const fill = computed_fills.find((entry) => entry.idx === item.fill_idx)
|
|
2197
|
+
hovered_fill_key = fill && fill.visible !== false ? fill.hover_key : null
|
|
2198
|
+
hovered_legend_series_idx = null
|
|
2199
|
+
} else {
|
|
2200
|
+
hovered_legend_series_idx = item != null && item.series_idx >= 0
|
|
2201
|
+
? item.series_idx
|
|
2202
|
+
: null
|
|
2203
|
+
hovered_fill_key = null
|
|
2204
|
+
}
|
|
2205
|
+
}}
|
|
2616
2206
|
active_series_idx={tooltip_point?.series_idx ?? hovered_legend_series_idx}
|
|
2207
|
+
active_fill_idx={computed_fills.find((fill) => fill.hover_key === hovered_fill_key)?.idx ??
|
|
2208
|
+
null}
|
|
2617
2209
|
draggable={legend?.draggable ?? true}
|
|
2618
2210
|
{...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
|
-
})}
|
|
2211
|
+
on_toggle={legend?.on_toggle ?? legend_vis.on_toggle}
|
|
2212
|
+
on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
|
|
2213
|
+
on_group_toggle={legend?.on_group_toggle ?? legend_vis.on_group_toggle}
|
|
2637
2214
|
on_fill_toggle={(source_type: `fill_region` | `error_band`, source_idx: number) => {
|
|
2638
2215
|
// Only fill_regions can be toggled (error_bands are not bindable)
|
|
2639
2216
|
if (source_type === `fill_region`) {
|
|
@@ -2707,8 +2284,10 @@
|
|
|
2707
2284
|
background: var(--scatter-fullscreen-bg, var(--scatter-bg, var(--plot-bg)));
|
|
2708
2285
|
max-height: none !important;
|
|
2709
2286
|
overflow: hidden;
|
|
2710
|
-
/*
|
|
2711
|
-
|
|
2287
|
+
/* border-top (not padding-top): bind:clientHeight includes padding but excludes
|
|
2288
|
+
borders - padding made the chart overflow + clip its bottom 2em (x-axis title) */
|
|
2289
|
+
border-top: var(--plot-fullscreen-padding-top, 2em) solid
|
|
2290
|
+
var(--scatter-fullscreen-bg, var(--scatter-bg, var(--plot-bg, transparent)));
|
|
2712
2291
|
box-sizing: border-box;
|
|
2713
2292
|
}
|
|
2714
2293
|
/* Center the colorbar within its wrapper when shorter than it (e.g. capped by --cbar-max-height
|