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,11 +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 { format_value } from '
|
|
7
|
-
import { sanitize_html } from '
|
|
8
|
-
import { FullscreenToggle, set_fullscreen_bg } from '
|
|
9
|
-
import type { Point2D, Vec2 } from '
|
|
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'
|
|
10
10
|
import type {
|
|
11
11
|
AxisLoadError,
|
|
12
12
|
BarHandlerProps,
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
RefLineEvent,
|
|
28
28
|
ScaleType,
|
|
29
29
|
UserContentProps,
|
|
30
|
-
} from '
|
|
30
|
+
} from '..'
|
|
31
31
|
import {
|
|
32
32
|
BarPlotControls,
|
|
33
33
|
compute_element_placement,
|
|
@@ -35,56 +35,69 @@
|
|
|
35
35
|
PlotLegend,
|
|
36
36
|
ReferenceLine,
|
|
37
37
|
ScatterPoint,
|
|
38
|
-
} from '
|
|
39
|
-
import type { AxisChangeState } from '
|
|
40
|
-
import {
|
|
41
|
-
import { process_prop } from './data-transform'
|
|
38
|
+
} from '..'
|
|
39
|
+
import type { AxisChangeState } from '../core/axis-utils'
|
|
40
|
+
import { create_axis_loader } from '../core/axis-utils'
|
|
42
41
|
import {
|
|
43
42
|
create_dimension_tracker,
|
|
44
43
|
create_hover_lock,
|
|
45
|
-
} from '
|
|
44
|
+
} from '../core/hover-lock.svelte'
|
|
45
|
+
import { create_legend_visibility } from '../core/utils/series-visibility'
|
|
46
46
|
import {
|
|
47
|
+
axis_ranges_equal,
|
|
47
48
|
get_relative_coords,
|
|
48
|
-
|
|
49
|
+
MIN_TOUCH_DISTANCE_PIXELS,
|
|
50
|
+
pan_range_by_pixels,
|
|
49
51
|
PINCH_ZOOM_THRESHOLD,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
remove_drag_listeners,
|
|
53
|
+
resolve_axis_ranges,
|
|
54
|
+
sorted_range,
|
|
55
|
+
to_epoch_num,
|
|
56
|
+
zoom_range_by_factor,
|
|
57
|
+
} from '../core/interactions'
|
|
58
|
+
import type { IndexedRefLine } from '../core/reference-line'
|
|
59
|
+
import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
|
|
54
60
|
import {
|
|
55
61
|
create_color_scale,
|
|
56
62
|
create_scale,
|
|
57
63
|
create_size_scale,
|
|
58
64
|
generate_ticks,
|
|
59
|
-
get_nice_data_range,
|
|
60
65
|
get_tick_label,
|
|
61
|
-
} from '
|
|
62
|
-
import { DEFAULT_MARKERS
|
|
63
|
-
import { DEFAULTS } from '
|
|
66
|
+
} from '../core/scales'
|
|
67
|
+
import { DEFAULT_MARKERS } from '../core/types'
|
|
68
|
+
import { DEFAULTS } from '../../settings'
|
|
64
69
|
import { extent } from 'd3-array'
|
|
65
70
|
import type { Snippet } from 'svelte'
|
|
66
|
-
import { untrack } from 'svelte'
|
|
71
|
+
import { onDestroy, untrack } from 'svelte'
|
|
67
72
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
68
73
|
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
69
|
-
import { SvelteMap } from 'svelte/reactivity'
|
|
70
74
|
import {
|
|
71
75
|
build_obstacles_norm,
|
|
72
76
|
clip_bar,
|
|
73
77
|
has_explicit_position,
|
|
74
78
|
measured_footprint,
|
|
75
79
|
place_decorations,
|
|
76
|
-
} from '
|
|
80
|
+
} from '../core/auto-place'
|
|
77
81
|
import {
|
|
78
82
|
calc_auto_padding,
|
|
79
|
-
constrain_tooltip_position,
|
|
80
83
|
filter_padding,
|
|
81
84
|
LABEL_GAP_DEFAULT,
|
|
85
|
+
y2_axis_label_x,
|
|
82
86
|
measure_max_tick_width,
|
|
83
|
-
} from '
|
|
84
|
-
import PlotTooltip from '
|
|
85
|
-
import { bar_path } from '
|
|
86
|
-
import
|
|
87
|
-
import
|
|
87
|
+
} from '../core/layout'
|
|
88
|
+
import PlotTooltip from '../core/components/PlotTooltip.svelte'
|
|
89
|
+
import { bar_path } from '../core/svg'
|
|
90
|
+
import { unique_id } from '../core/utils'
|
|
91
|
+
import ZeroLines from '../core/components/ZeroLines.svelte'
|
|
92
|
+
import ZoomRect from '../core/components/ZoomRect.svelte'
|
|
93
|
+
import {
|
|
94
|
+
compute_bar_auto_ranges,
|
|
95
|
+
compute_group_info,
|
|
96
|
+
compute_stacked_offsets,
|
|
97
|
+
normalize_categorical,
|
|
98
|
+
} from './data'
|
|
99
|
+
import { compute_bar_rect, compute_line_points } from './geometry'
|
|
100
|
+
import type { LineSeriesPoint as BarLineSeriesPoint } from './geometry'
|
|
88
101
|
|
|
89
102
|
// Handler props for line marker events (extends BarHandlerProps with point-specific data)
|
|
90
103
|
interface LineMarkerHandlerProps extends BarHandlerProps<Metadata> {
|
|
@@ -92,27 +105,17 @@
|
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
// Extended point type with computed screen coordinates (used internally for rendering)
|
|
95
|
-
type LineSeriesPoint =
|
|
96
|
-
x: number // Screen x coordinate
|
|
97
|
-
y: number // Screen y coordinate
|
|
98
|
-
data_x: number // Original data x value
|
|
99
|
-
data_y: number // Original data y value
|
|
100
|
-
idx: number // Index in series
|
|
101
|
-
}
|
|
108
|
+
type LineSeriesPoint = BarLineSeriesPoint<Metadata>
|
|
102
109
|
|
|
103
110
|
let {
|
|
104
111
|
series = $bindable([]),
|
|
105
112
|
orientation = $bindable(`vertical`),
|
|
106
113
|
mode = $bindable(`overlay`),
|
|
107
114
|
x_axis = $bindable({}),
|
|
108
|
-
x2_axis = $bindable({}),
|
|
115
|
+
x2_axis: x2_axis_prop = $bindable({}),
|
|
109
116
|
y_axis = $bindable({}),
|
|
110
|
-
y2_axis = $bindable({}),
|
|
117
|
+
y2_axis: y2_axis_prop = $bindable({}),
|
|
111
118
|
display = $bindable(DEFAULTS.bar.display),
|
|
112
|
-
x_range = [null, null],
|
|
113
|
-
x2_range = [null, null],
|
|
114
|
-
y_range = [null, null],
|
|
115
|
-
y2_range = [null, null],
|
|
116
119
|
range_padding = 0.05,
|
|
117
120
|
padding = { t: 20, b: 60, l: 60, r: 20 },
|
|
118
121
|
legend = {},
|
|
@@ -222,29 +225,31 @@
|
|
|
222
225
|
// Initialize bar, line, y2_axis with defaults - using $derived for reactivity
|
|
223
226
|
let bar_state = $derived({ ...DEFAULTS.bar.bar, ...bar })
|
|
224
227
|
let line_state = $derived({ ...DEFAULTS.bar.line, ...line })
|
|
225
|
-
|
|
228
|
+
// Merge secondary-axis defaults as deriveds instead of assigning back into the
|
|
229
|
+
// $bindable props (which would push library defaults into the parent's bound state)
|
|
230
|
+
let y2_axis = $derived({
|
|
226
231
|
format: ``,
|
|
227
232
|
scale_type: `linear`,
|
|
228
233
|
ticks: 5,
|
|
229
|
-
label_shift: { y:
|
|
234
|
+
label_shift: { x: 0, y: 0 }, // y2 title stays vertically centered (x pos set by y2_axis_label_x)
|
|
230
235
|
tick: { label: { shift: { x: 0, y: 0 } } }, // base offset handled in rendering
|
|
231
236
|
range: [null, null],
|
|
232
|
-
...
|
|
233
|
-
}
|
|
234
|
-
x2_axis = {
|
|
237
|
+
...y2_axis_prop,
|
|
238
|
+
} as typeof y2_axis_prop)
|
|
239
|
+
let x2_axis = $derived({
|
|
235
240
|
format: ``,
|
|
236
241
|
scale_type: `linear`,
|
|
237
242
|
ticks: 5,
|
|
238
243
|
label_shift: { x: 0, y: 40 },
|
|
239
244
|
tick: { label: { shift: { x: 0, y: 0 } } },
|
|
240
245
|
range: [null, null],
|
|
241
|
-
...
|
|
242
|
-
}
|
|
246
|
+
...x2_axis_prop,
|
|
247
|
+
} as typeof x2_axis_prop)
|
|
243
248
|
|
|
244
249
|
let [width, height] = $state([0, 0])
|
|
245
250
|
let wrapper: HTMLDivElement | undefined = $state()
|
|
246
251
|
let svg_element: SVGElement | null = $state(null)
|
|
247
|
-
|
|
252
|
+
const clip_path_id = unique_id(`chart-clip`) // stable, collision-resistant (see unique_id)
|
|
248
253
|
|
|
249
254
|
// Reference line hover state
|
|
250
255
|
let hovered_ref_line_idx = $state<number | null>(null)
|
|
@@ -256,55 +261,25 @@
|
|
|
256
261
|
let indexed_ref_lines = $derived(index_ref_lines(ref_lines))
|
|
257
262
|
let ref_lines_by_z = $derived(group_ref_lines_by_z(indexed_ref_lines))
|
|
258
263
|
|
|
259
|
-
// === Categorical Normalization ===
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
let is_categorical = $derived(
|
|
264
|
-
series.some((srs) => srs.x.some((val) => typeof val === `string`)),
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
let category_list = $derived.by(() => {
|
|
268
|
-
if (!is_categorical) return [] as string[]
|
|
269
|
-
if (x_axis.categories?.length) return [...x_axis.categories]
|
|
270
|
-
return [...new Set(series.flatMap((srs) => srs.x.map(String)))]
|
|
271
|
-
})
|
|
264
|
+
// === Categorical Normalization (string x values -> integer indices, see ./data) ===
|
|
265
|
+
let cat_norm = $derived(normalize_categorical(series, x_axis.categories))
|
|
266
|
+
let category_list = $derived(cat_norm.category_list)
|
|
267
|
+
let internal_series = $derived(cat_norm.internal_series)
|
|
272
268
|
|
|
273
269
|
let category_indices = $derived(
|
|
274
|
-
category_list.length ? category_list.map((_, idx) => idx) : null,
|
|
270
|
+
category_list.length > 0 ? category_list.map((_, idx) => idx) : null,
|
|
275
271
|
)
|
|
276
272
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
// Resolve original index for each category (undefined if series lacks it)
|
|
290
|
-
const orig_indices = category_list.map((cat) => orig_map.get(cat))
|
|
291
|
-
const remap = <T>(arr: readonly T[] | null | undefined, fallback: T): T[] =>
|
|
292
|
-
orig_indices.map((oi) => oi != null ? (arr?.[oi] ?? fallback) : fallback)
|
|
293
|
-
const bw_arr = Array.isArray(srs.bar_width) ? srs.bar_width : null
|
|
294
|
-
const meta_arr = Array.isArray(srs.metadata) ? srs.metadata : null
|
|
295
|
-
return {
|
|
296
|
-
...srs,
|
|
297
|
-
x: category_indices,
|
|
298
|
-
y: remap(srs.y, srs.render_mode === `line` ? NaN : 0),
|
|
299
|
-
labels: remap(srs.labels, null),
|
|
300
|
-
metadata: orig_indices.map((oi) =>
|
|
301
|
-
oi != null ? (meta_arr ? meta_arr[oi] : srs.metadata) : undefined
|
|
302
|
-
) as Metadata[],
|
|
303
|
-
...(bw_arr ? { bar_width: remap(bw_arr, 0.5) } : {}),
|
|
304
|
-
...(srs.color_values ? { color_values: remap(srs.color_values, null) } : {}),
|
|
305
|
-
...(srs.size_values ? { size_values: remap(srs.size_values, null) } : {}),
|
|
306
|
-
} as NumericBarSeries
|
|
307
|
-
})
|
|
273
|
+
// Thin categorical tick labels + grid lines when many categories would overlap.
|
|
274
|
+
// Bars still render for every category (this only reduces drawn ticks/labels/grid).
|
|
275
|
+
let cat_tick_indices = $derived.by<number[]>(() => {
|
|
276
|
+
if (!category_indices) return []
|
|
277
|
+
const axis_px = (orientation === `horizontal` ? height : width) || 0
|
|
278
|
+
const max_ticks = Math.max(1, Math.floor(axis_px / 28)) // ~28px per category label
|
|
279
|
+
const step = Math.ceil(category_indices.length / max_ticks)
|
|
280
|
+
return step <= 1
|
|
281
|
+
? category_indices
|
|
282
|
+
: category_indices.filter((_, idx) => idx % step === 0)
|
|
308
283
|
})
|
|
309
284
|
|
|
310
285
|
// Compute auto ranges from visible series
|
|
@@ -323,124 +298,26 @@
|
|
|
323
298
|
visible_series.filter((srs) => srs.x_axis === `x2`),
|
|
324
299
|
)
|
|
325
300
|
|
|
326
|
-
let auto_ranges = $derived
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const y_val = srs.y[idx] ?? 0
|
|
347
|
-
const totals = stacked_totals.get(x_val) ?? { pos: 0, neg: 0 }
|
|
348
|
-
if (y_val >= 0) totals.pos += y_val
|
|
349
|
-
else totals.neg += y_val
|
|
350
|
-
stacked_totals.set(x_val, totals)
|
|
351
|
-
})
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
// Replace points with stacked totals + line series (which don't stack)
|
|
355
|
-
points = [
|
|
356
|
-
...Array.from(stacked_totals).flatMap(([x_val, { pos, neg }]) => [
|
|
357
|
-
...(pos > 0 ? [{ x: x_val, y: pos }] : []),
|
|
358
|
-
...(neg < 0 ? [{ x: x_val, y: neg }] : []),
|
|
359
|
-
]),
|
|
360
|
-
...series_list
|
|
361
|
-
.filter((srs) => srs.render_mode === `line`)
|
|
362
|
-
.flatMap((srs) =>
|
|
363
|
-
srs.x.map((x_val, idx) => ({ x: x_val, y: srs.y[idx] }))
|
|
364
|
-
),
|
|
365
|
-
]
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (!points.length) return [0, 1]
|
|
369
|
-
|
|
370
|
-
let computed_y_range = get_nice_data_range(
|
|
371
|
-
points,
|
|
372
|
-
(pt) => pt.y,
|
|
373
|
-
y_limit,
|
|
374
|
-
scale_type,
|
|
375
|
-
range_padding,
|
|
376
|
-
false,
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
// For bar plots, ensure the value axis starts at 0 unless there are negative values
|
|
380
|
-
// Only apply zero-clamping for linear and arcsinh scales (not log)
|
|
381
|
-
const type_name = get_scale_type_name(scale_type)
|
|
382
|
-
if (type_name === `linear` || type_name === `arcsinh`) {
|
|
383
|
-
const has_negative = points.some((pt) => pt.y < 0)
|
|
384
|
-
const has_positive = points.some((pt) => pt.y > 0)
|
|
385
|
-
|
|
386
|
-
// Only adjust if no explicit y_range is set
|
|
387
|
-
if (y_limit?.[0] == null && y_limit?.[1] == null) {
|
|
388
|
-
if (has_positive && !has_negative) computed_y_range = [0, computed_y_range[1]]
|
|
389
|
-
else if (has_negative && !has_positive) computed_y_range = [computed_y_range[0], 0]
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return computed_y_range
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// Get x values split by axis for range calculation
|
|
397
|
-
// For categorical data, use fixed range centered on integer indices
|
|
398
|
-
let x_auto_range: number[]
|
|
399
|
-
if (category_list.length) {
|
|
400
|
-
x_auto_range = [-0.5, category_list.length - 0.5]
|
|
401
|
-
} else {
|
|
402
|
-
const x1_x_points = visible_series
|
|
403
|
-
.filter((srs) => (srs.x_axis ?? `x1`) === `x1`)
|
|
404
|
-
.flatMap((srs) => srs.x.map((x_val) => ({ x: x_val, y: 0 })))
|
|
405
|
-
x_auto_range = x1_x_points.length
|
|
406
|
-
? get_nice_data_range(
|
|
407
|
-
x1_x_points,
|
|
408
|
-
(pt) => pt.x,
|
|
409
|
-
x_range,
|
|
410
|
-
x_axis.scale_type ?? `linear`,
|
|
411
|
-
range_padding,
|
|
412
|
-
x_axis.format?.startsWith(`%`) || false,
|
|
413
|
-
)
|
|
414
|
-
: [0, 1]
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const x2_x_points = x2_series.flatMap((srs) =>
|
|
418
|
-
srs.x.map((x_val) => ({ x: x_val, y: 0 }))
|
|
419
|
-
)
|
|
420
|
-
const x2_scale_type = x2_axis.scale_type ?? `linear`
|
|
421
|
-
const x2_auto_range = x2_x_points.length
|
|
422
|
-
? get_nice_data_range(
|
|
423
|
-
x2_x_points,
|
|
424
|
-
(pt) => pt.x,
|
|
425
|
-
x2_range,
|
|
426
|
-
x2_scale_type,
|
|
427
|
-
range_padding,
|
|
428
|
-
x2_axis.format?.startsWith(`%`) || false,
|
|
429
|
-
)
|
|
430
|
-
: [0, 1]
|
|
431
|
-
|
|
432
|
-
const y1_range = calc_y_range(y1_series, y_range, y_axis.scale_type ?? `linear`)
|
|
433
|
-
const y2_auto_range = calc_y_range(
|
|
434
|
-
y2_series,
|
|
435
|
-
y2_range,
|
|
436
|
-
y2_axis.scale_type ?? `linear`,
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
// Map data ranges to axis ranges depending on orientation
|
|
440
|
-
return orientation === `horizontal`
|
|
441
|
-
? ({ x: y1_range, x2: x2_auto_range, y: x_auto_range, y2: y2_auto_range })
|
|
442
|
-
: ({ x: x_auto_range, x2: x2_auto_range, y: y1_range, y2: y2_auto_range })
|
|
443
|
-
})
|
|
301
|
+
let auto_ranges = $derived(compute_bar_auto_ranges({
|
|
302
|
+
visible_series,
|
|
303
|
+
y1_series,
|
|
304
|
+
y2_series,
|
|
305
|
+
x2_series,
|
|
306
|
+
mode,
|
|
307
|
+
orientation,
|
|
308
|
+
range_padding,
|
|
309
|
+
category_count: category_list.length,
|
|
310
|
+
x_range: x_axis.range ?? [null, null],
|
|
311
|
+
x_scale_type: x_axis.scale_type ?? `linear`,
|
|
312
|
+
x_is_time: x_axis.format?.startsWith(`%`) || false,
|
|
313
|
+
x2_range: x2_axis.range ?? [null, null],
|
|
314
|
+
x2_scale_type: x2_axis.scale_type ?? `linear`,
|
|
315
|
+
x2_is_time: x2_axis.format?.startsWith(`%`) || false,
|
|
316
|
+
y_range: y_axis.range ?? [null, null],
|
|
317
|
+
y_scale_type: y_axis.scale_type ?? `linear`,
|
|
318
|
+
y2_range: y2_axis.range ?? [null, null],
|
|
319
|
+
y2_scale_type: y2_axis.scale_type ?? `linear`,
|
|
320
|
+
}))
|
|
444
321
|
|
|
445
322
|
// Initialize and current ranges
|
|
446
323
|
let ranges = $state<{
|
|
@@ -452,38 +329,16 @@
|
|
|
452
329
|
})
|
|
453
330
|
|
|
454
331
|
$effect(() => { // handle x_axis.range / x2_axis.range / y_axis.range / y2_axis.range changes
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
y_axis.range?.[1] ?? auto_ranges.y[1],
|
|
466
|
-
] as Vec2
|
|
467
|
-
const new_y2 = [
|
|
468
|
-
y2_axis.range?.[0] ?? auto_ranges.y2[0],
|
|
469
|
-
y2_axis.range?.[1] ?? auto_ranges.y2[1],
|
|
470
|
-
] as Vec2
|
|
471
|
-
// Only update if the initial (data-driven) ranges changed, not when user pans
|
|
472
|
-
// Comparing against initial preserves user's pan/zoom state
|
|
473
|
-
if (
|
|
474
|
-
ranges.initial.x[0] !== new_x[0] ||
|
|
475
|
-
ranges.initial.x[1] !== new_x[1] ||
|
|
476
|
-
ranges.initial.x2[0] !== new_x2[0] ||
|
|
477
|
-
ranges.initial.x2[1] !== new_x2[1] ||
|
|
478
|
-
ranges.initial.y[0] !== new_y[0] ||
|
|
479
|
-
ranges.initial.y[1] !== new_y[1] ||
|
|
480
|
-
ranges.initial.y2[0] !== new_y2[0] ||
|
|
481
|
-
ranges.initial.y2[1] !== new_y2[1]
|
|
482
|
-
) {
|
|
483
|
-
ranges = {
|
|
484
|
-
initial: { x: new_x, x2: new_x2, y: new_y, y2: new_y2 },
|
|
485
|
-
current: { x: new_x, x2: new_x2, y: new_y, y2: new_y2 },
|
|
486
|
-
}
|
|
332
|
+
// resolve_axis_ranges returns null for transient non-finite bounds (skip: writing
|
|
333
|
+
// NaN breaks scales and, since NaN !== NaN, loops the effect)
|
|
334
|
+
const next = resolve_axis_ranges({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis }, auto_ranges)
|
|
335
|
+
if (!next) return
|
|
336
|
+
// Only update if the initial (data-driven) ranges changed, not when user pans.
|
|
337
|
+
// untrack the read of `ranges` so the assignment below can't re-trigger this effect
|
|
338
|
+
// (reading + writing the same state otherwise causes effect_update_depth_exceeded).
|
|
339
|
+
const init = untrack(() => ranges.initial)
|
|
340
|
+
if (!axis_ranges_equal(init, next)) {
|
|
341
|
+
ranges = { initial: { ...next }, current: { ...next } }
|
|
487
342
|
}
|
|
488
343
|
})
|
|
489
344
|
|
|
@@ -494,7 +349,7 @@
|
|
|
494
349
|
|
|
495
350
|
// Update padding when format or ticks change
|
|
496
351
|
$effect(() => {
|
|
497
|
-
const new_pad = width && height && ticks.y.length
|
|
352
|
+
const new_pad = width && height && ticks.y.length > 0
|
|
498
353
|
? calc_auto_padding({
|
|
499
354
|
padding,
|
|
500
355
|
default_padding,
|
|
@@ -505,7 +360,7 @@
|
|
|
505
360
|
: filter_padding(padding, default_padding)
|
|
506
361
|
// Expand right padding if y2 ticks are shown (only for vertical orientation)
|
|
507
362
|
if (
|
|
508
|
-
width && height && y2_series.length && ticks.y2.length &&
|
|
363
|
+
width && height && y2_series.length > 0 && ticks.y2.length > 0 &&
|
|
509
364
|
orientation === `vertical`
|
|
510
365
|
) {
|
|
511
366
|
// Need space for: tick shift + tick width + gap (30px) + label space (20px if present)
|
|
@@ -521,7 +376,7 @@
|
|
|
521
376
|
}
|
|
522
377
|
// Expand top padding if x2 ticks are shown (only for vertical orientation)
|
|
523
378
|
if (
|
|
524
|
-
width && height && x2_series.length && ticks.x2.length &&
|
|
379
|
+
width && height && x2_series.length > 0 && ticks.x2.length > 0 &&
|
|
525
380
|
orientation === `vertical`
|
|
526
381
|
) {
|
|
527
382
|
const inside = x2_axis.tick?.label?.inside ?? false
|
|
@@ -546,7 +401,7 @@
|
|
|
546
401
|
// from baseline to its tip so the legend can't hide inside a tall bar. Built from internal_series
|
|
547
402
|
// (pad-independent) + ranges so the crowding decision can't see its own reservation.
|
|
548
403
|
const obstacles_norm = $derived.by(() => {
|
|
549
|
-
if (!width || !height ||
|
|
404
|
+
if (!width || !height || visible_series.length === 0) return []
|
|
550
405
|
const base_w = width - base_pad.l - base_pad.r
|
|
551
406
|
const base_h = height - base_pad.t - base_pad.b
|
|
552
407
|
if (base_w <= 0 || base_h <= 0) return []
|
|
@@ -661,7 +516,7 @@
|
|
|
661
516
|
// In vertical mode categories are on x-axis; in horizontal mode on y-axis
|
|
662
517
|
let cat_axis = $derived(orientation === `horizontal` ? `y` : `x`)
|
|
663
518
|
let effective_cat_ticks = $derived.by(() => {
|
|
664
|
-
if (
|
|
519
|
+
if (category_list.length === 0) return undefined
|
|
665
520
|
// Only respect user ticks when they're a Record (custom label mapping),
|
|
666
521
|
// not a number (tick count) or array (tick positions)
|
|
667
522
|
const user_ticks = cat_axis === `x` ? x_axis.ticks : y_axis.ticks
|
|
@@ -677,7 +532,7 @@
|
|
|
677
532
|
// Ticks
|
|
678
533
|
let ticks = $derived({
|
|
679
534
|
x: width && height
|
|
680
|
-
? (category_indices && cat_axis === `x` ?
|
|
535
|
+
? (category_indices && cat_axis === `x` ? cat_tick_indices : generate_ticks(
|
|
681
536
|
ranges.current.x,
|
|
682
537
|
x_axis.scale_type ?? `linear`,
|
|
683
538
|
x_axis.ticks,
|
|
@@ -686,7 +541,7 @@
|
|
|
686
541
|
))
|
|
687
542
|
: [],
|
|
688
543
|
y: width && height
|
|
689
|
-
? (category_indices && cat_axis === `y` ?
|
|
544
|
+
? (category_indices && cat_axis === `y` ? cat_tick_indices : generate_ticks(
|
|
690
545
|
ranges.current.y,
|
|
691
546
|
y_axis.scale_type ?? `linear`,
|
|
692
547
|
y_axis.ticks,
|
|
@@ -762,31 +617,22 @@
|
|
|
762
617
|
const dx = Math.abs(drag_state.start.x - drag_state.current.x)
|
|
763
618
|
const dy = Math.abs(drag_state.start.y - drag_state.current.y)
|
|
764
619
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
} else if (typeof x1_raw === `number` && typeof x2_raw === `number`) {
|
|
769
|
-
;[xr1, xr2] = [x1_raw, x2_raw]
|
|
770
|
-
} else [xr1, xr2] = [NaN, NaN] // bail: mixed types
|
|
771
|
-
|
|
772
|
-
let x2r1: number, x2r2: number
|
|
773
|
-
if (x2a_1_raw instanceof Date && x2a_2_raw instanceof Date) {
|
|
774
|
-
;[x2r1, x2r2] = [x2a_1_raw.getTime(), x2a_2_raw.getTime()]
|
|
775
|
-
} else if (typeof x2a_1_raw === `number` && typeof x2a_2_raw === `number`) {
|
|
776
|
-
;[x2r1, x2r2] = [x2a_1_raw, x2a_2_raw]
|
|
777
|
-
} else [x2r1, x2r2] = [NaN, NaN]
|
|
620
|
+
// Same scale inverts both coords, so each pair is all-number or all-Date
|
|
621
|
+
const [xr1, xr2] = [to_epoch_num(x1_raw), to_epoch_num(x2_raw)]
|
|
622
|
+
const [x2r1, x2r2] = [to_epoch_num(x2a_1_raw), to_epoch_num(x2a_2_raw)]
|
|
778
623
|
|
|
779
624
|
if (dx > 5 && dy > 5 && Number.isFinite(xr1) && Number.isFinite(xr2)) {
|
|
780
625
|
// Update axis ranges to trigger reactivity and prevent effect from overriding
|
|
781
|
-
x_axis = { ...x_axis, range:
|
|
626
|
+
x_axis = { ...x_axis, range: sorted_range(xr1, xr2) }
|
|
782
627
|
if (x2_series.length > 0 && Number.isFinite(x2r1) && Number.isFinite(x2r2)) {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
628
|
+
x2_axis_prop = { ...x2_axis_prop, range: sorted_range(x2r1, x2r2) }
|
|
629
|
+
}
|
|
630
|
+
y_axis = { ...y_axis, range: sorted_range(y1, y2) }
|
|
631
|
+
// gate on y2 series presence (like x2): the y2 scale is a [0, 1] sentinel
|
|
632
|
+
// otherwise, so inverting would store a phantom range in the bindable prop
|
|
633
|
+
if (y2_series.length > 0) {
|
|
634
|
+
y2_axis_prop = { ...y2_axis_prop, range: sorted_range(y2_1, y2_2) }
|
|
787
635
|
}
|
|
788
|
-
y_axis = { ...y_axis, range: [Math.min(y1, y2), Math.max(y1, y2)] }
|
|
789
|
-
y2_axis = { ...y2_axis, range: [Math.min(y2_1, y2_2), Math.max(y2_1, y2_2)] }
|
|
790
636
|
}
|
|
791
637
|
}
|
|
792
638
|
drag_state = { start: null, current: null, bounds: null }
|
|
@@ -795,40 +641,30 @@
|
|
|
795
641
|
document.body.style.cursor = `default`
|
|
796
642
|
}
|
|
797
643
|
|
|
798
|
-
// Pan
|
|
644
|
+
// Pan/zoom all four axes from an interaction-start snapshot, each in its own
|
|
645
|
+
// scale's transform space (log axes pan by a constant factor, linear by a shift)
|
|
646
|
+
const pan_all_axes = (init: InitialRanges, dx_px: number, dy_px: number) => {
|
|
647
|
+
ranges.current.x = pan_range_by_pixels(init.initial_x_range, dx_px, chart_width, x_axis.scale_type)
|
|
648
|
+
ranges.current.x2 = pan_range_by_pixels(init.initial_x2_range, dx_px, chart_width, x2_axis.scale_type)
|
|
649
|
+
ranges.current.y = pan_range_by_pixels(init.initial_y_range, dy_px, chart_height, y_axis.scale_type)
|
|
650
|
+
ranges.current.y2 = pan_range_by_pixels(init.initial_y2_range, dy_px, chart_height, y2_axis.scale_type)
|
|
651
|
+
}
|
|
652
|
+
const zoom_all_axes = (init: InitialRanges, factor: number) => {
|
|
653
|
+
ranges.current.x = zoom_range_by_factor(init.initial_x_range, factor, x_axis.scale_type)
|
|
654
|
+
ranges.current.x2 = zoom_range_by_factor(init.initial_x2_range, factor, x2_axis.scale_type)
|
|
655
|
+
ranges.current.y = zoom_range_by_factor(init.initial_y_range, factor, y_axis.scale_type)
|
|
656
|
+
ranges.current.y2 = zoom_range_by_factor(init.initial_y2_range, factor, y2_axis.scale_type)
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Pan drag handler (drag direction inverted on x for natural pan feel)
|
|
799
660
|
const on_pan_move = (evt: MouseEvent) => {
|
|
800
661
|
if (!pan_drag_state) return
|
|
801
|
-
const dx = evt.clientX - pan_drag_state.start.x
|
|
802
|
-
const dy = evt.clientY - pan_drag_state.start.y
|
|
803
|
-
|
|
804
|
-
// Convert pixel delta to data delta (note: drag direction is inverted for natural pan feel)
|
|
805
662
|
const sensitivity = pan?.drag_sensitivity ?? 1
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
-
|
|
809
|
-
pan_drag_state.
|
|
810
|
-
chart_width,
|
|
811
|
-
)
|
|
812
|
-
const x2_delta = pixels_to_data_delta(
|
|
813
|
-
-dx * sensitivity,
|
|
814
|
-
pan_drag_state.initial_x2_range,
|
|
815
|
-
chart_width,
|
|
816
|
-
)
|
|
817
|
-
const y_delta = pixels_to_data_delta(
|
|
818
|
-
dy * sensitivity,
|
|
819
|
-
pan_drag_state.initial_y_range,
|
|
820
|
-
chart_height,
|
|
663
|
+
pan_all_axes(
|
|
664
|
+
pan_drag_state,
|
|
665
|
+
-(evt.clientX - pan_drag_state.start.x) * sensitivity,
|
|
666
|
+
(evt.clientY - pan_drag_state.start.y) * sensitivity,
|
|
821
667
|
)
|
|
822
|
-
const y2_delta = pixels_to_data_delta(
|
|
823
|
-
dy * sensitivity,
|
|
824
|
-
pan_drag_state.initial_y2_range,
|
|
825
|
-
chart_height,
|
|
826
|
-
)
|
|
827
|
-
|
|
828
|
-
ranges.current.x = pan_range(pan_drag_state.initial_x_range, x_delta)
|
|
829
|
-
ranges.current.x2 = pan_range(pan_drag_state.initial_x2_range, x2_delta)
|
|
830
|
-
ranges.current.y = pan_range(pan_drag_state.initial_y_range, y_delta)
|
|
831
|
-
ranges.current.y2 = pan_range(pan_drag_state.initial_y2_range, y2_delta)
|
|
832
668
|
}
|
|
833
669
|
|
|
834
670
|
const on_pan_end = () => {
|
|
@@ -838,6 +674,15 @@
|
|
|
838
674
|
window.removeEventListener(`mouseup`, on_pan_end)
|
|
839
675
|
}
|
|
840
676
|
|
|
677
|
+
// Tear down any window listeners + cursor override if the component unmounts mid-drag
|
|
678
|
+
// (mouseup/panend would otherwise never fire, leaking listeners and a stuck cursor).
|
|
679
|
+
// onDestroy also runs during SSR teardown, where window/document don't exist.
|
|
680
|
+
onDestroy(() => {
|
|
681
|
+
remove_drag_listeners([on_window_mouse_move, on_pan_move], [on_window_mouse_up, on_pan_end])
|
|
682
|
+
drag_state = { start: null, current: null, bounds: null }
|
|
683
|
+
pan_drag_state = null
|
|
684
|
+
})
|
|
685
|
+
|
|
841
686
|
function handle_mouse_down(evt: MouseEvent) {
|
|
842
687
|
const coords = get_relative_coords(evt)
|
|
843
688
|
if (!coords || !svg_element) return
|
|
@@ -880,34 +725,15 @@
|
|
|
880
725
|
|
|
881
726
|
const sensitivity = pan?.wheel_sensitivity ?? 1
|
|
882
727
|
|
|
883
|
-
//
|
|
884
|
-
const x_delta = pixels_to_data_delta(
|
|
885
|
-
evt.deltaX * sensitivity,
|
|
886
|
-
ranges.current.x,
|
|
887
|
-
chart_width,
|
|
888
|
-
)
|
|
889
|
-
const x2_delta = pixels_to_data_delta(
|
|
890
|
-
evt.deltaX * sensitivity,
|
|
891
|
-
ranges.current.x2,
|
|
892
|
-
chart_width,
|
|
893
|
-
)
|
|
894
|
-
const y_delta = pixels_to_data_delta(
|
|
895
|
-
evt.deltaY * sensitivity,
|
|
896
|
-
ranges.current.y,
|
|
897
|
-
chart_height,
|
|
898
|
-
)
|
|
899
|
-
const y2_delta = pixels_to_data_delta(
|
|
900
|
-
evt.deltaY * sensitivity,
|
|
901
|
-
ranges.current.y2,
|
|
902
|
-
chart_height,
|
|
903
|
-
)
|
|
904
|
-
|
|
728
|
+
// Pan along the dominant wheel direction
|
|
905
729
|
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
906
|
-
|
|
907
|
-
ranges.current.
|
|
730
|
+
const dx = evt.deltaX * sensitivity
|
|
731
|
+
ranges.current.x = pan_range_by_pixels(ranges.current.x, dx, chart_width, x_axis.scale_type)
|
|
732
|
+
ranges.current.x2 = pan_range_by_pixels(ranges.current.x2, dx, chart_width, x2_axis.scale_type)
|
|
908
733
|
} else {
|
|
909
|
-
|
|
910
|
-
ranges.current.
|
|
734
|
+
const dy = evt.deltaY * sensitivity
|
|
735
|
+
ranges.current.y = pan_range_by_pixels(ranges.current.y, dy, chart_height, y_axis.scale_type)
|
|
736
|
+
ranges.current.y2 = pan_range_by_pixels(ranges.current.y2, dy, chart_height, y2_axis.scale_type)
|
|
911
737
|
}
|
|
912
738
|
}
|
|
913
739
|
|
|
@@ -945,74 +771,17 @@
|
|
|
945
771
|
|
|
946
772
|
// Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
|
|
947
773
|
const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
|
|
948
|
-
//
|
|
949
|
-
if (start_dist <
|
|
774
|
+
// ignore near-coincident touches so curr_dist / start_dist can't blow up the scale
|
|
775
|
+
if (start_dist < MIN_TOUCH_DISTANCE_PIXELS) return
|
|
950
776
|
const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
|
|
951
777
|
const scale = curr_dist / start_dist
|
|
952
778
|
|
|
953
779
|
// If scale changed significantly, treat as pinch-zoom
|
|
954
780
|
// Also guard against scale being too small to avoid division by zero
|
|
781
|
+
// Pinch zoom about the view center (spread = zoom in, pinch = zoom out)
|
|
955
782
|
if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const x_span = touch_state.initial_x_range[1] - touch_state.initial_x_range[0]
|
|
959
|
-
const x2_span = touch_state.initial_x2_range[1] -
|
|
960
|
-
touch_state.initial_x2_range[0]
|
|
961
|
-
const y_span = touch_state.initial_y_range[1] - touch_state.initial_y_range[0]
|
|
962
|
-
const y2_span = touch_state.initial_y2_range[1] -
|
|
963
|
-
touch_state.initial_y2_range[0]
|
|
964
|
-
const x_center =
|
|
965
|
-
(touch_state.initial_x_range[0] + touch_state.initial_x_range[1]) / 2
|
|
966
|
-
const x2_center =
|
|
967
|
-
(touch_state.initial_x2_range[0] + touch_state.initial_x2_range[1]) / 2
|
|
968
|
-
const y_center =
|
|
969
|
-
(touch_state.initial_y_range[0] + touch_state.initial_y_range[1]) / 2
|
|
970
|
-
const y2_center =
|
|
971
|
-
(touch_state.initial_y2_range[0] + touch_state.initial_y2_range[1]) / 2
|
|
972
|
-
|
|
973
|
-
ranges.current.x = [
|
|
974
|
-
x_center - x_span / scale / 2,
|
|
975
|
-
x_center + x_span / scale / 2,
|
|
976
|
-
]
|
|
977
|
-
ranges.current.x2 = [
|
|
978
|
-
x2_center - x2_span / scale / 2,
|
|
979
|
-
x2_center + x2_span / scale / 2,
|
|
980
|
-
]
|
|
981
|
-
ranges.current.y = [
|
|
982
|
-
y_center - y_span / scale / 2,
|
|
983
|
-
y_center + y_span / scale / 2,
|
|
984
|
-
]
|
|
985
|
-
ranges.current.y2 = [
|
|
986
|
-
y2_center - y2_span / scale / 2,
|
|
987
|
-
y2_center + y2_span / scale / 2,
|
|
988
|
-
]
|
|
989
|
-
} else {
|
|
990
|
-
// Pan
|
|
991
|
-
const x_delta = pixels_to_data_delta(
|
|
992
|
-
-dx,
|
|
993
|
-
touch_state.initial_x_range,
|
|
994
|
-
chart_width,
|
|
995
|
-
)
|
|
996
|
-
const x2_delta = pixels_to_data_delta(
|
|
997
|
-
-dx,
|
|
998
|
-
touch_state.initial_x2_range,
|
|
999
|
-
chart_width,
|
|
1000
|
-
)
|
|
1001
|
-
const y_delta = pixels_to_data_delta(
|
|
1002
|
-
dy,
|
|
1003
|
-
touch_state.initial_y_range,
|
|
1004
|
-
chart_height,
|
|
1005
|
-
)
|
|
1006
|
-
const y2_delta = pixels_to_data_delta(
|
|
1007
|
-
dy,
|
|
1008
|
-
touch_state.initial_y2_range,
|
|
1009
|
-
chart_height,
|
|
1010
|
-
)
|
|
1011
|
-
ranges.current.x = pan_range(touch_state.initial_x_range, x_delta)
|
|
1012
|
-
ranges.current.x2 = pan_range(touch_state.initial_x2_range, x2_delta)
|
|
1013
|
-
ranges.current.y = pan_range(touch_state.initial_y_range, y_delta)
|
|
1014
|
-
ranges.current.y2 = pan_range(touch_state.initial_y2_range, y2_delta)
|
|
1015
|
-
}
|
|
783
|
+
zoom_all_axes(touch_state, scale)
|
|
784
|
+
} else pan_all_axes(touch_state, -dx, dy)
|
|
1016
785
|
}
|
|
1017
786
|
|
|
1018
787
|
function handle_touch_end() {
|
|
@@ -1076,34 +845,11 @@
|
|
|
1076
845
|
})
|
|
1077
846
|
)
|
|
1078
847
|
|
|
1079
|
-
|
|
1080
|
-
if (series_idx >= 0 && series_idx < series.length) {
|
|
1081
|
-
series = series.map((srs, idx) =>
|
|
1082
|
-
idx === series_idx ? { ...srs, visible: !(srs.visible ?? true) } : srs
|
|
1083
|
-
)
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
function toggle_group_visibility(_group_name: string, series_indices: number[]) {
|
|
1088
|
-
// Filter to valid indices upfront (consistent with shared toggle_group_visibility)
|
|
1089
|
-
const valid_indices = series_indices.filter((idx) =>
|
|
1090
|
-
idx >= 0 && idx < series.length
|
|
1091
|
-
)
|
|
1092
|
-
if (valid_indices.length === 0) return
|
|
1093
|
-
|
|
1094
|
-
const idx_set = new Set(valid_indices)
|
|
1095
|
-
// Check if all series in the group are currently visible
|
|
1096
|
-
const all_visible = valid_indices.every((idx) => series[idx].visible ?? true)
|
|
1097
|
-
// Toggle: if all visible, hide all; otherwise show all
|
|
1098
|
-
const new_visibility = !all_visible
|
|
1099
|
-
series = series.map((srs, idx) =>
|
|
1100
|
-
idx_set.has(idx) ? { ...srs, visible: new_visibility } : srs
|
|
1101
|
-
)
|
|
1102
|
-
}
|
|
848
|
+
const legend_vis = create_legend_visibility(() => series, (next) => (series = next))
|
|
1103
849
|
|
|
1104
850
|
// Collect bar and line positions for legend placement
|
|
1105
851
|
let bar_points_for_placement = $derived.by(() => {
|
|
1106
|
-
if (!width || !height ||
|
|
852
|
+
if (!width || !height || visible_series.length === 0) return []
|
|
1107
853
|
|
|
1108
854
|
return internal_series.flatMap((srs, series_idx) => {
|
|
1109
855
|
if (!(srs?.visible ?? true)) return []
|
|
@@ -1189,7 +935,6 @@
|
|
|
1189
935
|
|
|
1190
936
|
// Tooltip state
|
|
1191
937
|
let hover_info = $state<BarHandlerProps<Metadata> | null>(null)
|
|
1192
|
-
let tooltip_el = $state<HTMLDivElement | undefined>()
|
|
1193
938
|
|
|
1194
939
|
function get_bar_data(
|
|
1195
940
|
series_idx: number,
|
|
@@ -1267,49 +1012,10 @@
|
|
|
1267
1012
|
}
|
|
1268
1013
|
|
|
1269
1014
|
// Stack offsets (only for bar series in stacked mode, grouped by y-axis)
|
|
1270
|
-
let stacked_offsets = $derived
|
|
1271
|
-
if (mode !== `stacked`) return [] as number[][]
|
|
1272
|
-
const max_len = Math.max(
|
|
1273
|
-
0,
|
|
1274
|
-
...internal_series.map((srs) => srs.y.length),
|
|
1275
|
-
)
|
|
1276
|
-
const offsets = internal_series.map(() =>
|
|
1277
|
-
Array.from({ length: max_len }, () => 0)
|
|
1278
|
-
)
|
|
1279
|
-
|
|
1280
|
-
// Separate accumulators for y1 and y2 axes
|
|
1281
|
-
const y1_pos_acc = Array.from({ length: max_len }, () => 0)
|
|
1282
|
-
const y1_neg_acc = Array.from({ length: max_len }, () => 0)
|
|
1283
|
-
const y2_pos_acc = Array.from({ length: max_len }, () => 0)
|
|
1284
|
-
const y2_neg_acc = Array.from({ length: max_len }, () => 0)
|
|
1285
|
-
|
|
1286
|
-
internal_series.forEach((srs, series_idx) => {
|
|
1287
|
-
if (!(srs?.visible ?? true) || srs.render_mode === `line`) return
|
|
1288
|
-
|
|
1289
|
-
const use_y2 = srs.y_axis === `y2`
|
|
1290
|
-
const pos_acc = use_y2 ? y2_pos_acc : y1_pos_acc
|
|
1291
|
-
const neg_acc = use_y2 ? y2_neg_acc : y1_neg_acc
|
|
1292
|
-
|
|
1293
|
-
for (let bar_idx = 0; bar_idx < max_len; bar_idx++) {
|
|
1294
|
-
const y_val = srs.y[bar_idx] ?? 0
|
|
1295
|
-
const acc = y_val >= 0 ? pos_acc : neg_acc
|
|
1296
|
-
offsets[series_idx][bar_idx] = acc[bar_idx]
|
|
1297
|
-
acc[bar_idx] += y_val
|
|
1298
|
-
}
|
|
1299
|
-
})
|
|
1300
|
-
return offsets
|
|
1301
|
-
})
|
|
1015
|
+
let stacked_offsets = $derived(compute_stacked_offsets(internal_series, mode))
|
|
1302
1016
|
|
|
1303
1017
|
// Calculate group positions for grouped mode (side-by-side bars)
|
|
1304
|
-
let group_info = $derived
|
|
1305
|
-
if (mode !== `grouped`) return { bar_series_count: 0, bar_series_indices: [] }
|
|
1306
|
-
const bar_series_indices = internal_series
|
|
1307
|
-
.map((srs, idx) =>
|
|
1308
|
-
(srs?.visible ?? true) && srs.render_mode !== `line` ? idx : -1
|
|
1309
|
-
)
|
|
1310
|
-
.filter((idx) => idx >= 0)
|
|
1311
|
-
return { bar_series_count: bar_series_indices.length, bar_series_indices }
|
|
1312
|
-
})
|
|
1018
|
+
let group_info = $derived(compute_group_info(internal_series, mode))
|
|
1313
1019
|
|
|
1314
1020
|
// Set theme-aware background when entering fullscreen
|
|
1315
1021
|
$effect(() => {
|
|
@@ -1327,9 +1033,9 @@
|
|
|
1327
1033
|
set_axis: (axis, config) => {
|
|
1328
1034
|
// Spread into existing state to preserve merged type structure
|
|
1329
1035
|
if (axis === `x`) x_axis = { ...x_axis, ...config }
|
|
1330
|
-
else if (axis === `x2`)
|
|
1036
|
+
else if (axis === `x2`) x2_axis_prop = { ...x2_axis_prop, ...config }
|
|
1331
1037
|
else if (axis === `y`) y_axis = { ...y_axis, ...config }
|
|
1332
|
-
else
|
|
1038
|
+
else y2_axis_prop = { ...y2_axis_prop, ...config }
|
|
1333
1039
|
},
|
|
1334
1040
|
get_series: () => series,
|
|
1335
1041
|
set_series: (new_series) => (series = new_series),
|
|
@@ -1337,32 +1043,12 @@
|
|
|
1337
1043
|
set_loading: (axis) => (axis_loading = axis),
|
|
1338
1044
|
}
|
|
1339
1045
|
|
|
1340
|
-
//
|
|
1341
|
-
|
|
1342
|
-
const handle_axis_change = $derived(create_axis_change_handler(
|
|
1046
|
+
// Shared handler + one-shot auto-load bound to this component's state
|
|
1047
|
+
const { handle_axis_change, try_auto_load } = create_axis_loader(
|
|
1343
1048
|
axis_state,
|
|
1344
|
-
data_loader,
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
))
|
|
1348
|
-
|
|
1349
|
-
let auto_load_attempted = false // prevent infinite retries on failure
|
|
1350
|
-
|
|
1351
|
-
// Auto-load data if series is empty but options exist (runs once)
|
|
1352
|
-
$effect(() => {
|
|
1353
|
-
if (series.length === 0 && data_loader && !auto_load_attempted) {
|
|
1354
|
-
// Check x-axis first, then y-axis
|
|
1355
|
-
if (x_axis.options?.length) {
|
|
1356
|
-
auto_load_attempted = true
|
|
1357
|
-
const first_key = x_axis.selected_key ?? x_axis.options[0].key
|
|
1358
|
-
handle_axis_change(`x`, first_key).catch(() => {})
|
|
1359
|
-
} else if (y_axis.options?.length) {
|
|
1360
|
-
auto_load_attempted = true
|
|
1361
|
-
const first_key = y_axis.selected_key ?? y_axis.options[0].key
|
|
1362
|
-
handle_axis_change(`y`, first_key).catch(() => {})
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
})
|
|
1049
|
+
() => ({ data_loader, on_axis_change, on_error }),
|
|
1050
|
+
)
|
|
1051
|
+
$effect(try_auto_load)
|
|
1366
1052
|
</script>
|
|
1367
1053
|
|
|
1368
1054
|
{#snippet ref_lines_layer(lines: IndexedRefLine[])}
|
|
@@ -1440,9 +1126,9 @@
|
|
|
1440
1126
|
ranges.current.y2 = [...ranges.initial.y2] as [number, number]
|
|
1441
1127
|
// Also reset axis props so future data changes recalculate auto ranges
|
|
1442
1128
|
x_axis = { ...x_axis, range: [null, null] }
|
|
1443
|
-
|
|
1129
|
+
x2_axis_prop = { ...x2_axis_prop, range: [null, null] }
|
|
1444
1130
|
y_axis = { ...y_axis, range: [null, null] }
|
|
1445
|
-
|
|
1131
|
+
y2_axis_prop = { ...y2_axis_prop, range: [null, null] }
|
|
1446
1132
|
}}
|
|
1447
1133
|
onmouseleave={() => {
|
|
1448
1134
|
hovered = false
|
|
@@ -1454,6 +1140,7 @@
|
|
|
1454
1140
|
ontouchstart={handle_touch_start}
|
|
1455
1141
|
ontouchmove={handle_touch_move}
|
|
1456
1142
|
ontouchend={handle_touch_end}
|
|
1143
|
+
ontouchcancel={handle_touch_end}
|
|
1457
1144
|
style:cursor={pan_drag_state
|
|
1458
1145
|
? `grabbing`
|
|
1459
1146
|
: shift_held && pan?.enabled !== false
|
|
@@ -1487,6 +1174,7 @@
|
|
|
1487
1174
|
ticks={ticks.x as number[]}
|
|
1488
1175
|
place={scales.x}
|
|
1489
1176
|
axis={x_axis}
|
|
1177
|
+
domain={ranges.current.x as Vec2}
|
|
1490
1178
|
{pad}
|
|
1491
1179
|
{width}
|
|
1492
1180
|
{height}
|
|
@@ -1507,6 +1195,7 @@
|
|
|
1507
1195
|
ticks={ticks.x2 as number[]}
|
|
1508
1196
|
place={scales.x2}
|
|
1509
1197
|
axis={x2_axis}
|
|
1198
|
+
domain={ranges.current.x2 as Vec2}
|
|
1510
1199
|
{pad}
|
|
1511
1200
|
{width}
|
|
1512
1201
|
{height}
|
|
@@ -1525,6 +1214,7 @@
|
|
|
1525
1214
|
ticks={ticks.y as number[]}
|
|
1526
1215
|
place={scales.y}
|
|
1527
1216
|
axis={y_axis}
|
|
1217
|
+
domain={ranges.current.y as Vec2}
|
|
1528
1218
|
{pad}
|
|
1529
1219
|
{width}
|
|
1530
1220
|
{height}
|
|
@@ -1544,21 +1234,18 @@
|
|
|
1544
1234
|
<!-- Y2-axis (Right) -->
|
|
1545
1235
|
<!-- Note: y2 axis is only supported for vertical orientation. Implementing x2 for horizontal mode requires additional complexity. -->
|
|
1546
1236
|
{#if y2_series.length > 0 && orientation === `vertical`}
|
|
1547
|
-
{@const y2_inside = y2_axis.tick?.label?.inside ?? false}
|
|
1548
|
-
{@const y2_tick_shift = y2_inside ? 0 : (y2_axis.tick?.label?.shift?.x ?? 0) + 8}
|
|
1549
|
-
{@const y2_tick_width = y2_inside ? 0 : tick_label_widths.y2_max}
|
|
1550
1237
|
<PlotAxis
|
|
1551
1238
|
side="y2"
|
|
1552
1239
|
ticks={ticks.y2 as number[]}
|
|
1553
1240
|
place={scales.y2}
|
|
1554
1241
|
axis={y2_axis}
|
|
1242
|
+
domain={ranges.current.y2 as Vec2}
|
|
1555
1243
|
{pad}
|
|
1556
1244
|
{width}
|
|
1557
1245
|
{height}
|
|
1558
1246
|
show_grid={display.y2_grid}
|
|
1559
1247
|
tick_label={(tick) => get_tick_label(tick, y2_axis.ticks)}
|
|
1560
|
-
label_x={width
|
|
1561
|
-
(y2_axis.label_shift?.x ?? 0)}
|
|
1248
|
+
label_x={y2_axis_label_x(y2_axis, width, pad.r, tick_label_widths.y2_max)}
|
|
1562
1249
|
label_y={pad.t + chart_height / 2 + (y2_axis.label_shift?.y ?? 0)}
|
|
1563
1250
|
axis_loading={axis_loading === `y2`}
|
|
1564
1251
|
on_axis_change={(key) => handle_axis_change(`y2`, key)}
|
|
@@ -1572,7 +1259,10 @@
|
|
|
1572
1259
|
</clipPath>
|
|
1573
1260
|
</defs>
|
|
1574
1261
|
|
|
1575
|
-
<!--
|
|
1262
|
+
<!-- Chart content is clipped in two groups so reference lines can interleave
|
|
1263
|
+
at their z positions while staying outside the chart clip: each line still
|
|
1264
|
+
self-clips to the plot area inside ReferenceLine, only its annotation text
|
|
1265
|
+
is allowed to overflow the plot edges. -->
|
|
1576
1266
|
<g clip-path="url(#{clip_path_id})">
|
|
1577
1267
|
<ZeroLines
|
|
1578
1268
|
{display}
|
|
@@ -1596,11 +1286,12 @@
|
|
|
1596
1286
|
{height}
|
|
1597
1287
|
{pad}
|
|
1598
1288
|
/>
|
|
1289
|
+
</g>
|
|
1599
1290
|
|
|
1600
|
-
|
|
1601
|
-
{@render ref_lines_layer(ref_lines_by_z.below_lines)}
|
|
1291
|
+
{@render ref_lines_layer(ref_lines_by_z.below_lines)}
|
|
1602
1292
|
|
|
1603
|
-
|
|
1293
|
+
<!-- Bars and Lines -->
|
|
1294
|
+
<g clip-path="url(#{clip_path_id})">
|
|
1604
1295
|
{#each internal_series as srs, series_idx (srs?.id ?? series_idx)}
|
|
1605
1296
|
{#if srs?.visible ?? true}
|
|
1606
1297
|
{@const is_line = srs.render_mode === `line`}
|
|
@@ -1626,42 +1317,14 @@
|
|
|
1626
1317
|
series_markers === `line+points`}
|
|
1627
1318
|
{@const show_points = series_markers === `points` ||
|
|
1628
1319
|
series_markers === `line+points`}
|
|
1629
|
-
{@const points =
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
: scales.y(x_val)
|
|
1638
|
-
// Create internal point with all needed data
|
|
1639
|
-
const color_value = srs.color_values?.[idx] ?? null
|
|
1640
|
-
const size_value = srs.size_values?.[idx] ?? null
|
|
1641
|
-
const point_style = process_prop(srs.point_style, idx)
|
|
1642
|
-
const point_hover = process_prop(srs.point_hover, idx)
|
|
1643
|
-
const point_label = process_prop(srs.point_label, idx)
|
|
1644
|
-
const point_offset = process_prop(srs.point_offset, idx)
|
|
1645
|
-
const metadata = Array.isArray(srs.metadata)
|
|
1646
|
-
? srs.metadata[idx]
|
|
1647
|
-
: srs.metadata
|
|
1648
|
-
return {
|
|
1649
|
-
x: plot_x,
|
|
1650
|
-
y: plot_y,
|
|
1651
|
-
data_x: x_val,
|
|
1652
|
-
data_y: y_val,
|
|
1653
|
-
idx,
|
|
1654
|
-
color_value,
|
|
1655
|
-
size_value,
|
|
1656
|
-
point_style,
|
|
1657
|
-
point_hover,
|
|
1658
|
-
point_label,
|
|
1659
|
-
point_offset,
|
|
1660
|
-
metadata,
|
|
1661
|
-
series_idx,
|
|
1662
|
-
point_idx: idx,
|
|
1663
|
-
} as LineSeriesPoint
|
|
1664
|
-
}).filter((pt) => isFinite(pt.x) && isFinite(pt.y))}
|
|
1320
|
+
{@const points = compute_line_points({
|
|
1321
|
+
series: srs,
|
|
1322
|
+
series_idx,
|
|
1323
|
+
orientation,
|
|
1324
|
+
x_scale,
|
|
1325
|
+
y_scale,
|
|
1326
|
+
cat_y_scale: scales.y,
|
|
1327
|
+
})}
|
|
1665
1328
|
{@const polyline_str = show_line && points.length > 1
|
|
1666
1329
|
? points.map((pt) => `${pt.x},${pt.y}`).join(` `)
|
|
1667
1330
|
: ``}
|
|
@@ -1826,37 +1489,24 @@
|
|
|
1826
1489
|
{@const bar_width_val = Array.isArray(srs.bar_width)
|
|
1827
1490
|
? (srs.bar_width[bar_idx] ?? 0.5)
|
|
1828
1491
|
: (srs.bar_width ?? 0.5)}
|
|
1829
|
-
{@const half = mode === `grouped` && group_info.bar_series_count > 1
|
|
1830
|
-
? bar_width_val / (2 * group_info.bar_series_count)
|
|
1831
|
-
: bar_width_val / 2}
|
|
1832
|
-
{@const calculate_group_offset = (idx: number) => {
|
|
1833
|
-
const position = group_info.bar_series_indices.indexOf(idx)
|
|
1834
|
-
const offset = position - (group_info.bar_series_count - 1) / 2
|
|
1835
|
-
return offset * (bar_width_val / group_info.bar_series_count)
|
|
1836
|
-
}}
|
|
1837
|
-
{@const group_offset = mode === `grouped` && group_info.bar_series_count > 1
|
|
1838
|
-
? calculate_group_offset(series_idx)
|
|
1839
|
-
: 0}
|
|
1840
1492
|
{@const is_vertical = orientation === `vertical`}
|
|
1841
|
-
{@const
|
|
1842
|
-
{@const val = y_val}
|
|
1843
|
-
{@const use_y2 = srs.y_axis === `y2`}
|
|
1844
|
-
{@const y_scale = use_y2 ? scales.y2 : scales.y}
|
|
1845
|
-
{@const use_x2_bar = srs.x_axis === `x2`}
|
|
1846
|
-
{@const x_scale_bar = use_x2_bar ? scales.x2 : scales.x}
|
|
1493
|
+
{@const x_scale_bar = srs.x_axis === `x2` ? scales.x2 : scales.x}
|
|
1847
1494
|
{@const [cat_scale, val_scale] = is_vertical
|
|
1848
|
-
? [x_scale_bar,
|
|
1495
|
+
? [x_scale_bar, srs.y_axis === `y2` ? scales.y2 : scales.y]
|
|
1849
1496
|
: [scales.y, x_scale_bar]}
|
|
1850
|
-
{@const c0
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1497
|
+
{@const { c0, c1, v0, v1, rect_x, rect_y, rect_w, rect_h } =
|
|
1498
|
+
compute_bar_rect({
|
|
1499
|
+
cat_val: x_val,
|
|
1500
|
+
val: y_val,
|
|
1501
|
+
base,
|
|
1502
|
+
bar_width_val,
|
|
1503
|
+
series_idx,
|
|
1504
|
+
mode,
|
|
1505
|
+
orientation,
|
|
1506
|
+
group_info,
|
|
1507
|
+
cat_scale,
|
|
1508
|
+
val_scale,
|
|
1509
|
+
})}
|
|
1860
1510
|
{#if (is_vertical ? rect_h : rect_w) > 0}
|
|
1861
1511
|
<path
|
|
1862
1512
|
d={bar_path(
|
|
@@ -1914,13 +1564,10 @@
|
|
|
1914
1564
|
</g>
|
|
1915
1565
|
{/if}
|
|
1916
1566
|
{/each}
|
|
1917
|
-
|
|
1918
|
-
<!-- Reference lines: below points -->
|
|
1919
|
-
{@render ref_lines_layer(ref_lines_by_z.below_points)}
|
|
1920
|
-
|
|
1921
|
-
<!-- Reference lines: above all -->
|
|
1922
|
-
{@render ref_lines_layer(ref_lines_by_z.above_all)}
|
|
1923
1567
|
</g>
|
|
1568
|
+
|
|
1569
|
+
{@render ref_lines_layer(ref_lines_by_z.below_points)}
|
|
1570
|
+
{@render ref_lines_layer(ref_lines_by_z.above_all)}
|
|
1924
1571
|
</svg>
|
|
1925
1572
|
|
|
1926
1573
|
<!-- Legend -->
|
|
@@ -1939,12 +1586,13 @@
|
|
|
1939
1586
|
bind:root_element={legend_element}
|
|
1940
1587
|
{...legend}
|
|
1941
1588
|
series_data={legend_data}
|
|
1942
|
-
on_toggle={legend?.on_toggle
|
|
1943
|
-
on_group_toggle={legend?.on_group_toggle
|
|
1589
|
+
on_toggle={legend?.on_toggle ?? legend_vis.on_toggle}
|
|
1590
|
+
on_group_toggle={legend?.on_group_toggle ?? legend_vis.on_group_toggle}
|
|
1591
|
+
on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
|
|
1944
1592
|
on_hover_change={legend_hover.set_locked}
|
|
1945
|
-
on_item_hover={(
|
|
1946
|
-
(hovered_legend_series_idx =
|
|
1947
|
-
? series_idx
|
|
1593
|
+
on_item_hover={(item) =>
|
|
1594
|
+
(hovered_legend_series_idx = item != null && item.series_idx >= 0
|
|
1595
|
+
? item.series_idx
|
|
1948
1596
|
: null)}
|
|
1949
1597
|
active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
|
|
1950
1598
|
style={`
|
|
@@ -1963,22 +1611,14 @@
|
|
|
1963
1611
|
)}
|
|
1964
1612
|
{@const cy = (hover_info.active_y_axis === `y2` ? scales.y2 : scales.y)(
|
|
1965
1613
|
hover_info.orient_y,
|
|
1966
|
-
)}
|
|
1967
|
-
{@const tooltip_pos = constrain_tooltip_position(
|
|
1968
|
-
cx,
|
|
1969
|
-
cy,
|
|
1970
|
-
tooltip_el?.offsetWidth ?? 140,
|
|
1971
|
-
tooltip_el?.offsetHeight ?? 50,
|
|
1972
|
-
width,
|
|
1973
|
-
height,
|
|
1974
|
-
{ offset_x: 10, offset_y: 5 },
|
|
1975
1614
|
)}
|
|
1976
1615
|
<PlotTooltip
|
|
1977
|
-
x={
|
|
1978
|
-
y={
|
|
1979
|
-
offset={{ x:
|
|
1616
|
+
x={cx}
|
|
1617
|
+
y={cy}
|
|
1618
|
+
offset={{ x: 10, y: 5 }}
|
|
1619
|
+
constrain_to={{ width, height }}
|
|
1620
|
+
fallback_size={{ width: 140, height: 50 }}
|
|
1980
1621
|
bg_color={hover_info.color}
|
|
1981
|
-
bind:wrapper={tooltip_el}
|
|
1982
1622
|
>
|
|
1983
1623
|
{#if tooltip}
|
|
1984
1624
|
{@render tooltip({ ...hover_info, fullscreen })}
|
|
@@ -2017,9 +1657,9 @@
|
|
|
2017
1657
|
bind:orientation
|
|
2018
1658
|
bind:mode
|
|
2019
1659
|
bind:x_axis
|
|
2020
|
-
bind:x2_axis
|
|
1660
|
+
bind:x2_axis={x2_axis_prop}
|
|
2021
1661
|
bind:y_axis
|
|
2022
|
-
bind:y2_axis
|
|
1662
|
+
bind:y2_axis={y2_axis_prop}
|
|
2023
1663
|
bind:display
|
|
2024
1664
|
auto_x_range={auto_ranges.x as Vec2}
|
|
2025
1665
|
auto_x2_range={auto_ranges.x2 as Vec2}
|
|
@@ -2063,8 +1703,10 @@
|
|
|
2063
1703
|
background: var(--barplot-fullscreen-bg, var(--barplot-bg, var(--plot-bg)));
|
|
2064
1704
|
max-height: none !important;
|
|
2065
1705
|
overflow: hidden;
|
|
2066
|
-
/*
|
|
2067
|
-
|
|
1706
|
+
/* border-top (not padding-top): bind:clientHeight includes padding but excludes
|
|
1707
|
+
borders - padding made the chart overflow + clip its bottom 2em (x-axis title) */
|
|
1708
|
+
border-top: var(--plot-fullscreen-padding-top, 2em) solid
|
|
1709
|
+
var(--barplot-fullscreen-bg, var(--barplot-bg, var(--plot-bg, transparent)));
|
|
2068
1710
|
box-sizing: border-box;
|
|
2069
1711
|
}
|
|
2070
1712
|
.header-controls {
|