matterviz 0.3.4 → 0.3.6
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/.vscode/launch.json +13 -0
- package/.vscodeignore +7 -0
- package/dist/assets/STLExporter-BpTH3YHE.js +8 -0
- package/dist/assets/browser-DdDecX_W.js +1 -0
- package/dist/assets/export-qgn-H9y6.js +2 -0
- package/dist/assets/main-DiKYzti2.css +1 -0
- package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
- package/dist/extension.js +31293 -0
- package/dist/src/lib/FilePicker.svelte +360 -0
- package/dist/src/lib/MillerIndexInput.svelte +66 -0
- package/dist/src/lib/api/mp.ts +26 -0
- package/dist/src/lib/api/optimade.ts +204 -0
- package/dist/src/lib/app.css +247 -0
- package/dist/src/lib/brillouin/BrillouinZone.svelte +549 -0
- package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +144 -0
- package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +146 -0
- package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +146 -0
- package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +476 -0
- package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +92 -0
- package/dist/src/lib/brillouin/compute.ts +529 -0
- package/dist/src/lib/brillouin/index.ts +8 -0
- package/dist/src/lib/brillouin/types.ts +51 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +327 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +846 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +3193 -0
- package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +94 -0
- package/dist/src/lib/chempot-diagram/chempot-worker.ts +11 -0
- package/dist/src/lib/chempot-diagram/color.ts +42 -0
- package/dist/src/lib/chempot-diagram/compute.ts +1014 -0
- package/dist/src/lib/chempot-diagram/index.ts +6 -0
- package/dist/src/lib/chempot-diagram/pointer.ts +56 -0
- package/dist/src/lib/chempot-diagram/temperature.ts +77 -0
- package/dist/src/lib/chempot-diagram/types.ts +130 -0
- package/dist/src/lib/colors/index.ts +249 -0
- package/dist/src/lib/composition/BarChart.svelte +297 -0
- package/dist/src/lib/composition/BubbleChart.svelte +218 -0
- package/dist/src/lib/composition/Composition.svelte +165 -0
- package/dist/src/lib/composition/Formula.svelte +268 -0
- package/dist/src/lib/composition/FormulaFilter.svelte +1257 -0
- package/dist/src/lib/composition/PieChart.svelte +323 -0
- package/dist/src/lib/composition/format.ts +155 -0
- package/dist/src/lib/composition/index.ts +37 -0
- package/dist/src/lib/composition/parse.ts +605 -0
- package/dist/src/lib/constants.ts +134 -0
- package/dist/src/lib/controls.ts +42 -0
- package/dist/src/lib/convex-hull/ConvexHull.svelte +157 -0
- package/dist/src/lib/convex-hull/ConvexHull2D.svelte +825 -0
- package/dist/src/lib/convex-hull/ConvexHull3D.svelte +1801 -0
- package/dist/src/lib/convex-hull/ConvexHull4D.svelte +1398 -0
- package/dist/src/lib/convex-hull/ConvexHullControls.svelte +535 -0
- package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +125 -0
- package/dist/src/lib/convex-hull/ConvexHullStats.svelte +929 -0
- package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +131 -0
- package/dist/src/lib/convex-hull/GasPressureControls.svelte +247 -0
- package/dist/src/lib/convex-hull/StructurePopup.svelte +151 -0
- package/dist/src/lib/convex-hull/TemperatureSlider.svelte +140 -0
- package/dist/src/lib/convex-hull/barycentric-coords.ts +246 -0
- package/dist/src/lib/convex-hull/demo-temperature.ts +63 -0
- package/dist/src/lib/convex-hull/gas-thermodynamics.ts +405 -0
- package/dist/src/lib/convex-hull/helpers.ts +932 -0
- package/dist/src/lib/convex-hull/index.ts +202 -0
- package/dist/src/lib/convex-hull/thermodynamics.ts +2192 -0
- package/dist/src/lib/convex-hull/types.ts +267 -0
- package/dist/src/lib/coordination/CoordinationBarPlot.svelte +311 -0
- package/dist/src/lib/coordination/calc-coordination.ts +93 -0
- package/dist/src/lib/coordination/index.ts +9 -0
- package/dist/src/lib/effects.svelte.ts +48 -0
- package/dist/src/lib/element/BohrAtom.svelte +147 -0
- package/dist/src/lib/element/ElementHeading.svelte +26 -0
- package/dist/src/lib/element/ElementPhoto.svelte +57 -0
- package/dist/src/lib/element/ElementStats.svelte +80 -0
- package/dist/src/lib/element/ElementTile.svelte +484 -0
- package/dist/src/lib/element/data.json.gz.d.ts +4 -0
- package/dist/src/lib/element/data.ts +14 -0
- package/dist/src/lib/element/index.ts +8 -0
- package/dist/src/lib/element/types.ts +62 -0
- package/dist/src/lib/feedback/ClickFeedback.svelte +58 -0
- package/dist/src/lib/feedback/DragOverlay.svelte +42 -0
- package/dist/src/lib/feedback/index.ts +4 -0
- package/dist/src/lib/fermi-surface/FermiSlice.svelte +189 -0
- package/dist/src/lib/fermi-surface/FermiSurface.svelte +600 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +448 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +794 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +111 -0
- package/dist/src/lib/fermi-surface/compute.ts +728 -0
- package/dist/src/lib/fermi-surface/constants.ts +32 -0
- package/dist/src/lib/fermi-surface/export.ts +64 -0
- package/dist/src/lib/fermi-surface/index.ts +14 -0
- package/dist/src/lib/fermi-surface/marching-cubes.ts +3 -0
- package/dist/src/lib/fermi-surface/parse.ts +574 -0
- package/dist/src/lib/fermi-surface/symmetry.ts +56 -0
- package/dist/src/lib/fermi-surface/types.ts +159 -0
- package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +1545 -0
- package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
- package/dist/src/lib/heatmap-matrix/index.ts +167 -0
- package/dist/src/lib/heatmap-matrix/shared.ts +7 -0
- package/dist/src/lib/icons.ts +650 -0
- package/dist/src/lib/index.ts +61 -0
- package/dist/src/lib/io/decompress.ts +92 -0
- package/dist/src/lib/io/export.ts +385 -0
- package/dist/src/lib/io/fetch.ts +46 -0
- package/dist/src/lib/io/file-drop.ts +51 -0
- package/dist/src/lib/io/index.ts +7 -0
- package/dist/src/lib/io/is-binary.ts +24 -0
- package/dist/src/lib/io/types.ts +8 -0
- package/dist/src/lib/io/url-drop.ts +141 -0
- package/dist/src/lib/isosurface/Isosurface.svelte +285 -0
- package/dist/src/lib/isosurface/IsosurfaceControls.svelte +277 -0
- package/dist/src/lib/isosurface/index.ts +7 -0
- package/dist/src/lib/isosurface/parse.ts +656 -0
- package/dist/src/lib/isosurface/slice.ts +175 -0
- package/dist/src/lib/isosurface/types.ts +309 -0
- package/dist/src/lib/labels.ts +320 -0
- package/dist/src/lib/layout/FullscreenToggle.svelte +50 -0
- package/dist/src/lib/layout/InfoCard.svelte +120 -0
- package/dist/src/lib/layout/InfoTag.svelte +185 -0
- package/dist/src/lib/layout/PropertyFilter.svelte +246 -0
- package/dist/src/lib/layout/SettingsSection.svelte +148 -0
- package/dist/src/lib/layout/SubpageGrid.svelte +82 -0
- package/dist/src/lib/layout/fullscreen.ts +65 -0
- package/dist/src/lib/layout/index.ts +11 -0
- package/dist/src/lib/layout/json-tree/JsonNode.svelte +548 -0
- package/dist/src/lib/layout/json-tree/JsonTree.svelte +1230 -0
- package/dist/src/lib/layout/json-tree/JsonValue.svelte +334 -0
- package/dist/src/lib/layout/json-tree/index.ts +3 -0
- package/dist/src/lib/layout/json-tree/types.ts +126 -0
- package/dist/src/lib/layout/json-tree/utils.ts +682 -0
- package/dist/src/lib/marching-cubes.ts +614 -0
- package/dist/src/lib/math.ts +1081 -0
- package/dist/src/lib/overlays/ContextMenu.svelte +162 -0
- package/dist/src/lib/overlays/CopyButton.svelte +45 -0
- package/dist/src/lib/overlays/DragControlTab.svelte +98 -0
- package/dist/src/lib/overlays/DraggablePane.svelte +487 -0
- package/dist/src/lib/overlays/InfoPaneCards.svelte +149 -0
- package/dist/src/lib/overlays/index.ts +3 -0
- package/dist/src/lib/periodic-table/PeriodicTable.svelte +469 -0
- package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +557 -0
- package/dist/src/lib/periodic-table/PropertySelect.svelte +37 -0
- package/dist/src/lib/periodic-table/index.ts +12 -0
- package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1086 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +444 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
- package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +203 -0
- package/dist/src/lib/phase-diagram/build-diagram.ts +186 -0
- package/dist/src/lib/phase-diagram/colors.ts +58 -0
- package/dist/src/lib/phase-diagram/diagram-input.ts +40 -0
- package/dist/src/lib/phase-diagram/index.ts +13 -0
- package/dist/src/lib/phase-diagram/parse.ts +348 -0
- package/dist/src/lib/phase-diagram/svg-to-diagram.ts +1023 -0
- package/dist/src/lib/phase-diagram/types.ts +144 -0
- package/dist/src/lib/phase-diagram/utils.ts +775 -0
- package/dist/src/lib/plot/AxisLabel.svelte +51 -0
- package/dist/src/lib/plot/BarPlot.svelte +2113 -0
- package/dist/src/lib/plot/BarPlotControls.svelte +66 -0
- package/dist/src/lib/plot/BinnedScatterPlot.svelte +1114 -0
- package/dist/src/lib/plot/ColorBar.svelte +721 -0
- package/dist/src/lib/plot/ColorScaleSelect.svelte +54 -0
- package/dist/src/lib/plot/ElementScatter.svelte +63 -0
- package/dist/src/lib/plot/FillArea.svelte +223 -0
- package/dist/src/lib/plot/Histogram.svelte +1558 -0
- package/dist/src/lib/plot/HistogramControls.svelte +212 -0
- package/dist/src/lib/plot/InteractiveAxisLabel.svelte +96 -0
- package/dist/src/lib/plot/Line.svelte +84 -0
- package/dist/src/lib/plot/PlotAxis.svelte +169 -0
- package/dist/src/lib/plot/PlotControls.svelte +537 -0
- package/dist/src/lib/plot/PlotLegend.svelte +569 -0
- package/dist/src/lib/plot/PlotTooltip.svelte +67 -0
- package/dist/src/lib/plot/PortalSelect.svelte +253 -0
- package/dist/src/lib/plot/ReferenceLine3D.svelte +156 -0
- package/dist/src/lib/plot/ReferencePlane.svelte +175 -0
- package/dist/src/lib/plot/ScatterPlot.svelte +2778 -0
- package/dist/src/lib/plot/ScatterPlot3D.svelte +529 -0
- package/dist/src/lib/plot/ScatterPlot3DControls.svelte +437 -0
- package/dist/src/lib/plot/ScatterPlot3DScene.svelte +912 -0
- package/dist/src/lib/plot/ScatterPlotControls.svelte +306 -0
- package/dist/src/lib/plot/ScatterPoint.svelte +182 -0
- package/dist/src/lib/plot/SpacegroupBarPlot.svelte +293 -0
- package/dist/src/lib/plot/Surface3D.svelte +197 -0
- package/dist/src/lib/plot/ZeroLines.svelte +97 -0
- package/dist/src/lib/plot/ZoomRect.svelte +23 -0
- package/dist/src/lib/plot/adaptive-density.ts +316 -0
- package/dist/src/lib/plot/auto-place.ts +184 -0
- package/dist/src/lib/plot/axis-utils.ts +122 -0
- package/dist/src/lib/plot/binned-scatter-types.ts +83 -0
- package/dist/src/lib/plot/data-cleaning.ts +1069 -0
- package/dist/src/lib/plot/data-transform.ts +69 -0
- package/dist/src/lib/plot/defaults.ts +9 -0
- package/dist/src/lib/plot/fill-utils.ts +494 -0
- package/dist/src/lib/plot/hover-lock.svelte.ts +60 -0
- package/dist/src/lib/plot/index.ts +53 -0
- package/dist/src/lib/plot/interactions.ts +119 -0
- package/dist/src/lib/plot/layout.ts +425 -0
- package/dist/src/lib/plot/reference-line.ts +426 -0
- package/dist/src/lib/plot/scales.ts +654 -0
- package/dist/src/lib/plot/svg.ts +23 -0
- package/dist/src/lib/plot/types.ts +1144 -0
- package/dist/src/lib/plot/utils/label-placement.ts +541 -0
- package/dist/src/lib/plot/utils/series-visibility.ts +140 -0
- package/dist/src/lib/plot/utils.ts +11 -0
- package/dist/src/lib/rdf/RdfPlot.svelte +247 -0
- package/dist/src/lib/rdf/calc-rdf.ts +167 -0
- package/dist/src/lib/rdf/index.ts +27 -0
- package/dist/src/lib/sanitize.ts +126 -0
- package/dist/src/lib/settings.ts +1479 -0
- package/dist/src/lib/spectral/Bands.svelte +1040 -0
- package/dist/src/lib/spectral/BandsAndDos.svelte +134 -0
- package/dist/src/lib/spectral/BrillouinBandsDos.svelte +252 -0
- package/dist/src/lib/spectral/Dos.svelte +697 -0
- package/dist/src/lib/spectral/helpers.ts +1381 -0
- package/dist/src/lib/spectral/index.ts +8 -0
- package/dist/src/lib/spectral/types.ts +112 -0
- package/dist/src/lib/state.svelte.ts +64 -0
- package/dist/src/lib/structure/Arrow.svelte +72 -0
- package/dist/src/lib/structure/AtomLegend.svelte +815 -0
- package/dist/src/lib/structure/Bond.svelte +140 -0
- package/dist/src/lib/structure/CanvasTooltip.svelte +33 -0
- package/dist/src/lib/structure/CellSelect.svelte +349 -0
- package/dist/src/lib/structure/Cylinder.svelte +45 -0
- package/dist/src/lib/structure/Lattice.svelte +196 -0
- package/dist/src/lib/structure/Structure.svelte +2248 -0
- package/dist/src/lib/structure/StructureControls.svelte +1273 -0
- package/dist/src/lib/structure/StructureExportPane.svelte +252 -0
- package/dist/src/lib/structure/StructureInfoPane.svelte +737 -0
- package/dist/src/lib/structure/StructureScene.svelte +2255 -0
- package/dist/src/lib/structure/atom-properties.ts +316 -0
- package/dist/src/lib/structure/bond-order-perception.ts +447 -0
- package/dist/src/lib/structure/bonding.ts +944 -0
- package/dist/src/lib/structure/export.ts +861 -0
- package/dist/src/lib/structure/index.ts +291 -0
- package/dist/src/lib/structure/label-placement.ts +130 -0
- package/dist/src/lib/structure/measure.ts +45 -0
- package/dist/src/lib/structure/parse.ts +1705 -0
- package/dist/src/lib/structure/partial-occupancy.ts +183 -0
- package/dist/src/lib/structure/pbc.ts +164 -0
- package/dist/src/lib/structure/supercell.ts +226 -0
- package/dist/src/lib/structure/validation.ts +11 -0
- package/dist/src/lib/symmetry/SymmetryStats.svelte +226 -0
- package/dist/src/lib/symmetry/WyckoffTable.svelte +120 -0
- package/dist/src/lib/symmetry/cell-transform.ts +118 -0
- package/dist/src/lib/symmetry/index.ts +348 -0
- package/dist/src/lib/symmetry/spacegroups.ts +404 -0
- package/dist/src/lib/table/HeatmapTable.svelte +1833 -0
- package/dist/src/lib/table/ToggleMenu.svelte +385 -0
- package/dist/src/lib/table/index.ts +139 -0
- package/dist/src/lib/theme/ThemeControl.svelte +53 -0
- package/dist/src/lib/theme/index.ts +107 -0
- package/dist/src/lib/theme/themes.mjs +297 -0
- package/dist/src/lib/time.ts +71 -0
- package/dist/src/lib/tooltip/TooltipContent.svelte +58 -0
- package/dist/src/lib/tooltip/index.ts +2 -0
- package/dist/src/lib/tooltip/types.ts +13 -0
- package/dist/src/lib/trajectory/Trajectory.svelte +1545 -0
- package/dist/src/lib/trajectory/TrajectoryError.svelte +128 -0
- package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +357 -0
- package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +313 -0
- package/dist/src/lib/trajectory/constants.ts +7 -0
- package/dist/src/lib/trajectory/extract.ts +196 -0
- package/dist/src/lib/trajectory/format-detect.ts +96 -0
- package/dist/src/lib/trajectory/frame-reader.ts +456 -0
- package/dist/src/lib/trajectory/helpers.ts +217 -0
- package/dist/src/lib/trajectory/index.ts +218 -0
- package/dist/src/lib/trajectory/parse/ase.ts +109 -0
- package/dist/src/lib/trajectory/parse/hdf5.ts +173 -0
- package/dist/src/lib/trajectory/parse/index.ts +411 -0
- package/dist/src/lib/trajectory/parse/lammps.ts +215 -0
- package/dist/src/lib/trajectory/parse/vasp.ts +102 -0
- package/dist/src/lib/trajectory/parse/xyz.ts +143 -0
- package/dist/src/lib/trajectory/plotting.ts +599 -0
- package/dist/src/lib/trajectory/types.ts +13 -0
- package/dist/src/lib/utils.ts +56 -0
- package/dist/src/lib/xrd/XrdPlot.svelte +615 -0
- package/dist/src/lib/xrd/broadening.ts +130 -0
- package/dist/src/lib/xrd/calc-xrd.ts +397 -0
- package/dist/src/lib/xrd/index.ts +38 -0
- package/dist/src/lib/xrd/parse.ts +858 -0
- package/dist/webview.js +29421 -0
- package/icon.png +0 -0
- package/license +1 -1
- package/matterviz-0.3.2.vsix +0 -0
- package/matterviz-0.3.4.vsix +0 -0
- package/matterviz-0.3.5.vsix +0 -0
- package/package.json +1461 -231
- package/readme.md +171 -98
- package/scripts/sync-config.ts +101 -0
- package/src/declarations.d.ts +2 -0
- package/src/extension.ts +972 -0
- package/src/node-io.ts +65 -0
- package/src/types.ts +17 -0
- package/src/webview/JsonBrowser.svelte +1079 -0
- package/src/webview/PlotPanel.svelte +346 -0
- package/src/webview/detect.ts +444 -0
- package/src/webview/main.ts +764 -0
- package/src/webview/plot-utils.ts +250 -0
- package/test-fixtures/all-viz-types.json.gz +0 -0
- package/test-fixtures/plot-demo-data.json.gz +0 -0
- package/tests/detect.test.ts +604 -0
- package/tests/extension.test.ts +2041 -0
- package/tests/node-io.test.ts +39 -0
- package/tests/plot-utils.test.ts +302 -0
- package/tests/vite-plugin-json-gz.test.ts +114 -0
- package/tests/vscode-mock.ts +18 -0
- package/tests/webview.test.ts +231 -0
- package/tsconfig.json +20 -0
- package/vite-plugin-json-gz.ts +29 -0
- package/vite.config.ts +34 -0
- package/vite.extension.config.ts +34 -0
- package/dist/EmptyState.svelte.d.ts +0 -9
- package/dist/FilePicker.svelte +0 -360
- package/dist/FilePicker.svelte.d.ts +0 -17
- package/dist/Icon.svelte.d.ts +0 -13
- package/dist/MillerIndexInput.svelte +0 -66
- package/dist/MillerIndexInput.svelte.d.ts +0 -7
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/api/optimade.d.ts +0 -45
- package/dist/api/optimade.js +0 -135
- package/dist/app.css +0 -240
- package/dist/brillouin/BrillouinZone.svelte +0 -543
- package/dist/brillouin/BrillouinZone.svelte.d.ts +0 -83
- package/dist/brillouin/BrillouinZoneControls.svelte +0 -144
- package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +0 -17
- package/dist/brillouin/BrillouinZoneExportPane.svelte +0 -148
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +0 -15
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +0 -146
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +0 -13
- package/dist/brillouin/BrillouinZoneScene.svelte +0 -476
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +0 -48
- package/dist/brillouin/BrillouinZoneTooltip.svelte +0 -92
- package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +0 -8
- package/dist/brillouin/compute.d.ts +0 -17
- package/dist/brillouin/compute.js +0 -422
- package/dist/brillouin/index.d.ts +0 -8
- package/dist/brillouin/index.js +0 -8
- package/dist/brillouin/types.d.ts +0 -48
- package/dist/brillouin/types.js +0 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +0 -327
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +0 -13
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +0 -847
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +0 -16
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +0 -3194
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +0 -16
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +0 -7
- package/dist/chempot-diagram/async-compute.svelte.d.ts +0 -3
- package/dist/chempot-diagram/async-compute.svelte.js +0 -77
- package/dist/chempot-diagram/chempot-worker.d.ts +0 -1
- package/dist/chempot-diagram/chempot-worker.js +0 -11
- package/dist/chempot-diagram/color.d.ts +0 -10
- package/dist/chempot-diagram/color.js +0 -32
- package/dist/chempot-diagram/compute.d.ts +0 -48
- package/dist/chempot-diagram/compute.js +0 -812
- package/dist/chempot-diagram/index.d.ts +0 -6
- package/dist/chempot-diagram/index.js +0 -6
- package/dist/chempot-diagram/pointer.d.ts +0 -16
- package/dist/chempot-diagram/pointer.js +0 -40
- package/dist/chempot-diagram/temperature.d.ts +0 -15
- package/dist/chempot-diagram/temperature.js +0 -36
- package/dist/chempot-diagram/types.d.ts +0 -86
- package/dist/chempot-diagram/types.js +0 -28
- package/dist/colors/index.d.ts +0 -47
- package/dist/colors/index.js +0 -203
- package/dist/composition/BarChart.svelte +0 -297
- package/dist/composition/BarChart.svelte.d.ts +0 -39
- package/dist/composition/BubbleChart.svelte +0 -218
- package/dist/composition/BubbleChart.svelte.d.ts +0 -28
- package/dist/composition/Composition.svelte +0 -164
- package/dist/composition/Composition.svelte.d.ts +0 -15
- package/dist/composition/Formula.svelte +0 -265
- package/dist/composition/Formula.svelte.d.ts +0 -19
- package/dist/composition/FormulaFilter.svelte +0 -1259
- package/dist/composition/FormulaFilter.svelte.d.ts +0 -51
- package/dist/composition/PieChart.svelte +0 -323
- package/dist/composition/PieChart.svelte.d.ts +0 -37
- package/dist/composition/format.d.ts +0 -15
- package/dist/composition/format.js +0 -109
- package/dist/composition/index.d.ts +0 -20
- package/dist/composition/index.js +0 -14
- package/dist/composition/parse.d.ts +0 -55
- package/dist/composition/parse.js +0 -459
- package/dist/constants.d.ts +0 -29
- package/dist/constants.js +0 -105
- package/dist/controls.d.ts +0 -14
- package/dist/controls.js +0 -30
- package/dist/convex-hull/ConvexHull.svelte +0 -157
- package/dist/convex-hull/ConvexHull.svelte.d.ts +0 -13
- package/dist/convex-hull/ConvexHull2D.svelte +0 -813
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +0 -11
- package/dist/convex-hull/ConvexHull3D.svelte +0 -1788
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +0 -8
- package/dist/convex-hull/ConvexHull4D.svelte +0 -1374
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +0 -8
- package/dist/convex-hull/ConvexHullControls.svelte +0 -546
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +0 -48
- package/dist/convex-hull/ConvexHullInfoPane.svelte +0 -115
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +0 -18
- package/dist/convex-hull/ConvexHullStats.svelte +0 -905
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +0 -15
- package/dist/convex-hull/ConvexHullTooltip.svelte +0 -131
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +0 -33
- package/dist/convex-hull/GasPressureControls.svelte +0 -247
- package/dist/convex-hull/GasPressureControls.svelte.d.ts +0 -11
- package/dist/convex-hull/StructurePopup.svelte +0 -116
- package/dist/convex-hull/StructurePopup.svelte.d.ts +0 -18
- package/dist/convex-hull/TemperatureSlider.svelte +0 -137
- package/dist/convex-hull/TemperatureSlider.svelte.d.ts +0 -8
- package/dist/convex-hull/barycentric-coords.d.ts +0 -18
- package/dist/convex-hull/barycentric-coords.js +0 -182
- package/dist/convex-hull/demo-temperature.d.ts +0 -6
- package/dist/convex-hull/demo-temperature.js +0 -40
- package/dist/convex-hull/gas-thermodynamics.d.ts +0 -16
- package/dist/convex-hull/gas-thermodynamics.js +0 -316
- package/dist/convex-hull/helpers.d.ts +0 -103
- package/dist/convex-hull/helpers.js +0 -671
- package/dist/convex-hull/index.d.ts +0 -118
- package/dist/convex-hull/index.js +0 -57
- package/dist/convex-hull/thermodynamics.d.ts +0 -66
- package/dist/convex-hull/thermodynamics.js +0 -1752
- package/dist/convex-hull/types.d.ts +0 -162
- package/dist/convex-hull/types.js +0 -36
- package/dist/coordination/CoordinationBarPlot.svelte +0 -311
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +0 -30
- package/dist/coordination/calc-coordination.d.ts +0 -15
- package/dist/coordination/calc-coordination.js +0 -63
- package/dist/coordination/index.d.ts +0 -8
- package/dist/coordination/index.js +0 -7
- package/dist/element/BohrAtom.svelte +0 -149
- package/dist/element/BohrAtom.svelte.d.ts +0 -20
- package/dist/element/ElementHeading.svelte +0 -26
- package/dist/element/ElementHeading.svelte.d.ts +0 -8
- package/dist/element/ElementPhoto.svelte +0 -57
- package/dist/element/ElementPhoto.svelte.d.ts +0 -9
- package/dist/element/ElementStats.svelte +0 -80
- package/dist/element/ElementStats.svelte.d.ts +0 -8
- package/dist/element/ElementTile.svelte +0 -484
- package/dist/element/ElementTile.svelte.d.ts +0 -29
- package/dist/element/Nucleus.svelte.d.ts +0 -17
- package/dist/element/data.d.ts +0 -3
- package/dist/element/data.js +0 -2
- package/dist/element/data.json.gz.d.ts +0 -2
- package/dist/element/index.d.ts +0 -8
- package/dist/element/index.js +0 -8
- package/dist/element/types.d.ts +0 -57
- package/dist/element/types.js +0 -1
- package/dist/feedback/ClickFeedback.svelte +0 -58
- package/dist/feedback/ClickFeedback.svelte.d.ts +0 -12
- package/dist/feedback/DragOverlay.svelte +0 -42
- package/dist/feedback/DragOverlay.svelte.d.ts +0 -7
- package/dist/feedback/Spinner.svelte.d.ts +0 -7
- package/dist/feedback/StatusMessage.svelte.d.ts +0 -9
- package/dist/feedback/index.d.ts +0 -4
- package/dist/feedback/index.js +0 -4
- package/dist/fermi-surface/FermiSlice.svelte +0 -189
- package/dist/fermi-surface/FermiSlice.svelte.d.ts +0 -24
- package/dist/fermi-surface/FermiSurface.svelte +0 -597
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +0 -83
- package/dist/fermi-surface/FermiSurfaceControls.svelte +0 -452
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +0 -35
- package/dist/fermi-surface/FermiSurfaceScene.svelte +0 -792
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +0 -50
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +0 -8
- package/dist/fermi-surface/compute.d.ts +0 -5
- package/dist/fermi-surface/compute.js +0 -538
- package/dist/fermi-surface/constants.d.ts +0 -9
- package/dist/fermi-surface/constants.js +0 -27
- package/dist/fermi-surface/export.d.ts +0 -5
- package/dist/fermi-surface/export.js +0 -63
- package/dist/fermi-surface/index.d.ts +0 -12
- package/dist/fermi-surface/index.js +0 -13
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/fermi-surface/parse.d.ts +0 -2
- package/dist/fermi-surface/parse.js +0 -495
- package/dist/fermi-surface/symmetry.d.ts +0 -3
- package/dist/fermi-surface/symmetry.js +0 -46
- package/dist/fermi-surface/types.d.ts +0 -113
- package/dist/fermi-surface/types.js +0 -4
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +0 -1527
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +0 -110
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +0 -30
- package/dist/heatmap-matrix/index.d.ts +0 -53
- package/dist/heatmap-matrix/index.js +0 -100
- package/dist/heatmap-matrix/shared.d.ts +0 -2
- package/dist/heatmap-matrix/shared.js +0 -4
- package/dist/icons.d.ts +0 -569
- package/dist/icons.js +0 -648
- package/dist/index.d.ts +0 -39
- package/dist/index.js +0 -39
- package/dist/io/decompress.d.ts +0 -10
- package/dist/io/decompress.js +0 -69
- package/dist/io/export.d.ts +0 -16
- package/dist/io/export.js +0 -312
- package/dist/io/fetch.d.ts +0 -5
- package/dist/io/fetch.js +0 -39
- package/dist/io/file-drop.d.ts +0 -7
- package/dist/io/file-drop.js +0 -43
- package/dist/io/index.d.ts +0 -7
- package/dist/io/index.js +0 -7
- package/dist/io/is-binary.d.ts +0 -1
- package/dist/io/is-binary.js +0 -5
- package/dist/io/types.d.ts +0 -8
- package/dist/io/types.js +0 -1
- package/dist/io/url-drop.d.ts +0 -2
- package/dist/io/url-drop.js +0 -117
- package/dist/isosurface/Isosurface.svelte +0 -285
- package/dist/isosurface/Isosurface.svelte.d.ts +0 -8
- package/dist/isosurface/IsosurfaceControls.svelte +0 -291
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +0 -9
- package/dist/isosurface/index.d.ts +0 -5
- package/dist/isosurface/index.js +0 -6
- package/dist/isosurface/parse.d.ts +0 -6
- package/dist/isosurface/parse.js +0 -553
- package/dist/isosurface/slice.d.ts +0 -11
- package/dist/isosurface/slice.js +0 -140
- package/dist/isosurface/types.d.ts +0 -56
- package/dist/isosurface/types.js +0 -227
- package/dist/labels.d.ts +0 -53
- package/dist/labels.js +0 -274
- package/dist/layout/FullscreenToggle.svelte +0 -50
- package/dist/layout/FullscreenToggle.svelte.d.ts +0 -7
- package/dist/layout/InfoCard.svelte +0 -120
- package/dist/layout/InfoCard.svelte.d.ts +0 -21
- package/dist/layout/InfoTag.svelte +0 -183
- package/dist/layout/InfoTag.svelte.d.ts +0 -19
- package/dist/layout/PropertyFilter.svelte +0 -244
- package/dist/layout/PropertyFilter.svelte.d.ts +0 -24
- package/dist/layout/SettingsSection.svelte +0 -148
- package/dist/layout/SettingsSection.svelte.d.ts +0 -17
- package/dist/layout/SubpageGrid.svelte +0 -82
- package/dist/layout/SubpageGrid.svelte.d.ts +0 -14
- package/dist/layout/fullscreen.d.ts +0 -9
- package/dist/layout/fullscreen.js +0 -53
- package/dist/layout/index.d.ts +0 -10
- package/dist/layout/index.js +0 -8
- package/dist/layout/json-tree/JsonNode.svelte +0 -547
- package/dist/layout/json-tree/JsonNode.svelte.d.ts +0 -11
- package/dist/layout/json-tree/JsonTree.svelte +0 -1222
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +0 -6
- package/dist/layout/json-tree/JsonValue.svelte +0 -334
- package/dist/layout/json-tree/JsonValue.svelte.d.ts +0 -9
- package/dist/layout/json-tree/index.d.ts +0 -3
- package/dist/layout/json-tree/index.js +0 -3
- package/dist/layout/json-tree/types.d.ts +0 -73
- package/dist/layout/json-tree/types.js +0 -3
- package/dist/layout/json-tree/utils.d.ts +0 -29
- package/dist/layout/json-tree/utils.js +0 -648
- package/dist/marching-cubes.d.ts +0 -14
- package/dist/marching-cubes.js +0 -542
- package/dist/math.d.ts +0 -91
- package/dist/math.js +0 -896
- package/dist/overlays/ContextMenu.svelte +0 -162
- package/dist/overlays/ContextMenu.svelte.d.ts +0 -25
- package/dist/overlays/DraggablePane.svelte +0 -564
- package/dist/overlays/DraggablePane.svelte.d.ts +0 -36
- package/dist/overlays/index.d.ts +0 -2
- package/dist/overlays/index.js +0 -2
- package/dist/periodic-table/PeriodicTable.svelte +0 -469
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +0 -55
- package/dist/periodic-table/PeriodicTableControls.svelte +0 -557
- package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +0 -24
- package/dist/periodic-table/PropertySelect.svelte +0 -37
- package/dist/periodic-table/PropertySelect.svelte.d.ts +0 -13
- package/dist/periodic-table/TableInset.svelte.d.ts +0 -9
- package/dist/periodic-table/index.d.ts +0 -10
- package/dist/periodic-table/index.js +0 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +0 -44
- package/dist/phase-diagram/PhaseDiagramControls.svelte +0 -451
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +0 -30
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +0 -15
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +0 -192
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +0 -19
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +0 -392
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +0 -16
- package/dist/phase-diagram/TdbInfoPanel.svelte +0 -203
- package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +0 -12
- package/dist/phase-diagram/build-diagram.d.ts +0 -11
- package/dist/phase-diagram/build-diagram.js +0 -167
- package/dist/phase-diagram/colors.d.ts +0 -35
- package/dist/phase-diagram/colors.js +0 -51
- package/dist/phase-diagram/diagram-input.d.ts +0 -33
- package/dist/phase-diagram/diagram-input.js +0 -3
- package/dist/phase-diagram/index.d.ts +0 -13
- package/dist/phase-diagram/index.js +0 -13
- package/dist/phase-diagram/parse.d.ts +0 -55
- package/dist/phase-diagram/parse.js +0 -276
- package/dist/phase-diagram/svg-to-diagram.d.ts +0 -2
- package/dist/phase-diagram/svg-to-diagram.js +0 -869
- package/dist/phase-diagram/types.d.ts +0 -99
- package/dist/phase-diagram/types.js +0 -1
- package/dist/phase-diagram/utils.d.ts +0 -118
- package/dist/phase-diagram/utils.js +0 -606
- package/dist/plot/AxisLabel.svelte +0 -51
- package/dist/plot/AxisLabel.svelte.d.ts +0 -16
- package/dist/plot/BarPlot.svelte +0 -2256
- package/dist/plot/BarPlot.svelte.d.ts +0 -82
- package/dist/plot/BarPlotControls.svelte +0 -66
- package/dist/plot/BarPlotControls.svelte.d.ts +0 -18
- package/dist/plot/ColorBar.svelte +0 -719
- package/dist/plot/ColorBar.svelte.d.ts +0 -31
- package/dist/plot/ColorScaleSelect.svelte +0 -54
- package/dist/plot/ColorScaleSelect.svelte.d.ts +0 -15
- package/dist/plot/ElementScatter.svelte +0 -63
- package/dist/plot/ElementScatter.svelte.d.ts +0 -14
- package/dist/plot/FillArea.svelte +0 -226
- package/dist/plot/FillArea.svelte.d.ts +0 -20
- package/dist/plot/Histogram.svelte +0 -1654
- package/dist/plot/Histogram.svelte.d.ts +0 -50
- package/dist/plot/HistogramControls.svelte +0 -212
- package/dist/plot/HistogramControls.svelte.d.ts +0 -22
- package/dist/plot/InteractiveAxisLabel.svelte +0 -94
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +0 -14
- package/dist/plot/Line.svelte +0 -85
- package/dist/plot/Line.svelte.d.ts +0 -15
- package/dist/plot/PlotControls.svelte +0 -537
- package/dist/plot/PlotControls.svelte.d.ts +0 -4
- package/dist/plot/PlotLegend.svelte +0 -498
- package/dist/plot/PlotLegend.svelte.d.ts +0 -25
- package/dist/plot/PlotTooltip.svelte +0 -67
- package/dist/plot/PlotTooltip.svelte.d.ts +0 -17
- package/dist/plot/PortalSelect.svelte +0 -253
- package/dist/plot/PortalSelect.svelte.d.ts +0 -16
- package/dist/plot/ReferenceLine.svelte.d.ts +0 -20
- package/dist/plot/ReferenceLine3D.svelte +0 -154
- package/dist/plot/ReferenceLine3D.svelte.d.ts +0 -14
- package/dist/plot/ReferencePlane.svelte +0 -178
- package/dist/plot/ReferencePlane.svelte.d.ts +0 -14
- package/dist/plot/ScatterPlot.svelte +0 -2831
- package/dist/plot/ScatterPlot.svelte.d.ts +0 -92
- package/dist/plot/ScatterPlot3D.svelte +0 -499
- package/dist/plot/ScatterPlot3D.svelte.d.ts +0 -94
- package/dist/plot/ScatterPlot3DControls.svelte +0 -437
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +0 -20
- package/dist/plot/ScatterPlot3DScene.svelte +0 -912
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +0 -74
- package/dist/plot/ScatterPlotControls.svelte +0 -307
- package/dist/plot/ScatterPlotControls.svelte.d.ts +0 -17
- package/dist/plot/ScatterPoint.svelte +0 -185
- package/dist/plot/ScatterPoint.svelte.d.ts +0 -19
- package/dist/plot/SpacegroupBarPlot.svelte +0 -292
- package/dist/plot/SpacegroupBarPlot.svelte.d.ts +0 -9
- package/dist/plot/Surface3D.svelte +0 -200
- package/dist/plot/Surface3D.svelte.d.ts +0 -13
- package/dist/plot/ZeroLines.svelte +0 -96
- package/dist/plot/ZeroLines.svelte.d.ts +0 -32
- package/dist/plot/ZoomRect.svelte +0 -23
- package/dist/plot/ZoomRect.svelte.d.ts +0 -8
- package/dist/plot/axis-utils.d.ts +0 -19
- package/dist/plot/axis-utils.js +0 -80
- package/dist/plot/data-cleaning.d.ts +0 -37
- package/dist/plot/data-cleaning.js +0 -855
- package/dist/plot/data-transform.d.ts +0 -16
- package/dist/plot/data-transform.js +0 -45
- package/dist/plot/defaults.d.ts +0 -19
- package/dist/plot/defaults.js +0 -9
- package/dist/plot/fill-utils.d.ts +0 -51
- package/dist/plot/fill-utils.js +0 -337
- package/dist/plot/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/hover-lock.svelte.js +0 -46
- package/dist/plot/index.d.ts +0 -43
- package/dist/plot/index.js +0 -37
- package/dist/plot/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -100
- package/dist/plot/layout.d.ts +0 -60
- package/dist/plot/layout.js +0 -230
- package/dist/plot/reference-line.d.ts +0 -60
- package/dist/plot/reference-line.js +0 -316
- package/dist/plot/scales.d.ts +0 -48
- package/dist/plot/scales.js +0 -484
- package/dist/plot/svg.d.ts +0 -1
- package/dist/plot/svg.js +0 -11
- package/dist/plot/types.d.ts +0 -863
- package/dist/plot/types.js +0 -103
- package/dist/plot/utils/label-placement.d.ts +0 -47
- package/dist/plot/utils/label-placement.js +0 -256
- package/dist/plot/utils/series-visibility.d.ts +0 -9
- package/dist/plot/utils/series-visibility.js +0 -67
- package/dist/plot/utils.d.ts +0 -1
- package/dist/plot/utils.js +0 -14
- package/dist/rdf/RdfPlot.svelte +0 -247
- package/dist/rdf/RdfPlot.svelte.d.ts +0 -27
- package/dist/rdf/calc-rdf.d.ts +0 -4
- package/dist/rdf/calc-rdf.js +0 -111
- package/dist/rdf/index.d.ts +0 -23
- package/dist/rdf/index.js +0 -2
- package/dist/sanitize.d.ts +0 -4
- package/dist/sanitize.js +0 -107
- package/dist/settings.d.ts +0 -253
- package/dist/settings.js +0 -1123
- package/dist/spectral/Bands.svelte +0 -1040
- package/dist/spectral/Bands.svelte.d.ts +0 -40
- package/dist/spectral/BandsAndDos.svelte +0 -128
- package/dist/spectral/BandsAndDos.svelte.d.ts +0 -18
- package/dist/spectral/BrillouinBandsDos.svelte +0 -248
- package/dist/spectral/BrillouinBandsDos.svelte.d.ts +0 -20
- package/dist/spectral/Dos.svelte +0 -697
- package/dist/spectral/Dos.svelte.d.ts +0 -29
- package/dist/spectral/helpers.d.ts +0 -117
- package/dist/spectral/helpers.js +0 -1023
- package/dist/spectral/index.d.ts +0 -6
- package/dist/spectral/index.js +0 -7
- package/dist/spectral/types.d.ts +0 -84
- package/dist/spectral/types.js +0 -2
- package/dist/state.svelte.d.ts +0 -25
- package/dist/state.svelte.js +0 -45
- package/dist/structure/Arrow.svelte +0 -72
- package/dist/structure/Arrow.svelte.d.ts +0 -15
- package/dist/structure/AtomLegend.svelte +0 -798
- package/dist/structure/AtomLegend.svelte.d.ts +0 -34
- package/dist/structure/Bond.svelte +0 -140
- package/dist/structure/Bond.svelte.d.ts +0 -9
- package/dist/structure/CanvasTooltip.svelte +0 -33
- package/dist/structure/CanvasTooltip.svelte.d.ts +0 -12
- package/dist/structure/CellSelect.svelte +0 -351
- package/dist/structure/CellSelect.svelte.d.ts +0 -13
- package/dist/structure/Cylinder.svelte +0 -45
- package/dist/structure/Cylinder.svelte.d.ts +0 -10
- package/dist/structure/Lattice.svelte +0 -196
- package/dist/structure/Lattice.svelte.d.ts +0 -17
- package/dist/structure/Structure.svelte +0 -1857
- package/dist/structure/Structure.svelte.d.ts +0 -83
- package/dist/structure/StructureControls.svelte +0 -1184
- package/dist/structure/StructureControls.svelte.d.ts +0 -31
- package/dist/structure/StructureExportPane.svelte +0 -251
- package/dist/structure/StructureExportPane.svelte.d.ts +0 -17
- package/dist/structure/StructureInfoPane.svelte +0 -434
- package/dist/structure/StructureInfoPane.svelte.d.ts +0 -18
- package/dist/structure/StructureScene.svelte +0 -1574
- package/dist/structure/StructureScene.svelte.d.ts +0 -104
- package/dist/structure/atom-properties.d.ts +0 -37
- package/dist/structure/atom-properties.js +0 -198
- package/dist/structure/bonding.d.ts +0 -33
- package/dist/structure/bonding.js +0 -304
- package/dist/structure/export.d.ts +0 -20
- package/dist/structure/export.js +0 -725
- package/dist/structure/ferrox-wasm-types.d.ts +0 -46
- package/dist/structure/ferrox-wasm-types.js +0 -18
- package/dist/structure/ferrox-wasm.d.ts +0 -94
- package/dist/structure/ferrox-wasm.js +0 -249
- package/dist/structure/index.d.ts +0 -110
- package/dist/structure/index.js +0 -168
- package/dist/structure/measure.d.ts +0 -6
- package/dist/structure/measure.js +0 -29
- package/dist/structure/parse.d.ts +0 -65
- package/dist/structure/parse.js +0 -1374
- package/dist/structure/partial-occupancy.d.ts +0 -25
- package/dist/structure/partial-occupancy.js +0 -99
- package/dist/structure/pbc.d.ts +0 -9
- package/dist/structure/pbc.js +0 -123
- package/dist/structure/supercell.d.ts +0 -8
- package/dist/structure/supercell.js +0 -137
- package/dist/structure/validation.d.ts +0 -2
- package/dist/structure/validation.js +0 -10
- package/dist/symmetry/SymmetryStats.svelte +0 -226
- package/dist/symmetry/SymmetryStats.svelte.d.ts +0 -21
- package/dist/symmetry/WyckoffTable.svelte +0 -113
- package/dist/symmetry/WyckoffTable.svelte.d.ts +0 -11
- package/dist/symmetry/cell-transform.d.ts +0 -12
- package/dist/symmetry/cell-transform.js +0 -77
- package/dist/symmetry/index.d.ts +0 -43
- package/dist/symmetry/index.js +0 -229
- package/dist/symmetry/spacegroups.d.ts +0 -9
- package/dist/symmetry/spacegroups.js +0 -394
- package/dist/table/HeatmapTable.svelte +0 -1854
- package/dist/table/HeatmapTable.svelte.d.ts +0 -49
- package/dist/table/ToggleMenu.svelte +0 -376
- package/dist/table/ToggleMenu.svelte.d.ts +0 -11
- package/dist/table/index.d.ts +0 -74
- package/dist/table/index.js +0 -38
- package/dist/theme/ThemeControl.svelte +0 -53
- package/dist/theme/ThemeControl.svelte.d.ts +0 -9
- package/dist/theme/index.d.ts +0 -29
- package/dist/theme/index.js +0 -79
- package/dist/theme/themes.mjs +0 -285
- package/dist/time.d.ts +0 -4
- package/dist/time.js +0 -70
- package/dist/tooltip/TooltipContent.svelte +0 -58
- package/dist/tooltip/TooltipContent.svelte.d.ts +0 -31
- package/dist/tooltip/index.d.ts +0 -2
- package/dist/tooltip/index.js +0 -2
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/tooltip/types.js +0 -1
- package/dist/trajectory/Trajectory.svelte +0 -1517
- package/dist/trajectory/Trajectory.svelte.d.ts +0 -77
- package/dist/trajectory/TrajectoryError.svelte +0 -128
- package/dist/trajectory/TrajectoryError.svelte.d.ts +0 -13
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -357
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +0 -17
- package/dist/trajectory/TrajectoryInfoPane.svelte +0 -387
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +0 -17
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -7
- package/dist/trajectory/extract.d.ts +0 -5
- package/dist/trajectory/extract.js +0 -162
- package/dist/trajectory/format-detect.d.ts +0 -9
- package/dist/trajectory/format-detect.js +0 -76
- package/dist/trajectory/frame-reader.d.ts +0 -17
- package/dist/trajectory/frame-reader.js +0 -332
- package/dist/trajectory/helpers.d.ts +0 -14
- package/dist/trajectory/helpers.js +0 -172
- package/dist/trajectory/index.d.ts +0 -63
- package/dist/trajectory/index.js +0 -126
- package/dist/trajectory/parse/ase.d.ts +0 -2
- package/dist/trajectory/parse/ase.js +0 -77
- package/dist/trajectory/parse/hdf5.d.ts +0 -2
- package/dist/trajectory/parse/hdf5.js +0 -129
- package/dist/trajectory/parse/index.d.ts +0 -12
- package/dist/trajectory/parse/index.js +0 -299
- package/dist/trajectory/parse/lammps.d.ts +0 -5
- package/dist/trajectory/parse/lammps.js +0 -179
- package/dist/trajectory/parse/vasp.d.ts +0 -2
- package/dist/trajectory/parse/vasp.js +0 -68
- package/dist/trajectory/parse/xyz.d.ts +0 -2
- package/dist/trajectory/parse/xyz.js +0 -110
- package/dist/trajectory/plotting.d.ts +0 -28
- package/dist/trajectory/plotting.js +0 -423
- package/dist/trajectory/types.d.ts +0 -11
- package/dist/trajectory/types.js +0 -1
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -36
- package/dist/xrd/XrdPlot.svelte +0 -615
- package/dist/xrd/XrdPlot.svelte.d.ts +0 -28
- package/dist/xrd/broadening.d.ts +0 -20
- package/dist/xrd/broadening.js +0 -97
- package/dist/xrd/calc-xrd.d.ts +0 -37
- package/dist/xrd/calc-xrd.js +0 -337
- package/dist/xrd/index.d.ts +0 -37
- package/dist/xrd/index.js +0 -4
- package/dist/xrd/parse.d.ts +0 -13
- package/dist/xrd/parse.js +0 -749
- /package/dist/{EmptyState.svelte → src/lib/EmptyState.svelte} +0 -0
- /package/dist/{Icon.svelte → src/lib/Icon.svelte} +0 -0
- /package/dist/{chempot-diagram → src/lib/chempot-diagram}/ChemPotScene3D.svelte +0 -0
- /package/dist/{colors → src/lib/colors}/alloy-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/dark-mode-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/jmol-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/muted-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/pastel-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/vesta-colors.json +0 -0
- /package/dist/{element → src/lib/element}/Nucleus.svelte +0 -0
- /package/dist/{element → src/lib/element}/data.json +0 -0
- /package/dist/{element → src/lib/element}/data.json.gz +0 -0
- /package/dist/{element → src/lib/element}/data.schema.json +0 -0
- /package/dist/{element-image-urls.json → src/lib/element-image-urls.json} +0 -0
- /package/dist/{feedback → src/lib/feedback}/Spinner.svelte +0 -0
- /package/dist/{feedback → src/lib/feedback}/StatusMessage.svelte +0 -0
- /package/dist/{periodic-table → src/lib/periodic-table}/TableInset.svelte +0 -0
- /package/dist/{plot → src/lib/plot}/ReferenceLine.svelte +0 -0
- /package/dist/{xrd → src/lib/xrd}/atomic_scattering_params.json +0 -0
|
@@ -0,0 +1,2113 @@
|
|
|
1
|
+
<script
|
|
2
|
+
lang="ts"
|
|
3
|
+
generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
|
|
4
|
+
>
|
|
5
|
+
import type { D3ColorSchemeName, D3InterpolateName } from '$lib/colors'
|
|
6
|
+
import { format_value } from '$lib/labels'
|
|
7
|
+
import { sanitize_html } from '$lib/sanitize'
|
|
8
|
+
import { FullscreenToggle, set_fullscreen_bg } from '$lib/layout'
|
|
9
|
+
import type { Point2D, Vec2 } from '$lib/math'
|
|
10
|
+
import type {
|
|
11
|
+
AxisLoadError,
|
|
12
|
+
BarHandlerProps,
|
|
13
|
+
BarMode,
|
|
14
|
+
BarSeries,
|
|
15
|
+
BarStyle,
|
|
16
|
+
BasePlotProps,
|
|
17
|
+
DataLoaderFn,
|
|
18
|
+
InitialRanges,
|
|
19
|
+
InternalPoint,
|
|
20
|
+
LegendConfig,
|
|
21
|
+
LegendItem,
|
|
22
|
+
LineStyle,
|
|
23
|
+
Orientation,
|
|
24
|
+
PanConfig,
|
|
25
|
+
PlotConfig,
|
|
26
|
+
RefLine,
|
|
27
|
+
RefLineEvent,
|
|
28
|
+
ScaleType,
|
|
29
|
+
UserContentProps,
|
|
30
|
+
} from '$lib/plot'
|
|
31
|
+
import {
|
|
32
|
+
BarPlotControls,
|
|
33
|
+
compute_element_placement,
|
|
34
|
+
PlotAxis,
|
|
35
|
+
PlotLegend,
|
|
36
|
+
ReferenceLine,
|
|
37
|
+
ScatterPoint,
|
|
38
|
+
} from '$lib/plot'
|
|
39
|
+
import type { AxisChangeState } from '$lib/plot/axis-utils'
|
|
40
|
+
import { create_axis_change_handler } from '$lib/plot/axis-utils'
|
|
41
|
+
import { process_prop } from '$lib/plot/data-transform'
|
|
42
|
+
import {
|
|
43
|
+
create_dimension_tracker,
|
|
44
|
+
create_hover_lock,
|
|
45
|
+
} from '$lib/plot/hover-lock.svelte'
|
|
46
|
+
import {
|
|
47
|
+
get_relative_coords,
|
|
48
|
+
pan_range,
|
|
49
|
+
PINCH_ZOOM_THRESHOLD,
|
|
50
|
+
pixels_to_data_delta,
|
|
51
|
+
} from '$lib/plot/interactions'
|
|
52
|
+
import type { IndexedRefLine } from '$lib/plot/reference-line'
|
|
53
|
+
import { group_ref_lines_by_z, index_ref_lines } from '$lib/plot/reference-line'
|
|
54
|
+
import {
|
|
55
|
+
create_color_scale,
|
|
56
|
+
create_scale,
|
|
57
|
+
create_size_scale,
|
|
58
|
+
generate_ticks,
|
|
59
|
+
get_nice_data_range,
|
|
60
|
+
get_tick_label,
|
|
61
|
+
} from '$lib/plot/scales'
|
|
62
|
+
import { DEFAULT_MARKERS, get_scale_type_name } from '$lib/plot/types'
|
|
63
|
+
import { DEFAULTS } from '$lib/settings'
|
|
64
|
+
import { extent } from 'd3-array'
|
|
65
|
+
import type { Snippet } from 'svelte'
|
|
66
|
+
import { untrack } from 'svelte'
|
|
67
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
68
|
+
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
69
|
+
import { SvelteMap } from 'svelte/reactivity'
|
|
70
|
+
import {
|
|
71
|
+
build_obstacles_norm,
|
|
72
|
+
clip_bar,
|
|
73
|
+
has_explicit_position,
|
|
74
|
+
measured_footprint,
|
|
75
|
+
place_decorations,
|
|
76
|
+
} from './auto-place'
|
|
77
|
+
import {
|
|
78
|
+
calc_auto_padding,
|
|
79
|
+
constrain_tooltip_position,
|
|
80
|
+
filter_padding,
|
|
81
|
+
LABEL_GAP_DEFAULT,
|
|
82
|
+
measure_max_tick_width,
|
|
83
|
+
} from './layout'
|
|
84
|
+
import PlotTooltip from './PlotTooltip.svelte'
|
|
85
|
+
import { bar_path } from './svg'
|
|
86
|
+
import ZeroLines from './ZeroLines.svelte'
|
|
87
|
+
import ZoomRect from './ZoomRect.svelte'
|
|
88
|
+
|
|
89
|
+
// Handler props for line marker events (extends BarHandlerProps with point-specific data)
|
|
90
|
+
interface LineMarkerHandlerProps extends BarHandlerProps<Metadata> {
|
|
91
|
+
point: InternalPoint<Metadata>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Extended point type with computed screen coordinates (used internally for rendering)
|
|
95
|
+
type LineSeriesPoint = InternalPoint<Metadata> & {
|
|
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
|
+
}
|
|
102
|
+
|
|
103
|
+
let {
|
|
104
|
+
series = $bindable([]),
|
|
105
|
+
orientation = $bindable(`vertical`),
|
|
106
|
+
mode = $bindable(`overlay`),
|
|
107
|
+
x_axis = $bindable({}),
|
|
108
|
+
x2_axis = $bindable({}),
|
|
109
|
+
y_axis = $bindable({}),
|
|
110
|
+
y2_axis = $bindable({}),
|
|
111
|
+
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
|
+
range_padding = 0.05,
|
|
117
|
+
padding = { t: 20, b: 60, l: 60, r: 20 },
|
|
118
|
+
legend = {},
|
|
119
|
+
show_legend,
|
|
120
|
+
bar = {},
|
|
121
|
+
line = {},
|
|
122
|
+
tooltip,
|
|
123
|
+
user_content,
|
|
124
|
+
hovered = $bindable(false),
|
|
125
|
+
change = () => {},
|
|
126
|
+
on_bar_click,
|
|
127
|
+
on_bar_hover,
|
|
128
|
+
// Line marker props (matching ScatterPlot)
|
|
129
|
+
color_scale = {
|
|
130
|
+
type: `linear`,
|
|
131
|
+
scheme: `interpolateViridis`,
|
|
132
|
+
value_range: undefined,
|
|
133
|
+
},
|
|
134
|
+
size_scale = { type: `linear`, radius_range: [2, 10], value_range: undefined },
|
|
135
|
+
point_tween,
|
|
136
|
+
on_point_click,
|
|
137
|
+
on_point_hover,
|
|
138
|
+
ref_lines = $bindable([]),
|
|
139
|
+
on_ref_line_click,
|
|
140
|
+
on_ref_line_hover,
|
|
141
|
+
show_controls = $bindable(true),
|
|
142
|
+
controls_open = $bindable(false),
|
|
143
|
+
controls_toggle_props,
|
|
144
|
+
controls_pane_props,
|
|
145
|
+
fullscreen = $bindable(false),
|
|
146
|
+
fullscreen_toggle = true,
|
|
147
|
+
children,
|
|
148
|
+
header_controls,
|
|
149
|
+
controls_extra,
|
|
150
|
+
data_loader,
|
|
151
|
+
on_axis_change,
|
|
152
|
+
on_error,
|
|
153
|
+
pan = {},
|
|
154
|
+
...rest
|
|
155
|
+
}: HTMLAttributes<HTMLDivElement> & BasePlotProps & PlotConfig & {
|
|
156
|
+
series?: BarSeries<Metadata>[]
|
|
157
|
+
// Component-specific props
|
|
158
|
+
orientation?: Orientation
|
|
159
|
+
mode?: BarMode
|
|
160
|
+
legend?: LegendConfig | null
|
|
161
|
+
show_legend?: boolean
|
|
162
|
+
bar?: BarStyle
|
|
163
|
+
line?: LineStyle
|
|
164
|
+
tooltip?: Snippet<[BarHandlerProps<Metadata>]>
|
|
165
|
+
user_content?: Snippet<[UserContentProps]>
|
|
166
|
+
header_controls?: Snippet<
|
|
167
|
+
[{ height: number; width: number; fullscreen: boolean }]
|
|
168
|
+
>
|
|
169
|
+
controls_extra?: Snippet<
|
|
170
|
+
[{ orientation: Orientation; mode: BarMode } & Required<PlotConfig>]
|
|
171
|
+
>
|
|
172
|
+
change?: (data: BarHandlerProps<Metadata> | null) => void
|
|
173
|
+
on_bar_click?: (
|
|
174
|
+
data: BarHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
|
|
175
|
+
) => void
|
|
176
|
+
on_bar_hover?: (
|
|
177
|
+
data:
|
|
178
|
+
| (BarHandlerProps<Metadata> & {
|
|
179
|
+
event: MouseEvent | FocusEvent | KeyboardEvent
|
|
180
|
+
})
|
|
181
|
+
| null,
|
|
182
|
+
) => void
|
|
183
|
+
// Line marker props (matching ScatterPlot)
|
|
184
|
+
// Note: For line series with markers, BOTH on_bar_* AND on_point_* events fire.
|
|
185
|
+
// Use on_point_* for marker-specific data (includes `point` with InternalPoint details)
|
|
186
|
+
// or on_bar_* for backward compatibility with bar-style event handling.
|
|
187
|
+
color_scale?: {
|
|
188
|
+
type?: ScaleType
|
|
189
|
+
scheme?: D3ColorSchemeName | D3InterpolateName
|
|
190
|
+
value_range?: [number, number]
|
|
191
|
+
} | D3InterpolateName
|
|
192
|
+
size_scale?: {
|
|
193
|
+
type?: ScaleType
|
|
194
|
+
radius_range?: [number, number]
|
|
195
|
+
value_range?: [number, number]
|
|
196
|
+
}
|
|
197
|
+
point_tween?: TweenOptions<Point2D>
|
|
198
|
+
on_point_click?: (
|
|
199
|
+
data: LineMarkerHandlerProps & { event: MouseEvent | KeyboardEvent },
|
|
200
|
+
) => void
|
|
201
|
+
on_point_hover?: (
|
|
202
|
+
data:
|
|
203
|
+
| (LineMarkerHandlerProps & {
|
|
204
|
+
event: MouseEvent | FocusEvent | KeyboardEvent
|
|
205
|
+
})
|
|
206
|
+
| null,
|
|
207
|
+
) => void
|
|
208
|
+
ref_lines?: RefLine[]
|
|
209
|
+
on_ref_line_click?: (event: RefLineEvent) => void
|
|
210
|
+
on_ref_line_hover?: (event: RefLineEvent | null) => void
|
|
211
|
+
// Interactive axis props
|
|
212
|
+
data_loader?: DataLoaderFn<Metadata, BarSeries<Metadata>>
|
|
213
|
+
on_axis_change?: (
|
|
214
|
+
axis: `x` | `x2` | `y` | `y2`,
|
|
215
|
+
key: string,
|
|
216
|
+
new_series: BarSeries<Metadata>[],
|
|
217
|
+
) => void
|
|
218
|
+
on_error?: (error: AxisLoadError) => void
|
|
219
|
+
pan?: PanConfig
|
|
220
|
+
} = $props()
|
|
221
|
+
|
|
222
|
+
// Initialize bar, line, y2_axis with defaults - using $derived for reactivity
|
|
223
|
+
let bar_state = $derived({ ...DEFAULTS.bar.bar, ...bar })
|
|
224
|
+
let line_state = $derived({ ...DEFAULTS.bar.line, ...line })
|
|
225
|
+
y2_axis = {
|
|
226
|
+
format: ``,
|
|
227
|
+
scale_type: `linear`,
|
|
228
|
+
ticks: 5,
|
|
229
|
+
label_shift: { y: 60 },
|
|
230
|
+
tick: { label: { shift: { x: 0, y: 0 } } }, // base offset handled in rendering
|
|
231
|
+
range: [null, null],
|
|
232
|
+
...y2_axis,
|
|
233
|
+
}
|
|
234
|
+
x2_axis = {
|
|
235
|
+
format: ``,
|
|
236
|
+
scale_type: `linear`,
|
|
237
|
+
ticks: 5,
|
|
238
|
+
label_shift: { x: 0, y: 40 },
|
|
239
|
+
tick: { label: { shift: { x: 0, y: 0 } } },
|
|
240
|
+
range: [null, null],
|
|
241
|
+
...x2_axis,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let [width, height] = $state([0, 0])
|
|
245
|
+
let wrapper: HTMLDivElement | undefined = $state()
|
|
246
|
+
let svg_element: SVGElement | null = $state(null)
|
|
247
|
+
let clip_path_id = `chart-clip-${crypto?.randomUUID?.()}`
|
|
248
|
+
|
|
249
|
+
// Reference line hover state
|
|
250
|
+
let hovered_ref_line_idx = $state<number | null>(null)
|
|
251
|
+
|
|
252
|
+
// Interactive axis loading state
|
|
253
|
+
let axis_loading = $state<`x` | `x2` | `y` | `y2` | null>(null)
|
|
254
|
+
|
|
255
|
+
// Compute ref_lines with index and group by z-index (using shared utilities)
|
|
256
|
+
let indexed_ref_lines = $derived(index_ref_lines(ref_lines))
|
|
257
|
+
let ref_lines_by_z = $derived(group_ref_lines_by_z(indexed_ref_lines))
|
|
258
|
+
|
|
259
|
+
// === Categorical Normalization ===
|
|
260
|
+
// Internal type with guaranteed numeric x (for downstream scale/rendering code)
|
|
261
|
+
type NumericBarSeries = Omit<BarSeries<Metadata>, `x`> & { x: readonly number[] }
|
|
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
|
+
})
|
|
272
|
+
|
|
273
|
+
let category_indices = $derived(
|
|
274
|
+
category_list.length ? category_list.map((_, idx) => idx) : null,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
let internal_series = $derived.by<NumericBarSeries[]>(() => {
|
|
278
|
+
// safe: when !category_indices, all x values are numeric (is_categorical is false)
|
|
279
|
+
if (!category_indices) return series as unknown as NumericBarSeries[]
|
|
280
|
+
return series.map((srs) => {
|
|
281
|
+
const orig_map = new Map(srs.x.map((val, idx) => [String(val), idx]))
|
|
282
|
+
if (orig_map.size < srs.x.length) {
|
|
283
|
+
console.warn(
|
|
284
|
+
`BarPlot: series "${
|
|
285
|
+
srs.label ?? `?`
|
|
286
|
+
}" has duplicate x values — last occurrence wins`,
|
|
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
|
+
})
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
// Compute auto ranges from visible series
|
|
311
|
+
let visible_series = $derived(
|
|
312
|
+
internal_series.filter((srs) => srs?.visible ?? true),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
// Separate series by y-axis
|
|
316
|
+
let y1_series = $derived(
|
|
317
|
+
visible_series.filter((srs) => (srs.y_axis ?? `y1`) === `y1`),
|
|
318
|
+
)
|
|
319
|
+
let y2_series = $derived(
|
|
320
|
+
visible_series.filter((srs) => srs.y_axis === `y2`),
|
|
321
|
+
)
|
|
322
|
+
let x2_series = $derived(
|
|
323
|
+
visible_series.filter((srs) => srs.x_axis === `x2`),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
let auto_ranges = $derived.by(() => {
|
|
327
|
+
// Calculate separate ranges for y1 and y2 axes
|
|
328
|
+
const calc_y_range = (
|
|
329
|
+
series_list: typeof visible_series,
|
|
330
|
+
y_limit: typeof y_range,
|
|
331
|
+
scale_type: ScaleType,
|
|
332
|
+
) => {
|
|
333
|
+
let points = series_list.flatMap((srs) =>
|
|
334
|
+
srs.x.map((x_val, idx) => ({ x: x_val, y: srs.y[idx] }))
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
// In stacked mode, calculate stacked totals for accurate range (only for bars on the same axis)
|
|
338
|
+
if (mode === `stacked`) {
|
|
339
|
+
const stacked_totals = new SvelteMap<number, { pos: number; neg: number }>()
|
|
340
|
+
|
|
341
|
+
// Only include visible bar series (not lines) in stacking
|
|
342
|
+
series_list
|
|
343
|
+
.filter((srs) => srs.render_mode !== `line`)
|
|
344
|
+
.forEach((srs) =>
|
|
345
|
+
srs.x.forEach((x_val, idx) => {
|
|
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
|
+
})
|
|
444
|
+
|
|
445
|
+
// Initialize and current ranges
|
|
446
|
+
let ranges = $state<{
|
|
447
|
+
initial: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
|
|
448
|
+
current: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
|
|
449
|
+
}>({
|
|
450
|
+
initial: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
|
|
451
|
+
current: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
$effect(() => { // handle x_axis.range / x2_axis.range / y_axis.range / y2_axis.range changes
|
|
455
|
+
const new_x = [
|
|
456
|
+
x_axis.range?.[0] ?? auto_ranges.x[0],
|
|
457
|
+
x_axis.range?.[1] ?? auto_ranges.x[1],
|
|
458
|
+
] as Vec2
|
|
459
|
+
const new_x2 = [
|
|
460
|
+
x2_axis.range?.[0] ?? auto_ranges.x2[0],
|
|
461
|
+
x2_axis.range?.[1] ?? auto_ranges.x2[1],
|
|
462
|
+
] as Vec2
|
|
463
|
+
const new_y = [
|
|
464
|
+
y_axis.range?.[0] ?? auto_ranges.y[0],
|
|
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
|
+
}
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
// Layout: dynamic padding based on tick label widths
|
|
491
|
+
const default_padding = { t: 20, b: 60, l: 60, r: 20 }
|
|
492
|
+
// base_pad reserves space for tick labels/axis titles; pad (below) adds decoration reservations
|
|
493
|
+
let base_pad = $derived(filter_padding(padding, default_padding))
|
|
494
|
+
|
|
495
|
+
// Update padding when format or ticks change
|
|
496
|
+
$effect(() => {
|
|
497
|
+
const new_pad = width && height && ticks.y.length
|
|
498
|
+
? calc_auto_padding({
|
|
499
|
+
padding,
|
|
500
|
+
default_padding,
|
|
501
|
+
x2_axis: { ...x2_axis, tick_values: ticks.x2 },
|
|
502
|
+
y_axis: { ...y_axis, tick_values: ticks.y },
|
|
503
|
+
y2_axis: { ...y2_axis, tick_values: ticks.y2 },
|
|
504
|
+
})
|
|
505
|
+
: filter_padding(padding, default_padding)
|
|
506
|
+
// Expand right padding if y2 ticks are shown (only for vertical orientation)
|
|
507
|
+
if (
|
|
508
|
+
width && height && y2_series.length && ticks.y2.length &&
|
|
509
|
+
orientation === `vertical`
|
|
510
|
+
) {
|
|
511
|
+
// Need space for: tick shift + tick width + gap (30px) + label space (20px if present)
|
|
512
|
+
// When ticks are inside, they don't contribute to padding
|
|
513
|
+
const inside = y2_axis.tick?.label?.inside ?? false
|
|
514
|
+
const tick_shift = inside ? 0 : (y2_axis.tick?.label?.shift?.x ?? 0) + 8
|
|
515
|
+
const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max
|
|
516
|
+
const label_space = y2_axis.label ? 20 : 0
|
|
517
|
+
new_pad.r = Math.max(
|
|
518
|
+
new_pad.r,
|
|
519
|
+
tick_shift + tick_width_contribution + 30 + label_space,
|
|
520
|
+
)
|
|
521
|
+
}
|
|
522
|
+
// Expand top padding if x2 ticks are shown (only for vertical orientation)
|
|
523
|
+
if (
|
|
524
|
+
width && height && x2_series.length && ticks.x2.length &&
|
|
525
|
+
orientation === `vertical`
|
|
526
|
+
) {
|
|
527
|
+
const inside = x2_axis.tick?.label?.inside ?? false
|
|
528
|
+
const tick_shift = inside ? 0 : Math.abs(x2_axis.tick?.label?.shift?.y ?? 0) + 5
|
|
529
|
+
const tick_height = inside ? 0 : 16
|
|
530
|
+
const label_space = x2_axis.label ? 20 : 0
|
|
531
|
+
new_pad.t = Math.max(new_pad.t, tick_shift + tick_height + 30 + label_space)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Only update if padding actually changed (prevents infinite loop)
|
|
535
|
+
if (
|
|
536
|
+
base_pad.t !== new_pad.t || base_pad.b !== new_pad.b ||
|
|
537
|
+
base_pad.l !== new_pad.l || base_pad.r !== new_pad.r
|
|
538
|
+
) base_pad = new_pad
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
let legend_element = $state<HTMLDivElement | undefined>()
|
|
542
|
+
const legend_footprint = $derived(measured_footprint(legend_element, { width: 120, height: 60 }))
|
|
543
|
+
const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
|
|
544
|
+
|
|
545
|
+
// Obstacle field in normalized [0,1] plot coords (y=0 at top). Each bar is modeled as a segment
|
|
546
|
+
// from baseline to its tip so the legend can't hide inside a tall bar. Built from internal_series
|
|
547
|
+
// (pad-independent) + ranges so the crowding decision can't see its own reservation.
|
|
548
|
+
const obstacles_norm = $derived.by(() => {
|
|
549
|
+
if (!width || !height || !visible_series.length) return []
|
|
550
|
+
const base_w = width - base_pad.l - base_pad.r
|
|
551
|
+
const base_h = height - base_pad.t - base_pad.b
|
|
552
|
+
if (base_w <= 0 || base_h <= 0) return []
|
|
553
|
+
const bars: { points: { x: number; y: number }[]; draws_line: boolean }[] = []
|
|
554
|
+
const vertical = orientation === `vertical`
|
|
555
|
+
internal_series.forEach((srs, series_idx) => {
|
|
556
|
+
if (!(srs?.visible ?? true)) return
|
|
557
|
+
const is_line = srs.render_mode === `line`
|
|
558
|
+
const series_offsets = stacked_offsets[series_idx] ?? []
|
|
559
|
+
const [ax0, ax1] = srs.x_axis === `x2` ? ranges.current.x2 : ranges.current.x
|
|
560
|
+
const [vy0, vy1] = srs.y_axis === `y2` ? ranges.current.y2 : ranges.current.y
|
|
561
|
+
const [cy0, cy1] = ranges.current.y
|
|
562
|
+
const x_span = ax1 - ax0
|
|
563
|
+
const y_span = vy1 - vy0
|
|
564
|
+
const cy_span = cy1 - cy0
|
|
565
|
+
if (!(x_span > 0) || !((vertical ? y_span : cy_span) > 0)) return
|
|
566
|
+
srs.x.forEach((x_val, bar_idx) => {
|
|
567
|
+
const base = !is_line && mode === `stacked` ? (series_offsets[bar_idx] ?? 0) : 0
|
|
568
|
+
const value = base + srs.y[bar_idx]
|
|
569
|
+
// vertical: category on x, value rises on y (inverted). horizontal: category on y, value on x
|
|
570
|
+
const seg = vertical
|
|
571
|
+
? clip_bar(true, (x_val - ax0) / x_span, 1 - (value - vy0) / y_span, 1 - (base - vy0) / y_span)
|
|
572
|
+
: clip_bar(false, 1 - (x_val - cy0) / cy_span, (value - ax0) / x_span, (base - ax0) / x_span)
|
|
573
|
+
if (seg) bars.push(seg)
|
|
574
|
+
})
|
|
575
|
+
})
|
|
576
|
+
return build_obstacles_norm(bars, base_w, base_h)
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
// Move the legend to the bottom margin when no interior spot avoids the bars
|
|
580
|
+
const decor = $derived.by(() =>
|
|
581
|
+
place_decorations({
|
|
582
|
+
base_pad,
|
|
583
|
+
width,
|
|
584
|
+
height,
|
|
585
|
+
obstacles_norm,
|
|
586
|
+
// gate on legend_element (the render signal) not legend_data, whose entries can read pad
|
|
587
|
+
legend: legend != null &&
|
|
588
|
+
(show_legend !== undefined ? show_legend : series.length > 1) &&
|
|
589
|
+
legend_element != null && !legend_has_explicit_pos
|
|
590
|
+
? { footprint: legend_footprint, clearance: legend?.axis_clearance }
|
|
591
|
+
: null,
|
|
592
|
+
})
|
|
593
|
+
)
|
|
594
|
+
const pad = $derived(decor.pad)
|
|
595
|
+
const legend_auto_outside = $derived(decor.legend_outside)
|
|
596
|
+
const legend_outside_x = $derived(decor.legend_pos.x)
|
|
597
|
+
const legend_outside_y = $derived(decor.legend_pos.y)
|
|
598
|
+
const chart_width = $derived(Math.max(1, width - pad.l - pad.r))
|
|
599
|
+
const chart_height = $derived(Math.max(1, height - pad.t - pad.b))
|
|
600
|
+
|
|
601
|
+
// Scales
|
|
602
|
+
let scales = $derived({
|
|
603
|
+
x: create_scale(x_axis.scale_type ?? `linear`, ranges.current.x, [
|
|
604
|
+
pad.l,
|
|
605
|
+
width - pad.r,
|
|
606
|
+
]),
|
|
607
|
+
x2: create_scale(x2_axis.scale_type ?? `linear`, ranges.current.x2, [
|
|
608
|
+
pad.l,
|
|
609
|
+
width - pad.r,
|
|
610
|
+
]),
|
|
611
|
+
y: create_scale(y_axis.scale_type ?? `linear`, ranges.current.y, [
|
|
612
|
+
height - pad.b,
|
|
613
|
+
pad.t,
|
|
614
|
+
]),
|
|
615
|
+
y2: create_scale(y2_axis.scale_type ?? `linear`, ranges.current.y2, [
|
|
616
|
+
height - pad.b,
|
|
617
|
+
pad.t,
|
|
618
|
+
]),
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
// Compute plot center for point tweening origin
|
|
622
|
+
let plot_center_x = $derived(pad.l + (width - pad.r - pad.l) / 2)
|
|
623
|
+
let plot_center_y = $derived(pad.t + (height - pad.b - pad.t) / 2)
|
|
624
|
+
|
|
625
|
+
// Compute color values from line series for color scaling (filter to numbers only)
|
|
626
|
+
let all_color_values = $derived(
|
|
627
|
+
visible_series
|
|
628
|
+
.filter((srs: BarSeries<Metadata>) => srs.render_mode === `line`)
|
|
629
|
+
.flatMap((srs: BarSeries<Metadata>) =>
|
|
630
|
+
(srs.color_values ?? []).filter(
|
|
631
|
+
(val): val is number => typeof val === `number`,
|
|
632
|
+
)
|
|
633
|
+
),
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
// Create auto color range (safely handle empty arrays or undefined extent results)
|
|
637
|
+
let auto_color_range: [number, number] = $derived.by(() => {
|
|
638
|
+
if (all_color_values.length === 0) return [0, 1]
|
|
639
|
+
const [min_val, max_val] = extent(all_color_values)
|
|
640
|
+
return [min_val ?? 0, max_val ?? 1]
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
// All size values from line series (for size scale, filter to numbers only)
|
|
644
|
+
let all_size_values = $derived(
|
|
645
|
+
visible_series
|
|
646
|
+
.filter((srs: BarSeries<Metadata>) => srs.render_mode === `line`)
|
|
647
|
+
.flatMap((srs: BarSeries<Metadata>) =>
|
|
648
|
+
[...(srs.size_values ?? [])].filter(
|
|
649
|
+
(val): val is number => typeof val === `number`,
|
|
650
|
+
)
|
|
651
|
+
),
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
// Color scale function (using shared utility)
|
|
655
|
+
let color_scale_fn = $derived(create_color_scale(color_scale, auto_color_range))
|
|
656
|
+
|
|
657
|
+
// Size scale function (using shared utility)
|
|
658
|
+
let size_scale_fn = $derived(create_size_scale(size_scale, all_size_values))
|
|
659
|
+
|
|
660
|
+
// Auto-generate tick labels for categorical data (unless user provides explicit ticks)
|
|
661
|
+
// In vertical mode categories are on x-axis; in horizontal mode on y-axis
|
|
662
|
+
let cat_axis = $derived(orientation === `horizontal` ? `y` : `x`)
|
|
663
|
+
let effective_cat_ticks = $derived.by(() => {
|
|
664
|
+
if (!category_list.length) return undefined
|
|
665
|
+
// Only respect user ticks when they're a Record (custom label mapping),
|
|
666
|
+
// not a number (tick count) or array (tick positions)
|
|
667
|
+
const user_ticks = cat_axis === `x` ? x_axis.ticks : y_axis.ticks
|
|
668
|
+
if (
|
|
669
|
+
user_ticks != null && typeof user_ticks === `object` &&
|
|
670
|
+
!Array.isArray(user_ticks)
|
|
671
|
+
) return user_ticks
|
|
672
|
+
return Object.fromEntries(
|
|
673
|
+
category_list.map((cat, idx) => [idx, cat]),
|
|
674
|
+
) as Record<number, string>
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
// Ticks
|
|
678
|
+
let ticks = $derived({
|
|
679
|
+
x: width && height
|
|
680
|
+
? (category_indices && cat_axis === `x` ? category_indices : generate_ticks(
|
|
681
|
+
ranges.current.x,
|
|
682
|
+
x_axis.scale_type ?? `linear`,
|
|
683
|
+
x_axis.ticks,
|
|
684
|
+
scales.x,
|
|
685
|
+
{ default_count: 8 },
|
|
686
|
+
))
|
|
687
|
+
: [],
|
|
688
|
+
y: width && height
|
|
689
|
+
? (category_indices && cat_axis === `y` ? category_indices : generate_ticks(
|
|
690
|
+
ranges.current.y,
|
|
691
|
+
y_axis.scale_type ?? `linear`,
|
|
692
|
+
y_axis.ticks,
|
|
693
|
+
scales.y,
|
|
694
|
+
{ default_count: 6 },
|
|
695
|
+
))
|
|
696
|
+
: [],
|
|
697
|
+
y2: width && height && y2_series.length > 0 && orientation === `vertical`
|
|
698
|
+
? generate_ticks(
|
|
699
|
+
ranges.current.y2,
|
|
700
|
+
y2_axis.scale_type ?? `linear`,
|
|
701
|
+
y2_axis.ticks,
|
|
702
|
+
scales.y2,
|
|
703
|
+
{
|
|
704
|
+
default_count: 6,
|
|
705
|
+
},
|
|
706
|
+
)
|
|
707
|
+
: [],
|
|
708
|
+
x2: width && height && x2_series.length > 0 && orientation === `vertical`
|
|
709
|
+
? generate_ticks(
|
|
710
|
+
ranges.current.x2,
|
|
711
|
+
x2_axis.scale_type ?? `linear`,
|
|
712
|
+
x2_axis.ticks,
|
|
713
|
+
scales.x2,
|
|
714
|
+
{
|
|
715
|
+
default_count: 8,
|
|
716
|
+
},
|
|
717
|
+
)
|
|
718
|
+
: [],
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
// Cache measured tick-label widths so expensive canvas text measurement
|
|
722
|
+
// only runs when ticks/format change, not on every template rerender.
|
|
723
|
+
let tick_label_widths = $derived({
|
|
724
|
+
y_max: measure_max_tick_width(ticks.y, y_axis.format ?? ``),
|
|
725
|
+
y2_max: measure_max_tick_width(ticks.y2, y2_axis.format ?? ``),
|
|
726
|
+
x2_max: measure_max_tick_width(ticks.x2, x2_axis.format ?? ``),
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
// Zoom drag state
|
|
730
|
+
let drag_state = $state<{
|
|
731
|
+
start: { x: number; y: number } | null
|
|
732
|
+
current: { x: number; y: number } | null
|
|
733
|
+
bounds: DOMRect | null
|
|
734
|
+
}>({ start: null, current: null, bounds: null })
|
|
735
|
+
|
|
736
|
+
// Pan state
|
|
737
|
+
let is_focused = $state(false)
|
|
738
|
+
let shift_held = $state(false)
|
|
739
|
+
let pan_drag_state = $state<
|
|
740
|
+
InitialRanges & { start: { x: number; y: number } } | null
|
|
741
|
+
>(null)
|
|
742
|
+
let touch_state = $state<
|
|
743
|
+
InitialRanges & { start_touches: { x: number; y: number }[] } | null
|
|
744
|
+
>(null)
|
|
745
|
+
const on_window_mouse_move = (evt: MouseEvent) => {
|
|
746
|
+
if (!drag_state.start || !drag_state.bounds) return
|
|
747
|
+
drag_state.current = {
|
|
748
|
+
x: evt.clientX - drag_state.bounds.left,
|
|
749
|
+
y: evt.clientY - drag_state.bounds.top,
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const on_window_mouse_up = () => {
|
|
753
|
+
if (drag_state.start && drag_state.current) {
|
|
754
|
+
const x1_raw = scales.x.invert(drag_state.start.x) as number | Date
|
|
755
|
+
const x2_raw = scales.x.invert(drag_state.current.x) as number | Date
|
|
756
|
+
const y1 = scales.y.invert(drag_state.start.y)
|
|
757
|
+
const y2 = scales.y.invert(drag_state.current.y)
|
|
758
|
+
const y2_1 = scales.y2.invert(drag_state.start.y)
|
|
759
|
+
const y2_2 = scales.y2.invert(drag_state.current.y)
|
|
760
|
+
const x2a_1_raw = scales.x2.invert(drag_state.start.x) as number | Date
|
|
761
|
+
const x2a_2_raw = scales.x2.invert(drag_state.current.x) as number | Date
|
|
762
|
+
const dx = Math.abs(drag_state.start.x - drag_state.current.x)
|
|
763
|
+
const dy = Math.abs(drag_state.start.y - drag_state.current.y)
|
|
764
|
+
|
|
765
|
+
let xr1: number, xr2: number
|
|
766
|
+
if (x1_raw instanceof Date && x2_raw instanceof Date) {
|
|
767
|
+
;[xr1, xr2] = [x1_raw.getTime(), x2_raw.getTime()]
|
|
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]
|
|
778
|
+
|
|
779
|
+
if (dx > 5 && dy > 5 && Number.isFinite(xr1) && Number.isFinite(xr2)) {
|
|
780
|
+
// Update axis ranges to trigger reactivity and prevent effect from overriding
|
|
781
|
+
x_axis = { ...x_axis, range: [Math.min(xr1, xr2), Math.max(xr1, xr2)] }
|
|
782
|
+
if (x2_series.length > 0 && Number.isFinite(x2r1) && Number.isFinite(x2r2)) {
|
|
783
|
+
x2_axis = {
|
|
784
|
+
...x2_axis,
|
|
785
|
+
range: [Math.min(x2r1, x2r2), Math.max(x2r1, x2r2)],
|
|
786
|
+
}
|
|
787
|
+
}
|
|
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
|
+
}
|
|
791
|
+
}
|
|
792
|
+
drag_state = { start: null, current: null, bounds: null }
|
|
793
|
+
window.removeEventListener(`mousemove`, on_window_mouse_move)
|
|
794
|
+
window.removeEventListener(`mouseup`, on_window_mouse_up)
|
|
795
|
+
document.body.style.cursor = `default`
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Pan drag handlers
|
|
799
|
+
const on_pan_move = (evt: MouseEvent) => {
|
|
800
|
+
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
|
+
const sensitivity = pan?.drag_sensitivity ?? 1
|
|
806
|
+
|
|
807
|
+
const x_delta = pixels_to_data_delta(
|
|
808
|
+
-dx * sensitivity,
|
|
809
|
+
pan_drag_state.initial_x_range,
|
|
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,
|
|
821
|
+
)
|
|
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
|
+
}
|
|
833
|
+
|
|
834
|
+
const on_pan_end = () => {
|
|
835
|
+
pan_drag_state = null
|
|
836
|
+
document.body.style.cursor = ``
|
|
837
|
+
window.removeEventListener(`mousemove`, on_pan_move)
|
|
838
|
+
window.removeEventListener(`mouseup`, on_pan_end)
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function handle_mouse_down(evt: MouseEvent) {
|
|
842
|
+
const coords = get_relative_coords(evt)
|
|
843
|
+
if (!coords || !svg_element) return
|
|
844
|
+
|
|
845
|
+
// Check if pan is enabled and shift is held for pan mode
|
|
846
|
+
const pan_enabled = pan?.enabled !== false
|
|
847
|
+
if (pan_enabled && evt.shiftKey) {
|
|
848
|
+
evt.preventDefault()
|
|
849
|
+
pan_drag_state = {
|
|
850
|
+
start: { x: evt.clientX, y: evt.clientY },
|
|
851
|
+
initial_x_range: [...ranges.current.x] as [number, number],
|
|
852
|
+
initial_x2_range: [...ranges.current.x2] as [number, number],
|
|
853
|
+
initial_y_range: [...ranges.current.y] as [number, number],
|
|
854
|
+
initial_y2_range: [...ranges.current.y2] as [number, number],
|
|
855
|
+
}
|
|
856
|
+
document.body.style.cursor = `grabbing`
|
|
857
|
+
window.addEventListener(`mousemove`, on_pan_move)
|
|
858
|
+
window.addEventListener(`mouseup`, on_pan_end)
|
|
859
|
+
return
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
drag_state = {
|
|
863
|
+
start: coords,
|
|
864
|
+
current: coords,
|
|
865
|
+
bounds: svg_element.getBoundingClientRect(),
|
|
866
|
+
}
|
|
867
|
+
window.addEventListener(`mousemove`, on_window_mouse_move)
|
|
868
|
+
window.addEventListener(`mouseup`, on_window_mouse_up)
|
|
869
|
+
evt.preventDefault()
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// Wheel handler for pan (requires focus and shift)
|
|
873
|
+
function handle_wheel(evt: WheelEvent) {
|
|
874
|
+
const pan_enabled = pan?.enabled !== false
|
|
875
|
+
// Only capture wheel when focused AND Shift is held
|
|
876
|
+
// Use shift_held state (tracked via keydown/keyup) for compatibility with synthetic events
|
|
877
|
+
if (!pan_enabled || !is_focused || !shift_held) return
|
|
878
|
+
|
|
879
|
+
evt.preventDefault()
|
|
880
|
+
|
|
881
|
+
const sensitivity = pan?.wheel_sensitivity ?? 1
|
|
882
|
+
|
|
883
|
+
// Determine pan direction based on wheel delta
|
|
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
|
+
|
|
905
|
+
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
906
|
+
ranges.current.x = pan_range(ranges.current.x, x_delta)
|
|
907
|
+
ranges.current.x2 = pan_range(ranges.current.x2, x2_delta)
|
|
908
|
+
} else {
|
|
909
|
+
ranges.current.y = pan_range(ranges.current.y, y_delta)
|
|
910
|
+
ranges.current.y2 = pan_range(ranges.current.y2, y2_delta)
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// Touch handlers for pinch-zoom and two-finger pan
|
|
915
|
+
function handle_touch_start(evt: TouchEvent) {
|
|
916
|
+
const touch_enabled = pan?.enabled !== false && pan?.touch_enabled !== false
|
|
917
|
+
if (!touch_enabled || evt.touches.length !== 2) return
|
|
918
|
+
|
|
919
|
+
evt.preventDefault()
|
|
920
|
+
const touches = Array.from(evt.touches)
|
|
921
|
+
touch_state = {
|
|
922
|
+
start_touches: touches.map((touch) => ({ x: touch.clientX, y: touch.clientY })),
|
|
923
|
+
initial_x_range: [...ranges.current.x] as [number, number],
|
|
924
|
+
initial_x2_range: [...ranges.current.x2] as [number, number],
|
|
925
|
+
initial_y_range: [...ranges.current.y] as [number, number],
|
|
926
|
+
initial_y2_range: [...ranges.current.y2] as [number, number],
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function handle_touch_move(evt: TouchEvent) {
|
|
931
|
+
if (!touch_state || evt.touches.length !== 2) return
|
|
932
|
+
evt.preventDefault()
|
|
933
|
+
|
|
934
|
+
const [t1, t2] = Array.from(evt.touches)
|
|
935
|
+
const [s1, s2] = touch_state.start_touches
|
|
936
|
+
|
|
937
|
+
// Calculate center movement for pan
|
|
938
|
+
const start_center = { x: (s1.x + s2.x) / 2, y: (s1.y + s2.y) / 2 }
|
|
939
|
+
const curr_center = {
|
|
940
|
+
x: (t1.clientX + t2.clientX) / 2,
|
|
941
|
+
y: (t1.clientY + t2.clientY) / 2,
|
|
942
|
+
}
|
|
943
|
+
const dx = curr_center.x - start_center.x
|
|
944
|
+
const dy = curr_center.y - start_center.y
|
|
945
|
+
|
|
946
|
+
// Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
|
|
947
|
+
const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
|
|
948
|
+
// Guard against zero-distance pinch to avoid Infinity scale
|
|
949
|
+
if (start_dist < Number.EPSILON) return
|
|
950
|
+
const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
|
|
951
|
+
const scale = curr_dist / start_dist
|
|
952
|
+
|
|
953
|
+
// If scale changed significantly, treat as pinch-zoom
|
|
954
|
+
// Also guard against scale being too small to avoid division by zero
|
|
955
|
+
if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
|
|
956
|
+
// Pinch zoom centered on gesture center
|
|
957
|
+
// Divide by scale so spread (scale > 1) = smaller span (zoom in)
|
|
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
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function handle_touch_end() {
|
|
1019
|
+
touch_state = null
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// Legend data and handlers
|
|
1023
|
+
let legend_data = $derived.by<LegendItem[]>(() =>
|
|
1024
|
+
series.map((srs: BarSeries<Metadata>, idx: number) => {
|
|
1025
|
+
const is_line = srs.render_mode === `line`
|
|
1026
|
+
const series_markers = srs.markers ?? DEFAULT_MARKERS
|
|
1027
|
+
const has_line = series_markers === `line` || series_markers === `line+points`
|
|
1028
|
+
const has_points = series_markers === `points` ||
|
|
1029
|
+
series_markers === `line+points`
|
|
1030
|
+
const series_color = srs.color ?? (is_line ? line_state.color : bar_state.color)
|
|
1031
|
+
|
|
1032
|
+
// Get point style for symbol color (handle array or single object)
|
|
1033
|
+
const first_point_style = Array.isArray(srs.point_style)
|
|
1034
|
+
? srs.point_style[0]
|
|
1035
|
+
: srs.point_style
|
|
1036
|
+
const first_color_value = srs.color_values?.[0]
|
|
1037
|
+
const point_color = first_color_value != null
|
|
1038
|
+
? color_scale_fn(first_color_value)
|
|
1039
|
+
: first_point_style?.fill ?? series_color
|
|
1040
|
+
|
|
1041
|
+
if (is_line) {
|
|
1042
|
+
// Line series: show line and/or symbol based on markers
|
|
1043
|
+
return {
|
|
1044
|
+
series_idx: idx,
|
|
1045
|
+
label: srs.label ?? `Series ${idx + 1}`,
|
|
1046
|
+
visible: srs.visible ?? true,
|
|
1047
|
+
legend_group: srs.legend_group,
|
|
1048
|
+
display_style: {
|
|
1049
|
+
...(has_line
|
|
1050
|
+
? {
|
|
1051
|
+
line_color: series_color,
|
|
1052
|
+
line_dash: srs.line_style?.line_dash,
|
|
1053
|
+
}
|
|
1054
|
+
: {}),
|
|
1055
|
+
...(has_points
|
|
1056
|
+
? {
|
|
1057
|
+
symbol_type: first_point_style?.symbol_type ??
|
|
1058
|
+
DEFAULTS.scatter.symbol_type,
|
|
1059
|
+
symbol_color: point_color,
|
|
1060
|
+
}
|
|
1061
|
+
: {}),
|
|
1062
|
+
},
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
// Bar series: show square symbol
|
|
1066
|
+
return {
|
|
1067
|
+
series_idx: idx,
|
|
1068
|
+
label: srs.label ?? `Series ${idx + 1}`,
|
|
1069
|
+
visible: srs.visible ?? true,
|
|
1070
|
+
legend_group: srs.legend_group,
|
|
1071
|
+
display_style: {
|
|
1072
|
+
symbol_type: `Square` as const,
|
|
1073
|
+
symbol_color: series_color,
|
|
1074
|
+
},
|
|
1075
|
+
}
|
|
1076
|
+
})
|
|
1077
|
+
)
|
|
1078
|
+
|
|
1079
|
+
function toggle_series_visibility(series_idx: number) {
|
|
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
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// Collect bar and line positions for legend placement
|
|
1105
|
+
let bar_points_for_placement = $derived.by(() => {
|
|
1106
|
+
if (!width || !height || !visible_series.length) return []
|
|
1107
|
+
|
|
1108
|
+
return internal_series.flatMap((srs, series_idx) => {
|
|
1109
|
+
if (!(srs?.visible ?? true)) return []
|
|
1110
|
+
const is_line = srs.render_mode === `line`
|
|
1111
|
+
const series_offsets = stacked_offsets[series_idx] ?? []
|
|
1112
|
+
const use_y2 = srs.y_axis === `y2`
|
|
1113
|
+
const y_scale = use_y2 ? scales.y2 : scales.y
|
|
1114
|
+
const use_x2_pl = srs.x_axis === `x2`
|
|
1115
|
+
const x_scale_pl = use_x2_pl ? scales.x2 : scales.x
|
|
1116
|
+
return srs.x
|
|
1117
|
+
.map((x_val, bar_idx) => {
|
|
1118
|
+
const y_val = srs.y[bar_idx]
|
|
1119
|
+
const base = !is_line && mode === `stacked`
|
|
1120
|
+
? (series_offsets[bar_idx] ?? 0)
|
|
1121
|
+
: 0
|
|
1122
|
+
const [bar_x, bar_y] = orientation === `vertical`
|
|
1123
|
+
? [x_scale_pl(x_val), y_scale(base + y_val)]
|
|
1124
|
+
: [x_scale_pl(base + y_val), scales.y(x_val)]
|
|
1125
|
+
return { x: bar_x, y: bar_y }
|
|
1126
|
+
})
|
|
1127
|
+
.filter(({ x, y }) => isFinite(x) && isFinite(y))
|
|
1128
|
+
})
|
|
1129
|
+
})
|
|
1130
|
+
|
|
1131
|
+
// Legend placement stability state (legend_element declared above for the auto-place block)
|
|
1132
|
+
let hovered_legend_series_idx = $state<number | null>(null)
|
|
1133
|
+
const legend_hover = create_hover_lock()
|
|
1134
|
+
const dim_tracker = create_dimension_tracker()
|
|
1135
|
+
let has_initial_legend_placement = $state(false)
|
|
1136
|
+
|
|
1137
|
+
// Clear pending hover lock timeout on unmount
|
|
1138
|
+
$effect(() => () => legend_hover.cleanup())
|
|
1139
|
+
|
|
1140
|
+
// Calculate best legend placement using continuous grid sampling
|
|
1141
|
+
let legend_placement = $derived.by(() => {
|
|
1142
|
+
const should_show = show_legend !== undefined ? show_legend : series.length > 1
|
|
1143
|
+
if (!should_show || !width || !height) return null
|
|
1144
|
+
|
|
1145
|
+
const result = compute_element_placement({
|
|
1146
|
+
plot_bounds: { x: pad.l, y: pad.t, width: chart_width, height: chart_height },
|
|
1147
|
+
element: legend_element,
|
|
1148
|
+
element_size: { width: 120, height: 60 }, // fallback before first render
|
|
1149
|
+
axis_clearance: legend?.axis_clearance,
|
|
1150
|
+
exclude_rects: [],
|
|
1151
|
+
points: bar_points_for_placement,
|
|
1152
|
+
})
|
|
1153
|
+
|
|
1154
|
+
return result
|
|
1155
|
+
})
|
|
1156
|
+
|
|
1157
|
+
// Tweened legend coordinates for smooth animation - create once, update target via effect
|
|
1158
|
+
// untrack() explicitly captures initial tween config (intentional - config set once at mount)
|
|
1159
|
+
const tweened_legend_coords = new Tween(
|
|
1160
|
+
{ x: 0, y: 0 },
|
|
1161
|
+
untrack(() => ({ duration: 400, ...legend?.tween })),
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
// Update legend position with stability checks
|
|
1165
|
+
$effect(() => {
|
|
1166
|
+
if (!width || !height || !legend_placement) return
|
|
1167
|
+
|
|
1168
|
+
// Track dimensions for resize detection
|
|
1169
|
+
const dims_changed = dim_tracker.has_changed(width, height)
|
|
1170
|
+
if (dims_changed) dim_tracker.update(width, height)
|
|
1171
|
+
|
|
1172
|
+
const is_responsive = legend?.responsive ?? false
|
|
1173
|
+
// Only update if: resize occurred, OR (not hover-locked AND (responsive OR not yet initially placed))
|
|
1174
|
+
const should_update = dims_changed || (!legend_hover.is_locked.current &&
|
|
1175
|
+
(is_responsive || !has_initial_legend_placement))
|
|
1176
|
+
|
|
1177
|
+
if (should_update) {
|
|
1178
|
+
tweened_legend_coords.set(
|
|
1179
|
+
{ x: legend_placement.x, y: legend_placement.y },
|
|
1180
|
+
// Skip animation on initial placement to avoid jump from (0, 0)
|
|
1181
|
+
has_initial_legend_placement ? undefined : { duration: 0 },
|
|
1182
|
+
)
|
|
1183
|
+
// Only lock position after we have actual measured size
|
|
1184
|
+
if (legend_element) {
|
|
1185
|
+
has_initial_legend_placement = true
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
})
|
|
1189
|
+
|
|
1190
|
+
// Tooltip state
|
|
1191
|
+
let hover_info = $state<BarHandlerProps<Metadata> | null>(null)
|
|
1192
|
+
let tooltip_el = $state<HTMLDivElement | undefined>()
|
|
1193
|
+
|
|
1194
|
+
function get_bar_data(
|
|
1195
|
+
series_idx: number,
|
|
1196
|
+
bar_idx: number,
|
|
1197
|
+
color: string,
|
|
1198
|
+
): BarHandlerProps<Metadata> {
|
|
1199
|
+
const srs = internal_series[series_idx]
|
|
1200
|
+
const [x, y] = [srs.x[bar_idx], srs.y[bar_idx]]
|
|
1201
|
+
const [orient_x, orient_y] = orientation === `horizontal` ? [y, x] : [x, y]
|
|
1202
|
+
const metadata = Array.isArray(srs.metadata)
|
|
1203
|
+
? srs.metadata[bar_idx]
|
|
1204
|
+
: srs.metadata
|
|
1205
|
+
const label = srs.labels?.[bar_idx] ?? null
|
|
1206
|
+
const active_y_axis = srs.y_axis ?? `y1`
|
|
1207
|
+
const active_x_axis = srs.x_axis ?? `x1`
|
|
1208
|
+
const category_label = category_list[x]
|
|
1209
|
+
const coords = {
|
|
1210
|
+
x,
|
|
1211
|
+
y,
|
|
1212
|
+
orient_x,
|
|
1213
|
+
orient_y,
|
|
1214
|
+
x_axis: active_x_axis === `x2` ? x2_axis : x_axis,
|
|
1215
|
+
x2_axis,
|
|
1216
|
+
y_axis: active_y_axis === `y2` ? y2_axis : y_axis,
|
|
1217
|
+
y2_axis,
|
|
1218
|
+
}
|
|
1219
|
+
return {
|
|
1220
|
+
...coords,
|
|
1221
|
+
metadata,
|
|
1222
|
+
color,
|
|
1223
|
+
label,
|
|
1224
|
+
series_idx,
|
|
1225
|
+
bar_idx,
|
|
1226
|
+
active_y_axis,
|
|
1227
|
+
active_x_axis,
|
|
1228
|
+
category_label,
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// Find the point closest to the cursor on a polyline overlay (O(n) scan).
|
|
1233
|
+
function find_closest_point(
|
|
1234
|
+
evt: MouseEvent,
|
|
1235
|
+
points: LineSeriesPoint[],
|
|
1236
|
+
): LineSeriesPoint | null {
|
|
1237
|
+
const target = evt.target
|
|
1238
|
+
if (!(target instanceof Element)) return null
|
|
1239
|
+
const svg_el = target.closest(`svg`)
|
|
1240
|
+
if (!svg_el) return null
|
|
1241
|
+
const rect = svg_el.getBoundingClientRect()
|
|
1242
|
+
const mx = evt.clientX - rect.left
|
|
1243
|
+
const my = evt.clientY - rect.top
|
|
1244
|
+
let best: LineSeriesPoint | null = null
|
|
1245
|
+
let best_dist = Infinity
|
|
1246
|
+
for (const pt of points) {
|
|
1247
|
+
const dist = (pt.x - mx) ** 2 + (pt.y - my) ** 2
|
|
1248
|
+
if (dist < best_dist) {
|
|
1249
|
+
best_dist = dist
|
|
1250
|
+
best = pt
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return best
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
const line_point_fill = (pt: LineSeriesPoint, series_color: string): string =>
|
|
1257
|
+
pt.color_value != null
|
|
1258
|
+
? color_scale_fn(pt.color_value)
|
|
1259
|
+
: pt.point_style?.fill ?? series_color
|
|
1260
|
+
|
|
1261
|
+
const handle_bar_hover =
|
|
1262
|
+
(series_idx: number, bar_idx: number, color: string) => (event: MouseEvent) => {
|
|
1263
|
+
hovered = true
|
|
1264
|
+
hover_info = get_bar_data(series_idx, bar_idx, color)
|
|
1265
|
+
change(hover_info)
|
|
1266
|
+
on_bar_hover?.({ ...hover_info, event })
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// Stack offsets (only for bar series in stacked mode, grouped by y-axis)
|
|
1270
|
+
let stacked_offsets = $derived.by(() => {
|
|
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
|
+
})
|
|
1302
|
+
|
|
1303
|
+
// Calculate group positions for grouped mode (side-by-side bars)
|
|
1304
|
+
let group_info = $derived.by(() => {
|
|
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
|
+
})
|
|
1313
|
+
|
|
1314
|
+
// Set theme-aware background when entering fullscreen
|
|
1315
|
+
$effect(() => {
|
|
1316
|
+
set_fullscreen_bg(wrapper, fullscreen, `--barplot-fullscreen-bg`)
|
|
1317
|
+
})
|
|
1318
|
+
|
|
1319
|
+
// State accessors for shared axis change handler
|
|
1320
|
+
const axis_state: AxisChangeState<BarSeries<Metadata>> = {
|
|
1321
|
+
get_axis: (axis) => {
|
|
1322
|
+
if (axis === `x`) return x_axis
|
|
1323
|
+
if (axis === `x2`) return x2_axis
|
|
1324
|
+
if (axis === `y`) return y_axis
|
|
1325
|
+
return y2_axis
|
|
1326
|
+
},
|
|
1327
|
+
set_axis: (axis, config) => {
|
|
1328
|
+
// Spread into existing state to preserve merged type structure
|
|
1329
|
+
if (axis === `x`) x_axis = { ...x_axis, ...config }
|
|
1330
|
+
else if (axis === `x2`) x2_axis = { ...x2_axis, ...config }
|
|
1331
|
+
else if (axis === `y`) y_axis = { ...y_axis, ...config }
|
|
1332
|
+
else y2_axis = { ...y2_axis, ...config }
|
|
1333
|
+
},
|
|
1334
|
+
get_series: () => series,
|
|
1335
|
+
set_series: (new_series) => (series = new_series),
|
|
1336
|
+
get_loading: () => axis_loading,
|
|
1337
|
+
set_loading: (axis) => (axis_loading = axis),
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// Create shared handler bound to this component's state
|
|
1341
|
+
// Using $derived so handler updates when callback props change
|
|
1342
|
+
const handle_axis_change = $derived(create_axis_change_handler(
|
|
1343
|
+
axis_state,
|
|
1344
|
+
data_loader,
|
|
1345
|
+
on_axis_change,
|
|
1346
|
+
on_error,
|
|
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
|
+
})
|
|
1366
|
+
</script>
|
|
1367
|
+
|
|
1368
|
+
{#snippet ref_lines_layer(lines: IndexedRefLine[])}
|
|
1369
|
+
{#each lines as line (line.id ?? line.idx)}
|
|
1370
|
+
<ReferenceLine
|
|
1371
|
+
ref_line={line}
|
|
1372
|
+
line_idx={line.idx}
|
|
1373
|
+
x_min={line.x_axis === `x2` ? ranges.current.x2[0] : ranges.current.x[0]}
|
|
1374
|
+
x_max={line.x_axis === `x2` ? ranges.current.x2[1] : ranges.current.x[1]}
|
|
1375
|
+
y_min={line.y_axis === `y2` ? ranges.current.y2[0] : ranges.current.y[0]}
|
|
1376
|
+
y_max={line.y_axis === `y2` ? ranges.current.y2[1] : ranges.current.y[1]}
|
|
1377
|
+
x_scale={scales.x}
|
|
1378
|
+
x2_scale={scales.x2}
|
|
1379
|
+
y_scale={scales.y}
|
|
1380
|
+
y2_scale={scales.y2}
|
|
1381
|
+
{clip_path_id}
|
|
1382
|
+
hovered_line_idx={hovered_ref_line_idx}
|
|
1383
|
+
on_click={(event: RefLineEvent) => {
|
|
1384
|
+
line.on_click?.(event)
|
|
1385
|
+
on_ref_line_click?.(event)
|
|
1386
|
+
}}
|
|
1387
|
+
on_hover={(event: RefLineEvent | null) => {
|
|
1388
|
+
hovered_ref_line_idx = event?.line_idx ?? null
|
|
1389
|
+
line.on_hover?.(event)
|
|
1390
|
+
on_ref_line_hover?.(event)
|
|
1391
|
+
}}
|
|
1392
|
+
/>
|
|
1393
|
+
{/each}
|
|
1394
|
+
{/snippet}
|
|
1395
|
+
|
|
1396
|
+
<svelte:window
|
|
1397
|
+
onkeydown={(evt) => {
|
|
1398
|
+
if (evt.key === `Escape` && fullscreen) {
|
|
1399
|
+
evt.preventDefault()
|
|
1400
|
+
fullscreen = false
|
|
1401
|
+
}
|
|
1402
|
+
if (evt.key === `Shift`) shift_held = true
|
|
1403
|
+
}}
|
|
1404
|
+
onkeyup={(evt) => {
|
|
1405
|
+
if (evt.key === `Shift`) shift_held = false
|
|
1406
|
+
}}
|
|
1407
|
+
/>
|
|
1408
|
+
|
|
1409
|
+
<div
|
|
1410
|
+
bind:this={wrapper}
|
|
1411
|
+
bind:clientWidth={width}
|
|
1412
|
+
bind:clientHeight={height}
|
|
1413
|
+
{...rest}
|
|
1414
|
+
class="bar-plot {rest.class ?? ``}"
|
|
1415
|
+
class:fullscreen
|
|
1416
|
+
>
|
|
1417
|
+
{#if width && height}
|
|
1418
|
+
<div class="header-controls">
|
|
1419
|
+
{@render header_controls?.({ height, width, fullscreen })}
|
|
1420
|
+
{#if fullscreen_toggle}
|
|
1421
|
+
<FullscreenToggle bind:fullscreen />
|
|
1422
|
+
{/if}
|
|
1423
|
+
</div>
|
|
1424
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
1425
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
1426
|
+
<svg
|
|
1427
|
+
bind:this={svg_element}
|
|
1428
|
+
role="application"
|
|
1429
|
+
aria-label={rest[`aria-label`] ??
|
|
1430
|
+
([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Bar chart`)}
|
|
1431
|
+
tabindex="0"
|
|
1432
|
+
onfocusin={() => (is_focused = true)}
|
|
1433
|
+
onfocusout={() => (is_focused = false)}
|
|
1434
|
+
onmousedown={handle_mouse_down}
|
|
1435
|
+
ondblclick={() => {
|
|
1436
|
+
// Reset zoom to initial ranges (undo any pan/zoom)
|
|
1437
|
+
ranges.current.x = [...ranges.initial.x] as [number, number]
|
|
1438
|
+
ranges.current.x2 = [...ranges.initial.x2] as [number, number]
|
|
1439
|
+
ranges.current.y = [...ranges.initial.y] as [number, number]
|
|
1440
|
+
ranges.current.y2 = [...ranges.initial.y2] as [number, number]
|
|
1441
|
+
// Also reset axis props so future data changes recalculate auto ranges
|
|
1442
|
+
x_axis = { ...x_axis, range: [null, null] }
|
|
1443
|
+
x2_axis = { ...x2_axis, range: [null, null] }
|
|
1444
|
+
y_axis = { ...y_axis, range: [null, null] }
|
|
1445
|
+
y2_axis = { ...y2_axis, range: [null, null] }
|
|
1446
|
+
}}
|
|
1447
|
+
onmouseleave={() => {
|
|
1448
|
+
hovered = false
|
|
1449
|
+
hover_info = null
|
|
1450
|
+
change(null)
|
|
1451
|
+
on_bar_hover?.(null)
|
|
1452
|
+
}}
|
|
1453
|
+
onwheel={handle_wheel}
|
|
1454
|
+
ontouchstart={handle_touch_start}
|
|
1455
|
+
ontouchmove={handle_touch_move}
|
|
1456
|
+
ontouchend={handle_touch_end}
|
|
1457
|
+
style:cursor={pan_drag_state
|
|
1458
|
+
? `grabbing`
|
|
1459
|
+
: shift_held && pan?.enabled !== false
|
|
1460
|
+
? `grab`
|
|
1461
|
+
: `crosshair`}
|
|
1462
|
+
>
|
|
1463
|
+
<ZoomRect start={drag_state.start} current={drag_state.current} />
|
|
1464
|
+
|
|
1465
|
+
<!-- User content (custom overlays, reference lines, etc.) -->
|
|
1466
|
+
{@render user_content?.({
|
|
1467
|
+
height,
|
|
1468
|
+
width,
|
|
1469
|
+
x_scale_fn: scales.x,
|
|
1470
|
+
x2_scale_fn: scales.x2,
|
|
1471
|
+
y_scale_fn: scales.y,
|
|
1472
|
+
y2_scale_fn: scales.y2,
|
|
1473
|
+
pad,
|
|
1474
|
+
x_range: ranges.current.x,
|
|
1475
|
+
x2_range: ranges.current.x2,
|
|
1476
|
+
y_range: ranges.current.y,
|
|
1477
|
+
y2_range: ranges.current.y2,
|
|
1478
|
+
fullscreen,
|
|
1479
|
+
})}
|
|
1480
|
+
|
|
1481
|
+
<!-- Reference lines: below grid (rendered before axes which contain grid lines) -->
|
|
1482
|
+
{@render ref_lines_layer(ref_lines_by_z.below_grid)}
|
|
1483
|
+
|
|
1484
|
+
<!-- X-axis -->
|
|
1485
|
+
<PlotAxis
|
|
1486
|
+
side="x"
|
|
1487
|
+
ticks={ticks.x as number[]}
|
|
1488
|
+
place={scales.x}
|
|
1489
|
+
axis={x_axis}
|
|
1490
|
+
{pad}
|
|
1491
|
+
{width}
|
|
1492
|
+
{height}
|
|
1493
|
+
show_grid={display.x_grid}
|
|
1494
|
+
tick_label={(tick) =>
|
|
1495
|
+
get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
|
|
1496
|
+
label_x={pad.l + chart_width / 2 + (x_axis.label_shift?.x ?? 0)}
|
|
1497
|
+
label_y={height - pad.b / 3 + (x_axis.label_shift?.y ?? 0)}
|
|
1498
|
+
axis_loading={axis_loading === `x`}
|
|
1499
|
+
on_axis_change={(key) => handle_axis_change(`x`, key)}
|
|
1500
|
+
/>
|
|
1501
|
+
|
|
1502
|
+
<!-- X2-axis (Top) -->
|
|
1503
|
+
<!-- Note: x2 axis is only supported for vertical orientation -->
|
|
1504
|
+
{#if x2_series.length > 0 && orientation === `vertical`}
|
|
1505
|
+
<PlotAxis
|
|
1506
|
+
side="x2"
|
|
1507
|
+
ticks={ticks.x2 as number[]}
|
|
1508
|
+
place={scales.x2}
|
|
1509
|
+
axis={x2_axis}
|
|
1510
|
+
{pad}
|
|
1511
|
+
{width}
|
|
1512
|
+
{height}
|
|
1513
|
+
show_grid={display.x2_grid}
|
|
1514
|
+
tick_label={(tick) => get_tick_label(tick, x2_axis.ticks)}
|
|
1515
|
+
label_x={pad.l + chart_width / 2 + (x2_axis.label_shift?.x ?? 0)}
|
|
1516
|
+
label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? 40))}
|
|
1517
|
+
axis_loading={axis_loading === `x2`}
|
|
1518
|
+
on_axis_change={(key) => handle_axis_change(`x2`, key)}
|
|
1519
|
+
/>
|
|
1520
|
+
{/if}
|
|
1521
|
+
|
|
1522
|
+
<!-- Y-axis -->
|
|
1523
|
+
<PlotAxis
|
|
1524
|
+
side="y"
|
|
1525
|
+
ticks={ticks.y as number[]}
|
|
1526
|
+
place={scales.y}
|
|
1527
|
+
axis={y_axis}
|
|
1528
|
+
{pad}
|
|
1529
|
+
{width}
|
|
1530
|
+
{height}
|
|
1531
|
+
show_grid={display.y_grid}
|
|
1532
|
+
tick_label={(tick) =>
|
|
1533
|
+
get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
|
|
1534
|
+
label_x={Math.max(
|
|
1535
|
+
12,
|
|
1536
|
+
pad.l - (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
|
|
1537
|
+
LABEL_GAP_DEFAULT,
|
|
1538
|
+
) + (y_axis.label_shift?.x ?? 0)}
|
|
1539
|
+
label_y={pad.t + chart_height / 2 + (y_axis.label_shift?.y ?? 0)}
|
|
1540
|
+
axis_loading={axis_loading === `y`}
|
|
1541
|
+
on_axis_change={(key) => handle_axis_change(`y`, key)}
|
|
1542
|
+
/>
|
|
1543
|
+
|
|
1544
|
+
<!-- Y2-axis (Right) -->
|
|
1545
|
+
<!-- Note: y2 axis is only supported for vertical orientation. Implementing x2 for horizontal mode requires additional complexity. -->
|
|
1546
|
+
{#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
|
+
<PlotAxis
|
|
1551
|
+
side="y2"
|
|
1552
|
+
ticks={ticks.y2 as number[]}
|
|
1553
|
+
place={scales.y2}
|
|
1554
|
+
axis={y2_axis}
|
|
1555
|
+
{pad}
|
|
1556
|
+
{width}
|
|
1557
|
+
{height}
|
|
1558
|
+
show_grid={display.y2_grid}
|
|
1559
|
+
tick_label={(tick) => get_tick_label(tick, y2_axis.ticks)}
|
|
1560
|
+
label_x={width - pad.r + y2_tick_shift + y2_tick_width + LABEL_GAP_DEFAULT +
|
|
1561
|
+
(y2_axis.label_shift?.x ?? 0)}
|
|
1562
|
+
label_y={pad.t + chart_height / 2 + (y2_axis.label_shift?.y ?? 0)}
|
|
1563
|
+
axis_loading={axis_loading === `y2`}
|
|
1564
|
+
on_axis_change={(key) => handle_axis_change(`y2`, key)}
|
|
1565
|
+
/>
|
|
1566
|
+
{/if}
|
|
1567
|
+
|
|
1568
|
+
<!-- Define clip path for chart area -->
|
|
1569
|
+
<defs>
|
|
1570
|
+
<clipPath id={clip_path_id}>
|
|
1571
|
+
<rect x={pad.l} y={pad.t} width={chart_width} height={chart_height} />
|
|
1572
|
+
</clipPath>
|
|
1573
|
+
</defs>
|
|
1574
|
+
|
|
1575
|
+
<!-- Clipped content: zero lines, bars, and lines -->
|
|
1576
|
+
<g clip-path="url(#{clip_path_id})">
|
|
1577
|
+
<ZeroLines
|
|
1578
|
+
{display}
|
|
1579
|
+
x_scale_fn={scales.x}
|
|
1580
|
+
x2_scale_fn={scales.x2}
|
|
1581
|
+
y_scale_fn={scales.y}
|
|
1582
|
+
y2_scale_fn={scales.y2}
|
|
1583
|
+
x_range={ranges.current.x}
|
|
1584
|
+
x2_range={ranges.current.x2}
|
|
1585
|
+
y_range={ranges.current.y}
|
|
1586
|
+
y2_range={ranges.current.y2}
|
|
1587
|
+
x_scale_type={x_axis.scale_type}
|
|
1588
|
+
x2_scale_type={x2_axis.scale_type}
|
|
1589
|
+
y_scale_type={y_axis.scale_type}
|
|
1590
|
+
y2_scale_type={y2_axis.scale_type}
|
|
1591
|
+
x_is_time={x_axis.format?.startsWith(`%`) ?? false}
|
|
1592
|
+
x2_is_time={x2_axis.format?.startsWith(`%`) ?? false}
|
|
1593
|
+
has_x2={x2_series.length > 0}
|
|
1594
|
+
has_y2={y2_series.length > 0}
|
|
1595
|
+
{width}
|
|
1596
|
+
{height}
|
|
1597
|
+
{pad}
|
|
1598
|
+
/>
|
|
1599
|
+
|
|
1600
|
+
<!-- Reference lines: below lines -->
|
|
1601
|
+
{@render ref_lines_layer(ref_lines_by_z.below_lines)}
|
|
1602
|
+
|
|
1603
|
+
<!-- Bars and Lines -->
|
|
1604
|
+
{#each internal_series as srs, series_idx (srs?.id ?? series_idx)}
|
|
1605
|
+
{#if srs?.visible ?? true}
|
|
1606
|
+
{@const is_line = srs.render_mode === `line`}
|
|
1607
|
+
<g
|
|
1608
|
+
class={is_line ? `line-series` : `bar-series`}
|
|
1609
|
+
data-series-idx={series_idx}
|
|
1610
|
+
opacity={hovered_legend_series_idx !== null &&
|
|
1611
|
+
hovered_legend_series_idx !== series_idx
|
|
1612
|
+
? 0.25
|
|
1613
|
+
: 1}
|
|
1614
|
+
>
|
|
1615
|
+
{#if is_line}
|
|
1616
|
+
<!-- Render as line -->
|
|
1617
|
+
{@const color = srs.color ?? line_state.color ?? `steelblue`}
|
|
1618
|
+
{@const stroke_width = srs.line_style?.stroke_width ?? line_state.width ?? 2}
|
|
1619
|
+
{@const line_dash = srs.line_style?.line_dash ?? `none`}
|
|
1620
|
+
{@const use_y2 = srs.y_axis === `y2`}
|
|
1621
|
+
{@const y_scale = use_y2 ? scales.y2 : scales.y}
|
|
1622
|
+
{@const use_x2 = srs.x_axis === `x2`}
|
|
1623
|
+
{@const x_scale = use_x2 ? scales.x2 : scales.x}
|
|
1624
|
+
{@const series_markers = srs.markers ?? DEFAULT_MARKERS}
|
|
1625
|
+
{@const show_line = series_markers === `line` ||
|
|
1626
|
+
series_markers === `line+points`}
|
|
1627
|
+
{@const show_points = series_markers === `points` ||
|
|
1628
|
+
series_markers === `line+points`}
|
|
1629
|
+
{@const points = srs.x.map((x_val, idx) => {
|
|
1630
|
+
const y_val = srs.y[idx]
|
|
1631
|
+
// Lines don't stack - they show absolute values (useful for totals/trends)
|
|
1632
|
+
const plot_x = orientation === `vertical`
|
|
1633
|
+
? x_scale(x_val)
|
|
1634
|
+
: x_scale(y_val)
|
|
1635
|
+
const plot_y = orientation === `vertical`
|
|
1636
|
+
? y_scale(y_val)
|
|
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))}
|
|
1665
|
+
{@const polyline_str = show_line && points.length > 1
|
|
1666
|
+
? points.map((pt) => `${pt.x},${pt.y}`).join(` `)
|
|
1667
|
+
: ``}
|
|
1668
|
+
{#if polyline_str}
|
|
1669
|
+
<polyline
|
|
1670
|
+
points={polyline_str}
|
|
1671
|
+
fill="none"
|
|
1672
|
+
stroke={color}
|
|
1673
|
+
stroke-width={stroke_width}
|
|
1674
|
+
stroke-dasharray={line_dash}
|
|
1675
|
+
stroke-linejoin="round"
|
|
1676
|
+
stroke-linecap="round"
|
|
1677
|
+
/>
|
|
1678
|
+
{/if}
|
|
1679
|
+
{#if polyline_str && !show_points && (on_bar_hover || on_bar_click)}
|
|
1680
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
1681
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
1682
|
+
<polyline
|
|
1683
|
+
points={polyline_str}
|
|
1684
|
+
fill="none"
|
|
1685
|
+
stroke="transparent"
|
|
1686
|
+
stroke-width={Math.max(10, stroke_width * 3)}
|
|
1687
|
+
stroke-linejoin="round"
|
|
1688
|
+
stroke-linecap="round"
|
|
1689
|
+
style:cursor={on_bar_click ? `pointer` : undefined}
|
|
1690
|
+
onmousemove={(evt) => {
|
|
1691
|
+
const pt = find_closest_point(evt, points)
|
|
1692
|
+
if (!pt) return
|
|
1693
|
+
hovered = true
|
|
1694
|
+
const fill = line_point_fill(pt, color)
|
|
1695
|
+
hover_info = get_bar_data(series_idx, pt.idx, fill)
|
|
1696
|
+
change(hover_info)
|
|
1697
|
+
on_bar_hover?.({ ...hover_info!, event: evt })
|
|
1698
|
+
}}
|
|
1699
|
+
onmouseleave={() => {
|
|
1700
|
+
change(null)
|
|
1701
|
+
hover_info = null
|
|
1702
|
+
on_bar_hover?.(null)
|
|
1703
|
+
}}
|
|
1704
|
+
onclick={(evt) => {
|
|
1705
|
+
const pt = find_closest_point(evt, points)
|
|
1706
|
+
if (!pt) return
|
|
1707
|
+
const fill = line_point_fill(pt, color)
|
|
1708
|
+
const bar_data = get_bar_data(series_idx, pt.idx, fill)
|
|
1709
|
+
on_bar_click?.({ ...bar_data, event: evt })
|
|
1710
|
+
}}
|
|
1711
|
+
/>
|
|
1712
|
+
{/if}
|
|
1713
|
+
{#if show_points}
|
|
1714
|
+
{@const clickable = on_bar_click || on_point_click}
|
|
1715
|
+
{@const get_pt = (evt: Event) => {
|
|
1716
|
+
const attr = evt.target instanceof Element
|
|
1717
|
+
? evt.target.closest(`[data-bar-idx]`)?.getAttribute(
|
|
1718
|
+
`data-bar-idx`,
|
|
1719
|
+
)
|
|
1720
|
+
: null
|
|
1721
|
+
return points.find((pt) => pt.idx === parseInt(attr ?? ``, 10))
|
|
1722
|
+
}}
|
|
1723
|
+
{@const set_hover = (
|
|
1724
|
+
pt: LineSeriesPoint | null,
|
|
1725
|
+
evt: MouseEvent | FocusEvent,
|
|
1726
|
+
) => {
|
|
1727
|
+
if (pt) {
|
|
1728
|
+
hovered = true
|
|
1729
|
+
const fill = line_point_fill(pt, color)
|
|
1730
|
+
hover_info = get_bar_data(series_idx, pt.idx, fill)
|
|
1731
|
+
change(hover_info)
|
|
1732
|
+
} else {
|
|
1733
|
+
change(null)
|
|
1734
|
+
hover_info = null
|
|
1735
|
+
}
|
|
1736
|
+
on_bar_hover?.(pt ? { ...hover_info!, event: evt } : null)
|
|
1737
|
+
on_point_hover?.(
|
|
1738
|
+
pt ? { ...hover_info!, event: evt, point: pt } : null,
|
|
1739
|
+
)
|
|
1740
|
+
}}
|
|
1741
|
+
{@const do_click = (
|
|
1742
|
+
pt: LineSeriesPoint,
|
|
1743
|
+
evt: MouseEvent | KeyboardEvent,
|
|
1744
|
+
) => {
|
|
1745
|
+
const fill = line_point_fill(pt, color)
|
|
1746
|
+
const bar_data = get_bar_data(series_idx, pt.idx, fill)
|
|
1747
|
+
on_bar_click?.({ ...bar_data, event: evt })
|
|
1748
|
+
on_point_click?.({ ...bar_data, event: evt, point: pt })
|
|
1749
|
+
}}
|
|
1750
|
+
{@const leaving = (evt: MouseEvent | FocusEvent) =>
|
|
1751
|
+
(evt.relatedTarget instanceof Element
|
|
1752
|
+
? evt.relatedTarget.closest(`.line-points`)
|
|
1753
|
+
: null) !== evt.currentTarget}
|
|
1754
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions, a11y_mouse_events_have_key_events -->
|
|
1755
|
+
<g
|
|
1756
|
+
class="line-points"
|
|
1757
|
+
role="group"
|
|
1758
|
+
onmouseover={(evt) => {
|
|
1759
|
+
const pt = get_pt(evt)
|
|
1760
|
+
if (pt) set_hover(pt, evt)
|
|
1761
|
+
}}
|
|
1762
|
+
onfocusin={(evt) => {
|
|
1763
|
+
const pt = get_pt(evt)
|
|
1764
|
+
if (pt) set_hover(pt, evt)
|
|
1765
|
+
}}
|
|
1766
|
+
onmouseout={(evt) => {
|
|
1767
|
+
if (leaving(evt)) set_hover(null, evt)
|
|
1768
|
+
}}
|
|
1769
|
+
onfocusout={(evt) => {
|
|
1770
|
+
if (leaving(evt)) set_hover(null, evt)
|
|
1771
|
+
}}
|
|
1772
|
+
onclick={(evt) => {
|
|
1773
|
+
const pt = get_pt(evt)
|
|
1774
|
+
if (pt && clickable) do_click(pt, evt)
|
|
1775
|
+
}}
|
|
1776
|
+
onkeydown={(evt) => {
|
|
1777
|
+
const pt = get_pt(evt)
|
|
1778
|
+
if (pt && clickable && (evt.key === `Enter` || evt.key === ` `)) {
|
|
1779
|
+
evt.preventDefault()
|
|
1780
|
+
do_click(pt, evt)
|
|
1781
|
+
}
|
|
1782
|
+
}}
|
|
1783
|
+
>
|
|
1784
|
+
{#each points as pt (pt.idx)}
|
|
1785
|
+
{@const sty = pt.point_style}
|
|
1786
|
+
{@const fl = line_point_fill(pt, color)}
|
|
1787
|
+
{@const rad = pt.size_value != null
|
|
1788
|
+
? size_scale_fn(pt.size_value)
|
|
1789
|
+
: sty?.radius ?? 4}
|
|
1790
|
+
{@const hov = hover_info?.series_idx === series_idx &&
|
|
1791
|
+
hover_info?.bar_idx === pt.idx}
|
|
1792
|
+
<ScatterPoint
|
|
1793
|
+
x={pt.x}
|
|
1794
|
+
y={pt.y}
|
|
1795
|
+
is_hovered={hov}
|
|
1796
|
+
{point_tween}
|
|
1797
|
+
style={{
|
|
1798
|
+
...sty,
|
|
1799
|
+
radius: rad,
|
|
1800
|
+
fill: fl,
|
|
1801
|
+
stroke: sty?.stroke ?? `transparent`,
|
|
1802
|
+
stroke_width: sty?.stroke_width ?? 1,
|
|
1803
|
+
fill_opacity: sty?.fill_opacity ?? 1,
|
|
1804
|
+
stroke_opacity: sty?.stroke_opacity ?? 1,
|
|
1805
|
+
cursor: clickable ? `pointer` : undefined,
|
|
1806
|
+
}}
|
|
1807
|
+
hover={pt.point_hover ?? {}}
|
|
1808
|
+
label={pt.point_label ?? {}}
|
|
1809
|
+
offset={pt.point_offset ?? { x: 0, y: 0 }}
|
|
1810
|
+
origin={{ x: plot_center_x, y: plot_center_y }}
|
|
1811
|
+
--point-fill-color={fl}
|
|
1812
|
+
data-bar-idx={pt.idx}
|
|
1813
|
+
tabindex={clickable ? (hov ? 0 : -1) : undefined}
|
|
1814
|
+
/>
|
|
1815
|
+
{/each}
|
|
1816
|
+
</g>
|
|
1817
|
+
{/if}
|
|
1818
|
+
{:else}
|
|
1819
|
+
<!-- Render as bars -->
|
|
1820
|
+
{#each srs.x as x_val, bar_idx (bar_idx)}
|
|
1821
|
+
{@const y_val = srs.y[bar_idx]}
|
|
1822
|
+
{@const base = mode === `stacked`
|
|
1823
|
+
? (stacked_offsets[series_idx]?.[bar_idx] ?? 0)
|
|
1824
|
+
: 0}
|
|
1825
|
+
{@const color = srs.color ?? bar_state.color ?? `steelblue`}
|
|
1826
|
+
{@const bar_width_val = Array.isArray(srs.bar_width)
|
|
1827
|
+
? (srs.bar_width[bar_idx] ?? 0.5)
|
|
1828
|
+
: (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
|
+
{@const is_vertical = orientation === `vertical`}
|
|
1841
|
+
{@const cat_val = x_val}
|
|
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}
|
|
1847
|
+
{@const [cat_scale, val_scale] = is_vertical
|
|
1848
|
+
? [x_scale_bar, y_scale]
|
|
1849
|
+
: [scales.y, x_scale_bar]}
|
|
1850
|
+
{@const c0 = cat_scale(cat_val + group_offset - half)}
|
|
1851
|
+
{@const c1 = cat_scale(cat_val + group_offset + half)}
|
|
1852
|
+
{@const v0 = val_scale(base)}
|
|
1853
|
+
{@const v1 = val_scale(base + val)}
|
|
1854
|
+
{@const [rect_x, rect_y] = is_vertical
|
|
1855
|
+
? [Math.min(c0, c1), Math.min(v0, v1)]
|
|
1856
|
+
: [Math.min(v0, v1), Math.min(c0, c1)]}
|
|
1857
|
+
{@const [rect_w, rect_h] = is_vertical
|
|
1858
|
+
? [Math.max(1, Math.abs(c1 - c0)), Math.max(0, Math.abs(v1 - v0))]
|
|
1859
|
+
: [Math.max(1, Math.abs(v1 - v0)), Math.max(0, Math.abs(c1 - c0))]}
|
|
1860
|
+
{#if (is_vertical ? rect_h : rect_w) > 0}
|
|
1861
|
+
<path
|
|
1862
|
+
d={bar_path(
|
|
1863
|
+
rect_x,
|
|
1864
|
+
rect_y,
|
|
1865
|
+
rect_w,
|
|
1866
|
+
rect_h,
|
|
1867
|
+
Math.min(bar_state.border_radius ?? 0, rect_w / 2, rect_h / 2),
|
|
1868
|
+
is_vertical,
|
|
1869
|
+
)}
|
|
1870
|
+
fill={color}
|
|
1871
|
+
opacity={mode === `overlay` ? bar_state.opacity : 1}
|
|
1872
|
+
stroke={bar_state.stroke_color}
|
|
1873
|
+
stroke-opacity={bar_state.stroke_opacity}
|
|
1874
|
+
stroke-width={bar_state.stroke_width}
|
|
1875
|
+
role="button"
|
|
1876
|
+
tabindex="0"
|
|
1877
|
+
aria-label={`bar ${bar_idx + 1} of ${srs.label ?? `series`}`}
|
|
1878
|
+
style:cursor={on_bar_click ? `pointer` : undefined}
|
|
1879
|
+
onmousemove={handle_bar_hover(series_idx, bar_idx, color)}
|
|
1880
|
+
onmouseleave={() => {
|
|
1881
|
+
hover_info = null
|
|
1882
|
+
change(null)
|
|
1883
|
+
on_bar_hover?.(null)
|
|
1884
|
+
}}
|
|
1885
|
+
onclick={(evt) =>
|
|
1886
|
+
on_bar_click?.({
|
|
1887
|
+
...get_bar_data(series_idx, bar_idx, color),
|
|
1888
|
+
event: evt,
|
|
1889
|
+
})}
|
|
1890
|
+
onkeydown={(evt) => {
|
|
1891
|
+
if (evt.key === `Enter` || evt.key === ` `) {
|
|
1892
|
+
evt.preventDefault()
|
|
1893
|
+
on_bar_click?.({
|
|
1894
|
+
...get_bar_data(series_idx, bar_idx, color),
|
|
1895
|
+
event: evt,
|
|
1896
|
+
})
|
|
1897
|
+
}
|
|
1898
|
+
}}
|
|
1899
|
+
/>
|
|
1900
|
+
{#if srs.labels?.[bar_idx]}
|
|
1901
|
+
<text
|
|
1902
|
+
x={is_vertical ? (c0 + c1) / 2 : Math.max(v0, v1) + 4}
|
|
1903
|
+
y={is_vertical ? Math.max(0, Math.min(v0, v1) - 6) : (c0 + c1) / 2}
|
|
1904
|
+
text-anchor={is_vertical ? `middle` : undefined}
|
|
1905
|
+
dominant-baseline={is_vertical ? undefined : `central`}
|
|
1906
|
+
class="bar-label"
|
|
1907
|
+
>
|
|
1908
|
+
{srs.labels[bar_idx]}
|
|
1909
|
+
</text>
|
|
1910
|
+
{/if}
|
|
1911
|
+
{/if}
|
|
1912
|
+
{/each}
|
|
1913
|
+
{/if}
|
|
1914
|
+
</g>
|
|
1915
|
+
{/if}
|
|
1916
|
+
{/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
|
+
</g>
|
|
1924
|
+
</svg>
|
|
1925
|
+
|
|
1926
|
+
<!-- Legend -->
|
|
1927
|
+
{#if legend && (show_legend !== undefined ? show_legend : series.length > 1)}
|
|
1928
|
+
{@const legend_left = legend_auto_outside
|
|
1929
|
+
? legend_outside_x
|
|
1930
|
+
: legend_placement
|
|
1931
|
+
? tweened_legend_coords.current.x
|
|
1932
|
+
: pad.l + 10}
|
|
1933
|
+
{@const legend_top = legend_auto_outside
|
|
1934
|
+
? legend_outside_y
|
|
1935
|
+
: legend_placement
|
|
1936
|
+
? tweened_legend_coords.current.y
|
|
1937
|
+
: pad.t + 10}
|
|
1938
|
+
<PlotLegend
|
|
1939
|
+
bind:root_element={legend_element}
|
|
1940
|
+
{...legend}
|
|
1941
|
+
series_data={legend_data}
|
|
1942
|
+
on_toggle={legend?.on_toggle || toggle_series_visibility}
|
|
1943
|
+
on_group_toggle={legend?.on_group_toggle || toggle_group_visibility}
|
|
1944
|
+
on_hover_change={legend_hover.set_locked}
|
|
1945
|
+
on_item_hover={(series_idx) =>
|
|
1946
|
+
(hovered_legend_series_idx = series_idx != null && series_idx >= 0
|
|
1947
|
+
? series_idx
|
|
1948
|
+
: null)}
|
|
1949
|
+
active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
|
|
1950
|
+
style={`
|
|
1951
|
+
position: absolute;
|
|
1952
|
+
left: ${legend_left}px;
|
|
1953
|
+
top: ${legend_top}px;
|
|
1954
|
+
pointer-events: auto;
|
|
1955
|
+
${legend?.style || ``}
|
|
1956
|
+
`}
|
|
1957
|
+
/>
|
|
1958
|
+
{/if}
|
|
1959
|
+
|
|
1960
|
+
{#if hover_info && hovered}
|
|
1961
|
+
{@const cx = (hover_info.active_x_axis === `x2` ? scales.x2 : scales.x)(
|
|
1962
|
+
hover_info.orient_x,
|
|
1963
|
+
)}
|
|
1964
|
+
{@const cy = (hover_info.active_y_axis === `y2` ? scales.y2 : scales.y)(
|
|
1965
|
+
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
|
+
)}
|
|
1976
|
+
<PlotTooltip
|
|
1977
|
+
x={tooltip_pos.x}
|
|
1978
|
+
y={tooltip_pos.y}
|
|
1979
|
+
offset={{ x: 0, y: 0 }}
|
|
1980
|
+
bg_color={hover_info.color}
|
|
1981
|
+
bind:wrapper={tooltip_el}
|
|
1982
|
+
>
|
|
1983
|
+
{#if tooltip}
|
|
1984
|
+
{@render tooltip({ ...hover_info, fullscreen })}
|
|
1985
|
+
{:else}
|
|
1986
|
+
{@const series_label = series[hover_info.series_idx]?.label}
|
|
1987
|
+
{#if series.length > 1 && series_label}
|
|
1988
|
+
<div><strong>{series_label}</strong></div>
|
|
1989
|
+
{/if}
|
|
1990
|
+
<div>
|
|
1991
|
+
{@html sanitize_html(hover_info.x_axis.label || `x`)}: {
|
|
1992
|
+
(cat_axis === `x` ? hover_info.category_label : undefined) ??
|
|
1993
|
+
format_value(hover_info.orient_x, hover_info.x_axis.format || `.3~s`)
|
|
1994
|
+
}
|
|
1995
|
+
</div>
|
|
1996
|
+
<div>
|
|
1997
|
+
{@html sanitize_html(hover_info.y_axis.label || `y`)}: {
|
|
1998
|
+
(cat_axis === `y` ? hover_info.category_label : undefined) ??
|
|
1999
|
+
format_value(hover_info.orient_y, hover_info.y_axis.format || `.3~s`)
|
|
2000
|
+
}
|
|
2001
|
+
</div>
|
|
2002
|
+
{/if}
|
|
2003
|
+
</PlotTooltip>
|
|
2004
|
+
{/if}
|
|
2005
|
+
|
|
2006
|
+
{#if show_controls}
|
|
2007
|
+
<BarPlotControls
|
|
2008
|
+
toggle_props={{
|
|
2009
|
+
...controls_toggle_props,
|
|
2010
|
+
style: `--ctrl-btn-right: var(--fullscreen-btn-offset, 30px); ${
|
|
2011
|
+
controls_toggle_props?.style ?? ``
|
|
2012
|
+
}`,
|
|
2013
|
+
}}
|
|
2014
|
+
pane_props={controls_pane_props}
|
|
2015
|
+
bind:show_controls
|
|
2016
|
+
bind:controls_open
|
|
2017
|
+
bind:orientation
|
|
2018
|
+
bind:mode
|
|
2019
|
+
bind:x_axis
|
|
2020
|
+
bind:x2_axis
|
|
2021
|
+
bind:y_axis
|
|
2022
|
+
bind:y2_axis
|
|
2023
|
+
bind:display
|
|
2024
|
+
auto_x_range={auto_ranges.x as Vec2}
|
|
2025
|
+
auto_x2_range={auto_ranges.x2 as Vec2}
|
|
2026
|
+
auto_y_range={auto_ranges.y as Vec2}
|
|
2027
|
+
auto_y2_range={auto_ranges.y2 as Vec2}
|
|
2028
|
+
has_x2_points={x2_series.length > 0}
|
|
2029
|
+
has_y2_points={y2_series.length > 0}
|
|
2030
|
+
children={controls_extra}
|
|
2031
|
+
/>
|
|
2032
|
+
{/if}
|
|
2033
|
+
{/if}
|
|
2034
|
+
|
|
2035
|
+
<!-- User-provided children (e.g. for custom absolutely-positioned overlays) -->
|
|
2036
|
+
{@render children?.({ height, width, fullscreen })}
|
|
2037
|
+
</div>
|
|
2038
|
+
|
|
2039
|
+
<style>
|
|
2040
|
+
.bar-plot {
|
|
2041
|
+
position: relative;
|
|
2042
|
+
width: 100%;
|
|
2043
|
+
height: var(--barplot-height, auto);
|
|
2044
|
+
min-height: var(--barplot-min-height, 300px);
|
|
2045
|
+
container-type: size;
|
|
2046
|
+
z-index: var(--barplot-z-index, auto);
|
|
2047
|
+
border-radius: var(--barplot-border-radius, var(--border-radius, 3pt));
|
|
2048
|
+
flex: var(--barplot-flex, 1);
|
|
2049
|
+
display: var(--barplot-display, flex);
|
|
2050
|
+
flex-direction: column;
|
|
2051
|
+
background: var(--barplot-bg, var(--plot-bg));
|
|
2052
|
+
}
|
|
2053
|
+
.bar-plot.fullscreen {
|
|
2054
|
+
position: fixed;
|
|
2055
|
+
top: 0;
|
|
2056
|
+
left: 0;
|
|
2057
|
+
width: 100vw !important;
|
|
2058
|
+
height: 100vh !important;
|
|
2059
|
+
/* Must be higher than Structure.svelte's --struct-buttons-z-index. */
|
|
2060
|
+
z-index: var(--barplot-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
|
|
2061
|
+
margin: 0;
|
|
2062
|
+
border-radius: 0;
|
|
2063
|
+
background: var(--barplot-fullscreen-bg, var(--barplot-bg, var(--plot-bg)));
|
|
2064
|
+
max-height: none !important;
|
|
2065
|
+
overflow: hidden;
|
|
2066
|
+
/* Add padding to prevent titles from being cropped at top */
|
|
2067
|
+
padding-top: var(--plot-fullscreen-padding-top, 2em);
|
|
2068
|
+
box-sizing: border-box;
|
|
2069
|
+
}
|
|
2070
|
+
.header-controls {
|
|
2071
|
+
position: absolute;
|
|
2072
|
+
top: var(--ctrl-btn-top, 5pt);
|
|
2073
|
+
right: var(--fullscreen-btn-right, 4px);
|
|
2074
|
+
z-index: var(--fullscreen-btn-z-index, 10);
|
|
2075
|
+
display: flex;
|
|
2076
|
+
align-items: center;
|
|
2077
|
+
gap: 8px;
|
|
2078
|
+
}
|
|
2079
|
+
.header-controls :global(.fullscreen-toggle) {
|
|
2080
|
+
position: static; /* Override absolute positioning since container handles it */
|
|
2081
|
+
opacity: 1; /* Always visible when inside header-controls, container controls visibility */
|
|
2082
|
+
}
|
|
2083
|
+
/* Hide controls and fullscreen toggles by default, show on hover */
|
|
2084
|
+
.bar-plot :global(.pane-toggle),
|
|
2085
|
+
.bar-plot .header-controls {
|
|
2086
|
+
opacity: 0;
|
|
2087
|
+
transition: opacity 0.2s, background-color 0.2s;
|
|
2088
|
+
}
|
|
2089
|
+
.bar-plot:hover :global(.pane-toggle),
|
|
2090
|
+
.bar-plot:hover .header-controls,
|
|
2091
|
+
.bar-plot :global(.pane-toggle:focus-visible),
|
|
2092
|
+
.bar-plot :global(.pane-toggle[aria-expanded='true']),
|
|
2093
|
+
.bar-plot .header-controls:focus-within {
|
|
2094
|
+
opacity: 1;
|
|
2095
|
+
}
|
|
2096
|
+
svg {
|
|
2097
|
+
width: var(--barplot-svg-width, 100%);
|
|
2098
|
+
height: var(--barplot-svg-height, 100%);
|
|
2099
|
+
flex: var(--barplot-svg-flex, 1);
|
|
2100
|
+
overflow: var(--barplot-svg-overflow, visible);
|
|
2101
|
+
fill: var(--text-color);
|
|
2102
|
+
font-weight: var(--scatter-font-weight);
|
|
2103
|
+
font-size: var(--scatter-font-size);
|
|
2104
|
+
}
|
|
2105
|
+
.bar-plot.dragover {
|
|
2106
|
+
border: var(--barplot-dragover-border, var(--dragover-border));
|
|
2107
|
+
background-color: var(--barplot-dragover-bg, var(--dragover-bg));
|
|
2108
|
+
}
|
|
2109
|
+
.bar-label {
|
|
2110
|
+
fill: var(--text-color);
|
|
2111
|
+
font-size: 11px;
|
|
2112
|
+
}
|
|
2113
|
+
</style>
|