matterviz 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/dist/EmptyState.svelte +10 -4
  2. package/dist/FilePicker.svelte +12 -14
  3. package/dist/Icon.svelte +7 -4
  4. package/dist/MillerIndexInput.svelte +2 -2
  5. package/dist/app.css +53 -0
  6. package/dist/brillouin/BrillouinZone.svelte +75 -85
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
  8. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  9. package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
  10. package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
  11. package/dist/brillouin/BrillouinZoneScene.svelte +26 -39
  12. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  13. package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
  14. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +62 -74
  15. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
  16. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +511 -547
  17. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
  18. package/dist/chempot-diagram/async-compute.svelte.js +1 -1
  19. package/dist/chempot-diagram/controls-state.svelte.js +1 -1
  20. package/dist/colors/index.d.ts +0 -4
  21. package/dist/colors/index.js +19 -20
  22. package/dist/composition/BarChart.svelte +37 -31
  23. package/dist/composition/BubbleChart.svelte +17 -17
  24. package/dist/composition/Composition.svelte +7 -11
  25. package/dist/composition/Formula.svelte +18 -25
  26. package/dist/composition/FormulaFilter.svelte +92 -95
  27. package/dist/composition/PieChart.svelte +55 -41
  28. package/dist/composition/format.js +1 -1
  29. package/dist/composition/index.d.ts +2 -0
  30. package/dist/composition/index.js +14 -0
  31. package/dist/composition/parse.d.ts +2 -2
  32. package/dist/composition/parse.js +11 -10
  33. package/dist/constants.js +11 -9
  34. package/dist/convex-hull/ConvexHull.svelte +12 -15
  35. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  36. package/dist/convex-hull/ConvexHull2D.svelte +93 -92
  37. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  38. package/dist/convex-hull/ConvexHull3D.svelte +109 -140
  39. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  40. package/dist/convex-hull/ConvexHull4D.svelte +75 -94
  41. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  42. package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
  43. package/dist/convex-hull/ConvexHullControls.svelte +46 -48
  44. package/dist/convex-hull/ConvexHullStats.svelte +193 -195
  45. package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
  46. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  47. package/dist/convex-hull/StructurePopup.svelte +17 -21
  48. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  49. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  50. package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
  51. package/dist/convex-hull/helpers.d.ts +8 -10
  52. package/dist/convex-hull/helpers.js +36 -36
  53. package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
  54. package/dist/convex-hull/thermodynamics.js +18 -17
  55. package/dist/coordination/CoordinationBarPlot.svelte +38 -33
  56. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  57. package/dist/coordination/calc-coordination.d.ts +1 -1
  58. package/dist/coordination/calc-coordination.js +28 -25
  59. package/dist/element/BohrAtom.svelte +5 -8
  60. package/dist/element/ElementHeading.svelte +4 -1
  61. package/dist/element/ElementPhoto.svelte +11 -9
  62. package/dist/element/ElementStats.svelte +12 -10
  63. package/dist/element/ElementTile.svelte +24 -29
  64. package/dist/element/Nucleus.svelte +4 -2
  65. package/dist/element/data.d.ts +1 -2
  66. package/dist/element/helpers.d.ts +4 -0
  67. package/dist/element/helpers.js +18 -0
  68. package/dist/element/index.d.ts +1 -4
  69. package/dist/element/index.js +3 -18
  70. package/dist/feedback/DragOverlay.svelte +5 -5
  71. package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
  72. package/dist/feedback/StatusMessage.svelte +7 -4
  73. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  74. package/dist/fermi-surface/FermiSurface.svelte +14 -22
  75. package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -7
  76. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  77. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  78. package/dist/fermi-surface/FermiSurfaceScene.svelte +37 -64
  79. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  80. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  81. package/dist/fermi-surface/compute.js +3 -3
  82. package/dist/fermi-surface/parse.js +22 -24
  83. package/dist/heatmap-matrix/HeatmapMatrix.svelte +109 -148
  84. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
  85. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
  86. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  87. package/dist/heatmap-matrix/index.d.ts +5 -0
  88. package/dist/icons.d.ts +4 -0
  89. package/dist/icons.js +4 -0
  90. package/dist/io/ExportPane.svelte +10 -6
  91. package/dist/io/decompress.d.ts +2 -1
  92. package/dist/io/decompress.js +22 -35
  93. package/dist/io/export.d.ts +4 -0
  94. package/dist/io/export.js +5 -1
  95. package/dist/io/fetch.d.ts +0 -3
  96. package/dist/io/fetch.js +0 -11
  97. package/dist/io/index.d.ts +0 -1
  98. package/dist/io/index.js +2 -9
  99. package/dist/io/is-binary.d.ts +9 -0
  100. package/dist/io/is-binary.js +45 -1
  101. package/dist/io/url-drop.js +13 -25
  102. package/dist/isosurface/Isosurface.svelte +17 -23
  103. package/dist/isosurface/IsosurfaceControls.svelte +37 -42
  104. package/dist/isosurface/parse-vaspwave.d.ts +3 -0
  105. package/dist/isosurface/parse-vaspwave.js +138 -0
  106. package/dist/isosurface/parse.js +6 -6
  107. package/dist/labels.d.ts +6 -2
  108. package/dist/labels.js +43 -13
  109. package/dist/layout/FullscreenButton.svelte +2 -1
  110. package/dist/layout/FullscreenToggle.svelte +10 -4
  111. package/dist/layout/InfoCard.svelte +7 -18
  112. package/dist/layout/InfoTag.svelte +1 -3
  113. package/dist/layout/NumberRangeInput.svelte +46 -0
  114. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  115. package/dist/layout/PropertyFilter.svelte +2 -6
  116. package/dist/layout/SettingsSection.svelte +21 -17
  117. package/dist/layout/SubpageGrid.svelte +3 -1
  118. package/dist/layout/ViewerChrome.svelte +9 -10
  119. package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
  120. package/dist/layout/fullscreen.js +3 -5
  121. package/dist/layout/fullscreen.svelte.js +1 -2
  122. package/dist/layout/index.d.ts +1 -0
  123. package/dist/layout/index.js +1 -0
  124. package/dist/layout/json-tree/JsonNode.svelte +8 -11
  125. package/dist/layout/json-tree/JsonTree.svelte +53 -77
  126. package/dist/layout/json-tree/JsonValue.svelte +7 -15
  127. package/dist/layout/json-tree/utils.js +14 -23
  128. package/dist/math.d.ts +2 -0
  129. package/dist/math.js +19 -0
  130. package/dist/overlays/ContextMenu.svelte +4 -2
  131. package/dist/overlays/CopyButton.svelte +11 -5
  132. package/dist/overlays/DragControlTab.svelte +6 -1
  133. package/dist/overlays/DraggablePane.svelte +81 -26
  134. package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
  135. package/dist/overlays/GlassChip.svelte +30 -0
  136. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  137. package/dist/overlays/InfoPaneCards.svelte +11 -23
  138. package/dist/overlays/index.d.ts +7 -0
  139. package/dist/overlays/index.js +26 -0
  140. package/dist/overlays/portal.d.ts +2 -0
  141. package/dist/overlays/portal.js +14 -0
  142. package/dist/periodic-table/PeriodicTable.svelte +113 -145
  143. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  144. package/dist/periodic-table/PeriodicTableControls.svelte +11 -63
  145. package/dist/periodic-table/TableInset.svelte +9 -4
  146. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
  147. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  148. package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
  149. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  150. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  151. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
  152. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  153. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  154. package/dist/phase-diagram/build-diagram.js +3 -3
  155. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  156. package/dist/phase-diagram/parse.js +15 -15
  157. package/dist/phase-diagram/svg-to-diagram.js +21 -15
  158. package/dist/phase-diagram/utils.js +2 -3
  159. package/dist/plot/bar/BarPlot.svelte +430 -391
  160. package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
  161. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  162. package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
  163. package/dist/plot/bar/SpacegroupBarPlot.svelte +29 -36
  164. package/dist/plot/bar/data.d.ts +5 -4
  165. package/dist/plot/bar/data.js +2 -1
  166. package/dist/plot/box/BoxPlot.svelte +301 -209
  167. package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
  168. package/dist/plot/box/Violin.svelte.d.ts +1 -0
  169. package/dist/plot/box/box-plot.js +1 -1
  170. package/dist/plot/core/auto-place.js +1 -1
  171. package/dist/plot/core/axis-utils.d.ts +12 -7
  172. package/dist/plot/core/axis-utils.js +48 -48
  173. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  174. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  175. package/dist/plot/core/components/ColorBar.svelte +101 -106
  176. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  177. package/dist/plot/core/components/FillArea.svelte +10 -17
  178. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  179. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  180. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  181. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  182. package/dist/plot/core/components/Line.svelte +18 -9
  183. package/dist/plot/core/components/Line.svelte.d.ts +2 -0
  184. package/dist/plot/core/components/PlotAxis.svelte +40 -17
  185. package/dist/plot/core/components/PlotControls.svelte +65 -41
  186. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  187. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  188. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  189. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  190. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  191. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  192. package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
  193. package/dist/plot/core/components/ReferencePlane.svelte +7 -8
  194. package/dist/plot/core/components/ZeroLines.svelte +4 -8
  195. package/dist/plot/core/components/ZoomRect.svelte +1 -2
  196. package/dist/plot/core/components/index.d.ts +1 -0
  197. package/dist/plot/core/components/index.js +1 -0
  198. package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
  199. package/dist/plot/core/data-cleaning-signal.js +467 -0
  200. package/dist/plot/core/data-cleaning.d.ts +2 -42
  201. package/dist/plot/core/data-cleaning.js +27 -474
  202. package/dist/plot/core/data-transform.js +9 -5
  203. package/dist/plot/core/fill-utils.d.ts +3 -1
  204. package/dist/plot/core/fill-utils.js +28 -6
  205. package/dist/plot/core/index.d.ts +1 -0
  206. package/dist/plot/core/index.js +1 -0
  207. package/dist/plot/core/interactions.js +0 -2
  208. package/dist/plot/core/layout.d.ts +3 -1
  209. package/dist/plot/core/layout.js +22 -13
  210. package/dist/plot/core/marginals.d.ts +170 -0
  211. package/dist/plot/core/marginals.js +436 -0
  212. package/dist/plot/core/reference-line.js +1 -1
  213. package/dist/plot/core/scales.d.ts +23 -0
  214. package/dist/plot/core/scales.js +39 -13
  215. package/dist/plot/core/types/fills.d.ts +101 -0
  216. package/dist/plot/core/types/fills.js +15 -0
  217. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  218. package/dist/plot/core/types/plot-3d.js +4 -0
  219. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  220. package/dist/plot/core/types/reference-lines.js +9 -0
  221. package/dist/plot/core/types.d.ts +29 -324
  222. package/dist/plot/core/types.js +23 -20
  223. package/dist/plot/core/utils/hierarchy-chart.d.ts +51 -0
  224. package/dist/plot/core/utils/hierarchy-chart.js +179 -0
  225. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  226. package/dist/plot/core/utils/hierarchy-labels.js +99 -0
  227. package/dist/plot/core/utils/label-placement.js +2 -2
  228. package/dist/plot/histogram/Histogram.svelte +304 -325
  229. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  230. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  231. package/dist/plot/histogram/histogram.d.ts +31 -0
  232. package/dist/plot/histogram/histogram.js +70 -0
  233. package/dist/plot/histogram/index.d.ts +1 -0
  234. package/dist/plot/histogram/index.js +1 -0
  235. package/dist/plot/index.d.ts +1 -0
  236. package/dist/plot/index.js +1 -0
  237. package/dist/plot/sankey/Sankey.svelte +78 -69
  238. package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
  239. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  240. package/dist/plot/scatter/BinnedScatterPlot.svelte +263 -109
  241. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
  242. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  243. package/dist/plot/scatter/ScatterPlot.svelte +477 -385
  244. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
  245. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  246. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  247. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  248. package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
  249. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  250. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  251. package/dist/plot/scatter/scatter-data.js +8 -7
  252. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
  253. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
  254. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
  255. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
  256. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
  257. package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
  258. package/dist/plot/sunburst/Sunburst.svelte +251 -329
  259. package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
  260. package/dist/plot/sunburst/index.d.ts +1 -1
  261. package/dist/plot/sunburst/index.js +2 -1
  262. package/dist/plot/sunburst/render.d.ts +2 -2
  263. package/dist/plot/sunburst/render.js +47 -22
  264. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  265. package/dist/plot/sunburst/sunburst.js +10 -3
  266. package/dist/plot/treemap/Treemap.svelte +907 -0
  267. package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
  268. package/dist/plot/treemap/index.d.ts +3 -0
  269. package/dist/plot/treemap/index.js +4 -0
  270. package/dist/plot/treemap/treemap.d.ts +26 -0
  271. package/dist/plot/treemap/treemap.js +99 -0
  272. package/dist/rdf/RdfPlot.svelte +22 -26
  273. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  274. package/dist/rdf/calc-rdf.js +4 -8
  275. package/dist/sanitize.js +1 -1
  276. package/dist/scene/bind-renderer.svelte.js +14 -6
  277. package/dist/settings.d.ts +19 -9
  278. package/dist/settings.js +77 -50
  279. package/dist/spectral/Bands.svelte +223 -230
  280. package/dist/spectral/BandsAndDos.svelte +11 -9
  281. package/dist/spectral/BrillouinBandsDos.svelte +19 -27
  282. package/dist/spectral/Dos.svelte +70 -73
  283. package/dist/spectral/helpers.d.ts +3 -3
  284. package/dist/spectral/helpers.js +25 -35
  285. package/dist/state.svelte.js +5 -18
  286. package/dist/structure/Arrow.svelte +5 -18
  287. package/dist/structure/ArrowInstances.svelte +160 -0
  288. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  289. package/dist/structure/AtomLegend.svelte +115 -75
  290. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  291. package/dist/structure/Bond.svelte +31 -23
  292. package/dist/structure/CanvasTooltip.svelte +15 -3
  293. package/dist/structure/CellSelect.svelte +11 -26
  294. package/dist/structure/Cylinder.svelte +24 -40
  295. package/dist/structure/InstancedAtoms.svelte +114 -0
  296. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  297. package/dist/structure/Lattice.svelte +85 -149
  298. package/dist/structure/SiteLabels.svelte +122 -0
  299. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  300. package/dist/structure/Structure.svelte +473 -419
  301. package/dist/structure/Structure.svelte.d.ts +4 -2
  302. package/dist/structure/StructureCarousel.svelte +630 -0
  303. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  304. package/dist/structure/StructureControls.svelte +157 -399
  305. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  306. package/dist/structure/StructureExportPane.svelte +17 -21
  307. package/dist/structure/StructureInfoPane.svelte +72 -66
  308. package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
  309. package/dist/structure/StructureScene.svelte +467 -476
  310. package/dist/structure/StructureScene.svelte.d.ts +3 -1
  311. package/dist/structure/StructureViewport.svelte +386 -0
  312. package/dist/structure/StructureViewport.svelte.d.ts +63 -0
  313. package/dist/structure/atom-label-placement.d.ts +16 -12
  314. package/dist/structure/atom-label-placement.js +70 -37
  315. package/dist/structure/atom-properties.d.ts +2 -0
  316. package/dist/structure/atom-properties.js +104 -38
  317. package/dist/structure/bonding.d.ts +10 -3
  318. package/dist/structure/bonding.js +84 -28
  319. package/dist/structure/edit-history.d.ts +6 -0
  320. package/dist/structure/edit-history.js +27 -0
  321. package/dist/structure/export.d.ts +1 -0
  322. package/dist/structure/export.js +39 -31
  323. package/dist/structure/format-detect.d.ts +1 -0
  324. package/dist/structure/format-detect.js +22 -0
  325. package/dist/structure/geometry.d.ts +9 -0
  326. package/dist/structure/geometry.js +27 -0
  327. package/dist/structure/index.d.ts +16 -1
  328. package/dist/structure/index.js +13 -2
  329. package/dist/structure/measure.d.ts +1 -1
  330. package/dist/structure/measure.js +6 -12
  331. package/dist/structure/parse.d.ts +2 -1
  332. package/dist/structure/parse.js +243 -222
  333. package/dist/structure/partial-occupancy.js +4 -2
  334. package/dist/structure/pbc.js +78 -98
  335. package/dist/structure/polyhedra.d.ts +0 -1
  336. package/dist/structure/polyhedra.js +3 -18
  337. package/dist/structure/supercell.d.ts +1 -2
  338. package/dist/structure/supercell.js +7 -13
  339. package/dist/symmetry/SymmetryElements.svelte +11 -15
  340. package/dist/symmetry/SymmetryStats.svelte +56 -68
  341. package/dist/symmetry/WyckoffTable.svelte +5 -24
  342. package/dist/symmetry/cell-transform.js +1 -0
  343. package/dist/symmetry/index.d.ts +1 -0
  344. package/dist/symmetry/index.js +8 -2
  345. package/dist/symmetry/spacegroups.js +3 -2
  346. package/dist/table/HeatmapTable.svelte +1102 -365
  347. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  348. package/dist/table/ToggleMenu.svelte +5 -12
  349. package/dist/table/index.d.ts +21 -6
  350. package/dist/table/index.js +34 -21
  351. package/dist/theme/ThemeControl.svelte +16 -8
  352. package/dist/theme/index.d.ts +1 -5
  353. package/dist/theme/index.js +4 -10
  354. package/dist/theme/themes.mjs +14 -7
  355. package/dist/tooltip/KCoords.svelte +4 -1
  356. package/dist/tooltip/TooltipContent.svelte +2 -5
  357. package/dist/trajectory/Trajectory.svelte +515 -308
  358. package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
  359. package/dist/trajectory/TrajectoryError.svelte +11 -8
  360. package/dist/trajectory/TrajectoryExportPane.svelte +14 -31
  361. package/dist/trajectory/TrajectoryInfoPane.svelte +85 -97
  362. package/dist/trajectory/extract.js +56 -34
  363. package/dist/trajectory/format-detect.js +11 -9
  364. package/dist/trajectory/frame-reader.d.ts +2 -0
  365. package/dist/trajectory/frame-reader.js +33 -29
  366. package/dist/trajectory/helpers.d.ts +2 -0
  367. package/dist/trajectory/helpers.js +18 -7
  368. package/dist/trajectory/index.d.ts +1 -0
  369. package/dist/trajectory/index.js +3 -0
  370. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  371. package/dist/trajectory/parse/h5-utils.js +109 -0
  372. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  373. package/dist/trajectory/parse/hdf5.js +116 -106
  374. package/dist/trajectory/parse/index.d.ts +2 -3
  375. package/dist/trajectory/parse/index.js +17 -20
  376. package/dist/trajectory/parse/lammps.js +8 -12
  377. package/dist/trajectory/parse/vasp.js +10 -7
  378. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  379. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  380. package/dist/trajectory/parse/vaspout-h5.d.ts +5 -0
  381. package/dist/trajectory/parse/vaspout-h5.js +284 -0
  382. package/dist/trajectory/parse/xyz.js +11 -7
  383. package/dist/trajectory/plotting.d.ts +8 -9
  384. package/dist/trajectory/plotting.js +99 -27
  385. package/dist/utils.d.ts +3 -0
  386. package/dist/utils.js +7 -0
  387. package/dist/xrd/XrdPlot.svelte +47 -61
  388. package/dist/xrd/broadening.js +2 -1
  389. package/dist/xrd/calc-xrd.js +2 -4
  390. package/dist/xrd/parse.d.ts +0 -1
  391. package/dist/xrd/parse.js +56 -73
  392. package/package.json +61 -33
  393. package/dist/api/mp.d.ts +0 -6
  394. package/dist/api/mp.js +0 -22
  395. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
@@ -3,7 +3,7 @@
3
3
  generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
4
4
  >
5
5
  import type { D3InterpolateName } from '../../colors'
6
- import { format_value } from '../../labels'
6
+ import { format_value_or_num } from '../../labels'
7
7
  import { sanitize_html } from '../../sanitize'
8
8
  import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
9
9
  import type { Point2D, Vec2 } from '../../math'
@@ -33,11 +33,19 @@
33
33
  compute_element_placement,
34
34
  PlotAxis,
35
35
  PlotLegend,
36
+ PlotMarginals,
36
37
  ReferenceLine,
37
38
  ScatterPoint,
38
39
  } from '..'
39
- import type { AxisChangeState } from '../core/axis-utils'
40
- import { create_axis_loader } from '../core/axis-utils'
40
+ import type { MarginalSeriesInput, MarginalsProp } from '../core/marginals'
41
+ import {
42
+ add_sides,
43
+ marginal_axis,
44
+ marginal_axis_presence,
45
+ normalize_marginals,
46
+ reserve_marginal_pad,
47
+ } from '../core/marginals'
48
+ import { type AxisChangeState, create_axis_loader } from '../core/axis-utils'
41
49
  import { create_placed_tween } from '../core/placed-tween.svelte'
42
50
  import { create_pan_zoom } from '../core/pan-zoom.svelte'
43
51
  import { create_legend_visibility } from '../core/utils/series-visibility'
@@ -49,13 +57,13 @@
49
57
  import type { IndexedRefLine } from '../core/reference-line'
50
58
  import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
51
59
  import {
60
+ create_axis_scales,
52
61
  create_color_scale,
53
- create_scale,
54
62
  create_size_scale,
55
63
  generate_ticks,
56
64
  get_tick_label,
57
65
  } from '../core/scales'
58
- import { DEFAULT_MARKERS } from '../core/types'
66
+ import { DEFAULT_MARKERS, SCALE_DEFAULTS } from '../core/types'
59
67
  import { DEFAULTS } from '../../settings'
60
68
  import { extent } from 'd3-array'
61
69
  import type { Snippet } from 'svelte'
@@ -71,7 +79,9 @@
71
79
  placed_coords,
72
80
  } from '../core/auto-place'
73
81
  import {
82
+ AXIS_TITLE_OFFSET,
74
83
  calc_auto_padding,
84
+ DEFAULT_PLOT_PADDING,
75
85
  filter_padding,
76
86
  LABEL_GAP_DEFAULT,
77
87
  y2_axis_label_x,
@@ -108,8 +118,8 @@
108
118
  y_axis = $bindable({}),
109
119
  y2_axis: y2_axis_prop = $bindable({}),
110
120
  display = $bindable(DEFAULTS.bar.display),
111
- range_padding = 0.05,
112
- padding = { t: 20, b: 60, l: 60, r: 20 },
121
+ range_padding = 0,
122
+ padding = DEFAULT_PLOT_PADDING,
113
123
  legend = {},
114
124
  show_legend,
115
125
  bar = {},
@@ -121,12 +131,8 @@
121
131
  on_bar_click,
122
132
  on_bar_hover,
123
133
  // Line marker props (matching ScatterPlot)
124
- color_scale = {
125
- type: `linear`,
126
- scheme: `interpolateViridis`,
127
- value_range: undefined,
128
- },
129
- size_scale = { type: `linear`, radius_range: [2, 10], value_range: undefined },
134
+ color_scale = SCALE_DEFAULTS.color,
135
+ size_scale = SCALE_DEFAULTS.size,
130
136
  point_tween,
131
137
  on_point_click,
132
138
  on_point_hover,
@@ -146,61 +152,65 @@
146
152
  on_axis_change,
147
153
  on_error,
148
154
  pan = {},
155
+ marginals = false,
149
156
  ...rest
150
- }: HTMLAttributes<HTMLDivElement> & BasePlotProps & PlotConfig & {
151
- series?: BarSeries<Metadata>[]
152
- // Component-specific props
153
- orientation?: Orientation
154
- mode?: BarMode
155
- legend?: LegendConfig | null
156
- show_legend?: boolean
157
- bar?: BarStyle
158
- line?: LineStyle
159
- tooltip?: Snippet<[BarHandlerProps<Metadata>]>
160
- user_content?: Snippet<[UserContentProps]>
161
- header_controls?: Snippet<
162
- [{ height: number; width: number; fullscreen: boolean }]
163
- >
164
- controls_extra?: Snippet<
165
- [{ orientation: Orientation; mode: BarMode } & Required<PlotConfig>]
166
- >
167
- change?: (data: BarHandlerProps<Metadata> | null) => void
168
- on_bar_click?: (
169
- data: BarHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
170
- ) => void
171
- on_bar_hover?: (
172
- data: (BarHandlerProps<Metadata> & { event: MouseEvent | FocusEvent | KeyboardEvent }) | null,
173
- ) => void
174
- // Line marker props (matching ScatterPlot)
175
- // Note: For line series with markers, BOTH on_bar_* AND on_point_* events fire.
176
- // Use on_point_* for marker-specific data (includes `point` with InternalPoint details)
177
- // or on_bar_* for backward compatibility with bar-style event handling.
178
- color_scale?: ColorScaleConfig | D3InterpolateName
179
- size_scale?: SizeScaleConfig
180
- point_tween?: TweenOptions<Point2D>
181
- on_point_click?: (
182
- data: LineMarkerHandlerProps & { event: MouseEvent | KeyboardEvent },
183
- ) => void
184
- on_point_hover?: (
185
- data:
186
- | (LineMarkerHandlerProps & {
187
- event: MouseEvent | FocusEvent | KeyboardEvent
188
- })
189
- | null,
190
- ) => void
191
- ref_lines?: RefLine[]
192
- on_ref_line_click?: (event: RefLineEvent) => void
193
- on_ref_line_hover?: (event: RefLineEvent | null) => void
194
- // Interactive axis props
195
- data_loader?: DataLoaderFn<Metadata, BarSeries<Metadata>>
196
- on_axis_change?: (
197
- axis: `x` | `x2` | `y` | `y2`,
198
- key: string,
199
- new_series: BarSeries<Metadata>[],
200
- ) => void
201
- on_error?: (error: AxisLoadError) => void
202
- pan?: PanConfig
203
- } = $props()
157
+ }: HTMLAttributes<HTMLDivElement> &
158
+ BasePlotProps &
159
+ PlotConfig & {
160
+ series?: BarSeries<Metadata>[]
161
+ // Component-specific props
162
+ orientation?: Orientation
163
+ mode?: BarMode
164
+ legend?: LegendConfig | null
165
+ show_legend?: boolean
166
+ bar?: BarStyle
167
+ line?: LineStyle
168
+ tooltip?: Snippet<[BarHandlerProps<Metadata>]>
169
+ user_content?: Snippet<[UserContentProps]>
170
+ header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
171
+ controls_extra?: Snippet<
172
+ [{ orientation: Orientation; mode: BarMode } & Required<PlotConfig>]
173
+ >
174
+ change?: (data: BarHandlerProps<Metadata> | null) => void
175
+ on_bar_click?: (
176
+ data: BarHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
177
+ ) => void
178
+ on_bar_hover?: (
179
+ data:
180
+ | (BarHandlerProps<Metadata> & { event: MouseEvent | FocusEvent | KeyboardEvent })
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?: ColorScaleConfig | D3InterpolateName
188
+ size_scale?: SizeScaleConfig
189
+ point_tween?: TweenOptions<Point2D>
190
+ on_point_click?: (
191
+ data: LineMarkerHandlerProps & { event: MouseEvent | KeyboardEvent },
192
+ ) => void
193
+ on_point_hover?: (
194
+ data:
195
+ | (LineMarkerHandlerProps & {
196
+ event: MouseEvent | FocusEvent | KeyboardEvent
197
+ })
198
+ | null,
199
+ ) => void
200
+ ref_lines?: RefLine[]
201
+ on_ref_line_click?: (event: RefLineEvent) => void
202
+ on_ref_line_hover?: (event: RefLineEvent | null) => void
203
+ // Interactive axis props
204
+ data_loader?: DataLoaderFn<Metadata, BarSeries<Metadata>>
205
+ on_axis_change?: (
206
+ axis: `x` | `x2` | `y` | `y2`,
207
+ key: string,
208
+ new_series: BarSeries<Metadata>[],
209
+ ) => void
210
+ on_error?: (error: AxisLoadError) => void
211
+ pan?: PanConfig
212
+ marginals?: MarginalsProp
213
+ } = $props()
204
214
 
205
215
  // Initialize bar, line, y2_axis with defaults - using $derived for reactivity
206
216
  let bar_state = $derived({ ...DEFAULTS.bar.bar, ...bar })
@@ -220,7 +230,7 @@
220
230
  format: ``,
221
231
  scale_type: `linear`,
222
232
  ticks: 5,
223
- label_shift: { x: 0, y: 40 },
233
+ label_shift: { x: 0, y: AXIS_TITLE_OFFSET },
224
234
  tick: { label: { shift: { x: 0, y: 0 } } },
225
235
  range: [null, null],
226
236
  ...x2_axis_prop,
@@ -238,8 +248,7 @@
238
248
  let axis_loading = $state<`x` | `x2` | `y` | `y2` | null>(null)
239
249
 
240
250
  // Compute ref_lines with index and group by z-index (using shared utilities)
241
- let indexed_ref_lines = $derived(index_ref_lines(ref_lines))
242
- let ref_lines_by_z = $derived(group_ref_lines_by_z(indexed_ref_lines))
251
+ let ref_lines_by_z = $derived(group_ref_lines_by_z(index_ref_lines(ref_lines)))
243
252
 
244
253
  // === Categorical Normalization (string x values -> integer indices, see ./data) ===
245
254
  let cat_norm = $derived(normalize_categorical(series, x_axis.categories))
@@ -257,48 +266,45 @@
257
266
  const axis_px = (orientation === `horizontal` ? height : width) || 0
258
267
  const max_ticks = Math.max(1, Math.floor(axis_px / 28)) // ~28px per category label
259
268
  const step = Math.ceil(category_indices.length / max_ticks)
260
- return step <= 1
261
- ? category_indices
262
- : category_indices.filter((_, idx) => idx % step === 0)
269
+ return step <= 1 ? category_indices : category_indices.filter((_, idx) => idx % step === 0)
263
270
  })
264
271
 
265
272
  // Compute auto ranges from visible series
266
- let visible_series = $derived(
267
- internal_series.filter((srs) => srs?.visible ?? true),
268
- )
273
+ let visible_series = $derived(internal_series.filter((srs) => srs?.visible ?? true))
269
274
 
270
275
  // Separate series by y-axis
271
- let y1_series = $derived(
272
- visible_series.filter((srs) => (srs.y_axis ?? `y1`) === `y1`),
273
- )
274
- let y2_series = $derived(
275
- visible_series.filter((srs) => srs.y_axis === `y2`),
276
- )
277
- let x2_series = $derived(
278
- visible_series.filter((srs) => srs.x_axis === `x2`),
276
+ let y1_series = $derived(visible_series.filter((srs) => (srs.y_axis ?? `y1`) === `y1`))
277
+ let y2_series = $derived(visible_series.filter((srs) => srs.y_axis === `y2`))
278
+ let x2_series = $derived(visible_series.filter((srs) => srs.x_axis === `x2`))
279
+ // Whether the secondary x2 (top) / y2 (right) axis actually renders: BarPlot only supports
280
+ // them in vertical orientation. Derive once so ticks, padding, axis rendering, and marginal
281
+ // placement stay in sync. (Data-existence checks below use the bare `*_series.length` instead.)
282
+ let show_x2 = $derived(x2_series.length > 0 && orientation === `vertical`)
283
+ let show_y2 = $derived(y2_series.length > 0 && orientation === `vertical`)
284
+
285
+ let auto_ranges = $derived(
286
+ compute_bar_auto_ranges({
287
+ visible_series,
288
+ y1_series,
289
+ y2_series,
290
+ x2_series,
291
+ mode,
292
+ orientation,
293
+ range_padding,
294
+ category_count: category_list.length,
295
+ x_range: x_axis.range ?? [null, null],
296
+ x_scale_type: x_axis.scale_type ?? `linear`,
297
+ x_is_time: x_axis.format?.startsWith(`%`) || false,
298
+ x2_range: x2_axis.range ?? [null, null],
299
+ x2_scale_type: x2_axis.scale_type ?? `linear`,
300
+ x2_is_time: x2_axis.format?.startsWith(`%`) || false,
301
+ y_range: y_axis.range ?? [null, null],
302
+ y_scale_type: y_axis.scale_type ?? `linear`,
303
+ y2_range: y2_axis.range ?? [null, null],
304
+ y2_scale_type: y2_axis.scale_type ?? `linear`,
305
+ }),
279
306
  )
280
307
 
281
- let auto_ranges = $derived(compute_bar_auto_ranges({
282
- visible_series,
283
- y1_series,
284
- y2_series,
285
- x2_series,
286
- mode,
287
- orientation,
288
- range_padding,
289
- category_count: category_list.length,
290
- x_range: x_axis.range ?? [null, null],
291
- x_scale_type: x_axis.scale_type ?? `linear`,
292
- x_is_time: x_axis.format?.startsWith(`%`) || false,
293
- x2_range: x2_axis.range ?? [null, null],
294
- x2_scale_type: x2_axis.scale_type ?? `linear`,
295
- x2_is_time: x2_axis.format?.startsWith(`%`) || false,
296
- y_range: y_axis.range ?? [null, null],
297
- y_scale_type: y_axis.scale_type ?? `linear`,
298
- y2_range: y2_axis.range ?? [null, null],
299
- y2_scale_type: y2_axis.scale_type ?? `linear`,
300
- }))
301
-
302
308
  // Initialize and current ranges
303
309
  let ranges = $state<{
304
310
  initial: { x: Vec2; x2: Vec2; y: Vec2; y2: Vec2 }
@@ -308,10 +314,14 @@
308
314
  current: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
309
315
  })
310
316
 
311
- $effect(() => { // handle x_axis.range / x2_axis.range / y_axis.range / y2_axis.range changes
317
+ $effect(() => {
318
+ // handle x_axis.range / x2_axis.range / y_axis.range / y2_axis.range changes
312
319
  // resolve_axis_ranges returns null for transient non-finite bounds (skip: writing
313
320
  // NaN breaks scales and, since NaN !== NaN, loops the effect)
314
- const next = resolve_axis_ranges({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis }, auto_ranges)
321
+ const next = resolve_axis_ranges(
322
+ { x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis },
323
+ auto_ranges,
324
+ )
315
325
  if (!next) return
316
326
  // Only update if the initial (data-driven) ranges changed, not when user pans.
317
327
  // untrack the read of `ranges` so the assignment below can't re-trigger this effect
@@ -323,42 +333,33 @@
323
333
  })
324
334
 
325
335
  // Layout: dynamic padding based on tick label widths
326
- const default_padding = { t: 20, b: 60, l: 60, r: 20 }
327
336
  // base_pad reserves space for tick labels/axis titles; pad (below) adds decoration reservations
328
- let base_pad = $derived(filter_padding(padding, default_padding))
337
+ let base_pad = $derived(filter_padding(padding, DEFAULT_PLOT_PADDING))
329
338
 
330
339
  // Update padding when format or ticks change
331
340
  $effect(() => {
332
- const new_pad = width && height && ticks.y.length > 0
333
- ? calc_auto_padding({
334
- padding,
335
- default_padding,
336
- x2_axis: { ...x2_axis, tick_values: ticks.x2 },
337
- y_axis: { ...y_axis, tick_values: ticks.y },
338
- y2_axis: { ...y2_axis, tick_values: ticks.y2 },
339
- })
340
- : filter_padding(padding, default_padding)
341
+ const new_pad =
342
+ width && height && ticks.y.length > 0
343
+ ? calc_auto_padding({
344
+ padding,
345
+ default_padding: DEFAULT_PLOT_PADDING,
346
+ x2_axis: { ...x2_axis, tick_values: ticks.x2 },
347
+ y_axis: { ...y_axis, tick_values: ticks.y },
348
+ y2_axis: { ...y2_axis, tick_values: ticks.y2 },
349
+ })
350
+ : filter_padding(padding, DEFAULT_PLOT_PADDING)
341
351
  // Expand right padding if y2 ticks are shown (only for vertical orientation)
342
- if (
343
- width && height && y2_series.length > 0 && ticks.y2.length > 0 &&
344
- orientation === `vertical`
345
- ) {
352
+ if (width && height && show_y2 && ticks.y2.length > 0) {
346
353
  // Need space for: tick shift + tick width + gap (30px) + label space (20px if present)
347
354
  // When ticks are inside, they don't contribute to padding
348
355
  const inside = y2_axis.tick?.label?.inside ?? false
349
356
  const tick_shift = inside ? 0 : (y2_axis.tick?.label?.shift?.x ?? 0) + 8
350
357
  const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max
351
358
  const label_space = y2_axis.label ? 20 : 0
352
- new_pad.r = Math.max(
353
- new_pad.r,
354
- tick_shift + tick_width_contribution + 30 + label_space,
355
- )
359
+ new_pad.r = Math.max(new_pad.r, tick_shift + tick_width_contribution + 30 + label_space)
356
360
  }
357
361
  // Expand top padding if x2 ticks are shown (only for vertical orientation)
358
- if (
359
- width && height && x2_series.length > 0 && ticks.x2.length > 0 &&
360
- orientation === `vertical`
361
- ) {
362
+ if (width && height && show_x2 && ticks.x2.length > 0) {
362
363
  const inside = x2_axis.tick?.label?.inside ?? false
363
364
  const tick_shift = inside ? 0 : Math.abs(x2_axis.tick?.label?.shift?.y ?? 0) + 5
364
365
  const tick_height = inside ? 0 : 16
@@ -368,13 +369,18 @@
368
369
 
369
370
  // Only update if padding actually changed (prevents infinite loop)
370
371
  if (
371
- base_pad.t !== new_pad.t || base_pad.b !== new_pad.b ||
372
- base_pad.l !== new_pad.l || base_pad.r !== new_pad.r
373
- ) base_pad = new_pad
372
+ base_pad.t !== new_pad.t ||
373
+ base_pad.b !== new_pad.b ||
374
+ base_pad.l !== new_pad.l ||
375
+ base_pad.r !== new_pad.r
376
+ )
377
+ base_pad = new_pad
374
378
  })
375
379
 
376
380
  let legend_element = $state<HTMLDivElement | undefined>()
377
- const legend_footprint = $derived(measured_footprint(legend_element, { width: 120, height: 60 }))
381
+ const legend_footprint = $derived(
382
+ measured_footprint(legend_element, { width: 120, height: 60 }),
383
+ )
378
384
  const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
379
385
 
380
386
  // Obstacle field in normalized [0,1] plot coords (y=0 at top). Each bar is modeled as a segment
@@ -403,8 +409,18 @@
403
409
  const value = base + srs.y[bar_idx]
404
410
  // vertical: category on x, value rises on y (inverted). horizontal: category on y, value on x
405
411
  const seg = vertical
406
- ? clip_bar(true, (x_val - ax0) / x_span, 1 - (value - vy0) / y_span, 1 - (base - vy0) / y_span)
407
- : clip_bar(false, 1 - (x_val - cy0) / cy_span, (value - ax0) / x_span, (base - ax0) / x_span)
412
+ ? clip_bar(
413
+ true,
414
+ (x_val - ax0) / x_span,
415
+ 1 - (value - vy0) / y_span,
416
+ 1 - (base - vy0) / y_span,
417
+ )
418
+ : clip_bar(
419
+ false,
420
+ 1 - (x_val - cy0) / cy_span,
421
+ (value - ax0) / x_span,
422
+ (base - ax0) / x_span,
423
+ )
408
424
  if (seg) bars.push(seg)
409
425
  })
410
426
  })
@@ -419,39 +435,58 @@
419
435
  height,
420
436
  obstacles_norm,
421
437
  // gate on legend_element (the render signal) not legend_data, whose entries can read pad
422
- legend: legend != null &&
423
- (show_legend !== undefined ? show_legend : series.length > 1) &&
424
- legend_element != null && !legend_has_explicit_pos
425
- ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
426
- : null,
427
- })
438
+ legend:
439
+ legend != null &&
440
+ (show_legend !== undefined ? show_legend : series.length > 1) &&
441
+ legend_element != null &&
442
+ !legend_has_explicit_pos
443
+ ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
444
+ : null,
445
+ }),
428
446
  )
429
- const pad = $derived(decor.pad)
447
+ // Resolve marginals: a cumulative/Pareto CDF over the CATEGORY axis weighted by bar height.
448
+ // Categories sit on x (vertical) or y (horizontal), so the default side and the value array
449
+ // flip with orientation. The value axis carries no marginal (a bar's height isn't a sample).
450
+ const marginal_is_vertical = $derived(orientation === `vertical`)
451
+ const resolved_marginals = $derived(
452
+ normalize_marginals(
453
+ marginals,
454
+ marginal_is_vertical ? { top: { type: `cdf` } } : { right: { type: `cdf` } },
455
+ ),
456
+ )
457
+ const pad = $derived(add_sides(decor.pad, reserve_marginal_pad(resolved_marginals)))
458
+ const marginal_series = $derived<MarginalSeriesInput[]>(
459
+ internal_series.map((srs) => ({
460
+ x: marginal_is_vertical ? (srs?.x ?? []) : undefined,
461
+ y: marginal_is_vertical ? undefined : (srs?.x ?? []),
462
+ // magnitude weights so negative bars still yield a monotonic cumulative (CDF) marginal
463
+ weight: srs?.y?.map((value) => Math.abs(value)) ?? [],
464
+ color:
465
+ srs?.color ??
466
+ (srs?.render_mode === `line` ? line_state.color : bar_state.color) ??
467
+ `steelblue`,
468
+ label: srs?.label,
469
+ visible: srs?.visible ?? true,
470
+ x_axis: srs?.x_axis,
471
+ y_axis: srs?.y_axis,
472
+ })),
473
+ )
474
+ const marginal_has_axis = $derived(marginal_axis_presence(show_x2, show_y2))
430
475
  const legend_auto_outside = $derived(decor.legend_outside)
431
476
  const legend_outside_x = $derived(decor.legend_pos.x)
432
477
  const legend_outside_y = $derived(decor.legend_pos.y)
433
478
  const chart_width = $derived(Math.max(1, width - pad.l - pad.r))
434
479
  const chart_height = $derived(Math.max(1, height - pad.t - pad.b))
435
480
 
436
- // Scales
437
- let scales = $derived({
438
- x: create_scale(x_axis.scale_type ?? `linear`, ranges.current.x, [
439
- pad.l,
440
- width - pad.r,
441
- ]),
442
- x2: create_scale(x2_axis.scale_type ?? `linear`, ranges.current.x2, [
443
- pad.l,
444
- width - pad.r,
445
- ]),
446
- y: create_scale(y_axis.scale_type ?? `linear`, ranges.current.y, [
447
- height - pad.b,
448
- pad.t,
449
- ]),
450
- y2: create_scale(y2_axis.scale_type ?? `linear`, ranges.current.y2, [
451
- height - pad.b,
452
- pad.t,
453
- ]),
454
- })
481
+ let scales = $derived(
482
+ create_axis_scales(
483
+ { x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis },
484
+ ranges.current,
485
+ pad,
486
+ width,
487
+ height,
488
+ ),
489
+ )
455
490
 
456
491
  // Compute plot center for point tweening origin
457
492
  let plot_center_x = $derived(pad.l + (width - pad.r - pad.l) / 2)
@@ -462,9 +497,7 @@
462
497
  visible_series
463
498
  .filter((srs: BarSeries<Metadata>) => srs.render_mode === `line`)
464
499
  .flatMap((srs: BarSeries<Metadata>) =>
465
- (srs.color_values ?? []).filter(
466
- (val): val is number => typeof val === `number`,
467
- )
500
+ (srs.color_values ?? []).filter((val): val is number => typeof val === `number`),
468
501
  ),
469
502
  )
470
503
 
@@ -480,9 +513,7 @@
480
513
  visible_series
481
514
  .filter((srs: BarSeries<Metadata>) => srs.render_mode === `line`)
482
515
  .flatMap((srs: BarSeries<Metadata>) =>
483
- [...(srs.size_values ?? [])].filter(
484
- (val): val is number => typeof val === `number`,
485
- )
516
+ [...(srs.size_values ?? [])].filter((val): val is number => typeof val === `number`),
486
517
  ),
487
518
  )
488
519
 
@@ -500,57 +531,37 @@
500
531
  // Only respect user ticks when they're a Record (custom label mapping),
501
532
  // not a number (tick count) or array (tick positions)
502
533
  const user_ticks = cat_axis === `x` ? x_axis.ticks : y_axis.ticks
503
- if (
504
- user_ticks != null && typeof user_ticks === `object` &&
505
- !Array.isArray(user_ticks)
506
- ) return user_ticks
507
- return Object.fromEntries(
508
- category_list.map((cat, idx) => [idx, cat]),
509
- ) as Record<number, string>
534
+ if (user_ticks != null && typeof user_ticks === `object` && !Array.isArray(user_ticks))
535
+ return user_ticks
536
+ return Object.fromEntries(category_list.map((cat, idx): [number, string] => [idx, cat]))
510
537
  })
511
538
 
512
- // Ticks
513
- let ticks = $derived({
514
- x: width && height
515
- ? (category_indices && cat_axis === `x` ? cat_tick_indices : generate_ticks(
516
- ranges.current.x,
517
- x_axis.scale_type ?? `linear`,
518
- x_axis.ticks,
519
- scales.x,
520
- { default_count: 8 },
521
- ))
522
- : [],
523
- y: width && height
524
- ? (category_indices && cat_axis === `y` ? cat_tick_indices : generate_ticks(
525
- ranges.current.y,
526
- y_axis.scale_type ?? `linear`,
527
- y_axis.ticks,
528
- scales.y,
529
- { default_count: 6 },
530
- ))
531
- : [],
532
- y2: width && height && y2_series.length > 0 && orientation === `vertical`
533
- ? generate_ticks(
534
- ranges.current.y2,
535
- y2_axis.scale_type ?? `linear`,
536
- y2_axis.ticks,
537
- scales.y2,
538
- {
539
- default_count: 6,
540
- },
541
- )
542
- : [],
543
- x2: width && height && x2_series.length > 0 && orientation === `vertical`
544
- ? generate_ticks(
545
- ranges.current.x2,
546
- x2_axis.scale_type ?? `linear`,
547
- x2_axis.ticks,
548
- scales.x2,
549
- {
550
- default_count: 8,
551
- },
552
- )
553
- : [],
539
+ let ticks = $derived.by(() => {
540
+ const axis_ticks = (
541
+ axis: typeof x_axis,
542
+ range: Vec2,
543
+ scale: typeof scales.x,
544
+ default_count: number,
545
+ show = true,
546
+ ) =>
547
+ width && height && show
548
+ ? generate_ticks(range, axis.scale_type ?? `linear`, axis.ticks, scale, {
549
+ default_count,
550
+ })
551
+ : []
552
+ // categorical axes show one tick per category instead of generated numeric ticks
553
+ return {
554
+ x:
555
+ category_indices && cat_axis === `x` && width && height
556
+ ? cat_tick_indices
557
+ : axis_ticks(x_axis, ranges.current.x, scales.x, 8),
558
+ y:
559
+ category_indices && cat_axis === `y` && width && height
560
+ ? cat_tick_indices
561
+ : axis_ticks(y_axis, ranges.current.y, scales.y, 6),
562
+ y2: axis_ticks(y2_axis, ranges.current.y2, scales.y2, 6, show_y2),
563
+ x2: axis_ticks(x2_axis, ranges.current.x2, scales.x2, 8, show_x2),
564
+ }
554
565
  })
555
566
 
556
567
  // Cache measured tick-label widths so expensive canvas text measurement
@@ -564,7 +575,8 @@
564
575
  // Shared pan/zoom/touch/drag-rect interaction controller
565
576
  const pan_zoom = create_pan_zoom({
566
577
  ranges: () => ranges.current,
567
- scale_type: (axis) => ({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
578
+ scale_type: (axis) =>
579
+ ({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
568
580
  plot_dims: () => ({ width: chart_width, height: chart_height }),
569
581
  pan: () => pan,
570
582
  set_range: (axis, range) => (ranges.current[axis] = range),
@@ -574,13 +586,13 @@
574
586
  const next_x = invert_rect_range(scales.x, start.x, current.x)
575
587
  if (!next_x) return
576
588
  x_axis = { ...x_axis, range: next_x }
577
- // gate x2/y2 on series presence: their scales are [0, 1] sentinels otherwise,
578
- // so inverting would store a phantom range in the bindable prop
579
- const next_x2 = x2_series.length > 0 ? invert_rect_range(scales.x2, start.x, current.x) : null
589
+ // gate x2/y2 on whether they actually render (show_x2/show_y2 also require vertical);
590
+ // otherwise their [0, 1] sentinel scales would store a phantom range in the bindable prop
591
+ const next_x2 = show_x2 ? invert_rect_range(scales.x2, start.x, current.x) : null
580
592
  if (next_x2) x2_axis_prop = { ...x2_axis_prop, range: next_x2 }
581
593
  const next_y = invert_rect_range(scales.y, start.y, current.y)
582
594
  if (next_y) y_axis = { ...y_axis, range: next_y }
583
- const next_y2 = y2_series.length > 0 ? invert_rect_range(scales.y2, start.y, current.y) : null
595
+ const next_y2 = show_y2 ? invert_rect_range(scales.y2, start.y, current.y) : null
584
596
  if (next_y2) y2_axis_prop = { ...y2_axis_prop, range: next_y2 }
585
597
  },
586
598
  on_reset: () => {
@@ -606,8 +618,7 @@
606
618
  const is_line = srs.render_mode === `line`
607
619
  const series_markers = srs.markers ?? DEFAULT_MARKERS
608
620
  const has_line = series_markers === `line` || series_markers === `line+points`
609
- const has_points = series_markers === `points` ||
610
- series_markers === `line+points`
621
+ const has_points = series_markers === `points` || series_markers === `line+points`
611
622
  const series_color = srs.color ?? (is_line ? line_state.color : bar_state.color)
612
623
 
613
624
  // Get point style for symbol color (handle array or single object)
@@ -615,9 +626,10 @@
615
626
  ? srs.point_style[0]
616
627
  : srs.point_style
617
628
  const first_color_value = srs.color_values?.[0]
618
- const point_color = first_color_value != null
619
- ? color_scale_fn(first_color_value)
620
- : first_point_style?.fill ?? series_color
629
+ const point_color =
630
+ first_color_value != null
631
+ ? color_scale_fn(first_color_value)
632
+ : (first_point_style?.fill ?? series_color)
621
633
 
622
634
  if (is_line) {
623
635
  // Line series: show line and/or symbol based on markers
@@ -629,16 +641,15 @@
629
641
  display_style: {
630
642
  ...(has_line
631
643
  ? {
632
- line_color: series_color,
633
- line_dash: srs.line_style?.line_dash,
634
- }
644
+ line_color: series_color,
645
+ line_dash: srs.line_style?.line_dash,
646
+ }
635
647
  : {}),
636
648
  ...(has_points
637
649
  ? {
638
- symbol_type: first_point_style?.symbol_type ??
639
- DEFAULTS.scatter.symbol_type,
640
- symbol_color: point_color,
641
- }
650
+ symbol_type: first_point_style?.symbol_type ?? DEFAULTS.scatter.symbol_type,
651
+ symbol_color: point_color,
652
+ }
642
653
  : {}),
643
654
  },
644
655
  }
@@ -654,10 +665,13 @@
654
665
  symbol_color: series_color,
655
666
  },
656
667
  }
657
- })
668
+ }),
658
669
  )
659
670
 
660
- const legend_vis = create_legend_visibility(() => series, (next) => (series = next))
671
+ const legend_vis = create_legend_visibility(
672
+ () => series,
673
+ (next) => (series = next),
674
+ )
661
675
 
662
676
  // Collect bar and line positions for legend placement
663
677
  let bar_points_for_placement = $derived.by(() => {
@@ -674,12 +688,11 @@
674
688
  return srs.x
675
689
  .map((x_val, bar_idx) => {
676
690
  const y_val = srs.y[bar_idx]
677
- const base = !is_line && mode === `stacked`
678
- ? (series_offsets[bar_idx] ?? 0)
679
- : 0
680
- const [bar_x, bar_y] = orientation === `vertical`
681
- ? [x_scale_pl(x_val), y_scale(base + y_val)]
682
- : [x_scale_pl(base + y_val), scales.y(x_val)]
691
+ const base = !is_line && mode === `stacked` ? (series_offsets[bar_idx] ?? 0) : 0
692
+ const [bar_x, bar_y] =
693
+ orientation === `vertical`
694
+ ? [x_scale_pl(x_val), y_scale(base + y_val)]
695
+ : [x_scale_pl(base + y_val), scales.y(x_val)]
683
696
  return { x: bar_x, y: bar_y }
684
697
  })
685
698
  .filter(({ x, y }) => isFinite(x) && isFinite(y))
@@ -726,9 +739,7 @@
726
739
  const srs = internal_series[series_idx]
727
740
  const [x, y] = [srs.x[bar_idx], srs.y[bar_idx]]
728
741
  const [orient_x, orient_y] = orientation === `horizontal` ? [y, x] : [x, y]
729
- const metadata = Array.isArray(srs.metadata)
730
- ? srs.metadata[bar_idx]
731
- : srs.metadata
742
+ const metadata = Array.isArray(srs.metadata) ? srs.metadata[bar_idx] : srs.metadata
732
743
  const label = srs.labels?.[bar_idx] ?? null
733
744
  const active_y_axis = srs.y_axis ?? `y1`
734
745
  const active_x_axis = srs.x_axis ?? `x1`
@@ -783,7 +794,7 @@
783
794
  const line_point_fill = (pt: LineSeriesPoint, series_color: string): string =>
784
795
  pt.color_value != null
785
796
  ? color_scale_fn(pt.color_value)
786
- : pt.point_style?.fill ?? series_color
797
+ : (pt.point_style?.fill ?? series_color)
787
798
 
788
799
  const handle_bar_hover =
789
800
  (series_idx: number, bar_idx: number, color: string) => (event: MouseEvent) => {
@@ -805,31 +816,31 @@
805
816
  })
806
817
 
807
818
  // State accessors for shared axis change handler
819
+ // Secondary axes read the merged $derived (x2_axis/y2_axis) but write the raw $bindable props
820
+ // (x2_axis_prop/y2_axis_prop) so library defaults aren't pushed into the parent's bound state
808
821
  const axis_state: AxisChangeState<BarSeries<Metadata>> = {
809
- get_axis: (axis) => {
810
- if (axis === `x`) return x_axis
811
- if (axis === `x2`) return x2_axis
812
- if (axis === `y`) return y_axis
813
- return y2_axis
822
+ axes: {
823
+ x: { get: () => x_axis, set: (config) => (x_axis = { ...x_axis, ...config }) },
824
+ x2: {
825
+ get: () => x2_axis,
826
+ set: (config) => (x2_axis_prop = { ...x2_axis_prop, ...config }),
827
+ },
828
+ y: { get: () => y_axis, set: (config) => (y_axis = { ...y_axis, ...config }) },
829
+ y2: {
830
+ get: () => y2_axis,
831
+ set: (config) => (y2_axis_prop = { ...y2_axis_prop, ...config }),
832
+ },
814
833
  },
815
- set_axis: (axis, config) => {
816
- // Spread into existing state to preserve merged type structure
817
- if (axis === `x`) x_axis = { ...x_axis, ...config }
818
- else if (axis === `x2`) x2_axis_prop = { ...x2_axis_prop, ...config }
819
- else if (axis === `y`) y_axis = { ...y_axis, ...config }
820
- else y2_axis_prop = { ...y2_axis_prop, ...config }
821
- },
822
- get_series: () => series,
823
- set_series: (new_series) => (series = new_series),
824
- get_loading: () => axis_loading,
825
- set_loading: (axis) => (axis_loading = axis),
834
+ series: { get: () => series, set: (next) => (series = next) },
835
+ loading: { get: () => axis_loading, set: (axis) => (axis_loading = axis) },
826
836
  }
827
837
 
828
838
  // Shared handler + one-shot auto-load bound to this component's state
829
- const { handle_axis_change, try_auto_load } = create_axis_loader(
830
- axis_state,
831
- () => ({ data_loader, on_axis_change, on_error }),
832
- )
839
+ const { handle_axis_change, try_auto_load } = create_axis_loader(axis_state, () => ({
840
+ data_loader,
841
+ on_axis_change,
842
+ on_error,
843
+ }))
833
844
  $effect(try_auto_load)
834
845
  </script>
835
846
 
@@ -877,7 +888,7 @@
877
888
  bind:clientWidth={width}
878
889
  bind:clientHeight={height}
879
890
  {...rest}
880
- class="bar-plot {rest.class ?? ``}"
891
+ class={[`bar-plot`, rest.class]}
881
892
  class:fullscreen
882
893
  >
883
894
  {#if width && height}
@@ -893,7 +904,7 @@
893
904
  bind:this={svg_element}
894
905
  role="application"
895
906
  aria-label={rest[`aria-label`] ??
896
- ([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Bar chart`)}
907
+ ([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Bar chart`)}
897
908
  tabindex="0"
898
909
  onfocusin={() => pan_zoom.set_focused(true)}
899
910
  onfocusout={() => pan_zoom.set_focused(false)}
@@ -937,38 +948,38 @@
937
948
  <!-- X-axis -->
938
949
  <PlotAxis
939
950
  side="x"
940
- ticks={ticks.x as number[]}
951
+ ticks={ticks.x}
941
952
  place={scales.x}
942
953
  axis={x_axis}
943
- domain={ranges.current.x as Vec2}
954
+ domain={ranges.current.x}
944
955
  {pad}
945
956
  {width}
946
957
  {height}
947
958
  show_grid={display.x_grid}
948
959
  tick_label={(tick) =>
949
- get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
960
+ get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
950
961
  label_x={pad.l + chart_width / 2 + (x_axis.label_shift?.x ?? 0)}
951
- label_y={height - pad.b / 3 + (x_axis.label_shift?.y ?? 0)}
962
+ label_y={height - pad.b + AXIS_TITLE_OFFSET + (x_axis.label_shift?.y ?? 0)}
952
963
  axis_loading={axis_loading === `x`}
953
964
  on_axis_change={(key) => handle_axis_change(`x`, key)}
954
965
  />
955
966
 
956
967
  <!-- X2-axis (Top) -->
957
968
  <!-- Note: x2 axis is only supported for vertical orientation -->
958
- {#if x2_series.length > 0 && orientation === `vertical`}
969
+ {#if show_x2}
959
970
  <PlotAxis
960
971
  side="x2"
961
- ticks={ticks.x2 as number[]}
972
+ ticks={ticks.x2}
962
973
  place={scales.x2}
963
974
  axis={x2_axis}
964
- domain={ranges.current.x2 as Vec2}
975
+ domain={ranges.current.x2}
965
976
  {pad}
966
977
  {width}
967
978
  {height}
968
979
  show_grid={display.x2_grid}
969
980
  tick_label={(tick) => get_tick_label(tick, x2_axis.ticks)}
970
981
  label_x={pad.l + chart_width / 2 + (x2_axis.label_shift?.x ?? 0)}
971
- label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? 40))}
982
+ label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? AXIS_TITLE_OFFSET))}
972
983
  axis_loading={axis_loading === `x2`}
973
984
  on_axis_change={(key) => handle_axis_change(`x2`, key)}
974
985
  />
@@ -977,19 +988,20 @@
977
988
  <!-- Y-axis -->
978
989
  <PlotAxis
979
990
  side="y"
980
- ticks={ticks.y as number[]}
991
+ ticks={ticks.y}
981
992
  place={scales.y}
982
993
  axis={y_axis}
983
- domain={ranges.current.y as Vec2}
994
+ domain={ranges.current.y}
984
995
  {pad}
985
996
  {width}
986
997
  {height}
987
998
  show_grid={display.y_grid}
988
999
  tick_label={(tick) =>
989
- get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
1000
+ get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
990
1001
  label_x={Math.max(
991
1002
  12,
992
- pad.l - (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
1003
+ pad.l -
1004
+ (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
993
1005
  LABEL_GAP_DEFAULT,
994
1006
  ) + (y_axis.label_shift?.x ?? 0)}
995
1007
  label_y={pad.t + chart_height / 2 + (y_axis.label_shift?.y ?? 0)}
@@ -999,13 +1011,13 @@
999
1011
 
1000
1012
  <!-- Y2-axis (Right) -->
1001
1013
  <!-- Note: y2 axis is only supported for vertical orientation. Implementing x2 for horizontal mode requires additional complexity. -->
1002
- {#if y2_series.length > 0 && orientation === `vertical`}
1014
+ {#if show_y2}
1003
1015
  <PlotAxis
1004
1016
  side="y2"
1005
- ticks={ticks.y2 as number[]}
1017
+ ticks={ticks.y2}
1006
1018
  place={scales.y2}
1007
1019
  axis={y2_axis}
1008
- domain={ranges.current.y2 as Vec2}
1020
+ domain={ranges.current.y2}
1009
1021
  {pad}
1010
1022
  {width}
1011
1023
  {height}
@@ -1046,8 +1058,8 @@
1046
1058
  y2_scale_type={y2_axis.scale_type}
1047
1059
  x_is_time={x_axis.format?.startsWith(`%`) ?? false}
1048
1060
  x2_is_time={x2_axis.format?.startsWith(`%`) ?? false}
1049
- has_x2={x2_series.length > 0}
1050
- has_y2={y2_series.length > 0}
1061
+ has_x2={show_x2}
1062
+ has_y2={show_y2}
1051
1063
  {width}
1052
1064
  {height}
1053
1065
  {pad}
@@ -1065,7 +1077,7 @@
1065
1077
  class={is_line ? `line-series` : `bar-series`}
1066
1078
  data-series-idx={series_idx}
1067
1079
  opacity={hovered_legend_series_idx !== null &&
1068
- hovered_legend_series_idx !== series_idx
1080
+ hovered_legend_series_idx !== series_idx
1069
1081
  ? 0.25
1070
1082
  : 1}
1071
1083
  >
@@ -1079,21 +1091,22 @@
1079
1091
  {@const use_x2 = srs.x_axis === `x2`}
1080
1092
  {@const x_scale = use_x2 ? scales.x2 : scales.x}
1081
1093
  {@const series_markers = srs.markers ?? DEFAULT_MARKERS}
1082
- {@const show_line = series_markers === `line` ||
1083
- series_markers === `line+points`}
1084
- {@const show_points = series_markers === `points` ||
1085
- series_markers === `line+points`}
1094
+ {@const show_line =
1095
+ series_markers === `line` || series_markers === `line+points`}
1096
+ {@const show_points =
1097
+ series_markers === `points` || series_markers === `line+points`}
1086
1098
  {@const points = compute_line_points({
1087
- series: srs,
1088
- series_idx,
1089
- orientation,
1090
- x_scale,
1091
- y_scale,
1092
- cat_y_scale: scales.y,
1093
- })}
1094
- {@const polyline_str = show_line && points.length > 1
1095
- ? points.map((pt) => `${pt.x},${pt.y}`).join(` `)
1096
- : ``}
1099
+ series: srs,
1100
+ series_idx,
1101
+ orientation,
1102
+ x_scale,
1103
+ y_scale,
1104
+ cat_y_scale: scales.y,
1105
+ })}
1106
+ {@const polyline_str =
1107
+ show_line && points.length > 1
1108
+ ? points.map((pt) => `${pt.x},${pt.y}`).join(` `)
1109
+ : ``}
1097
1110
  {#if polyline_str}
1098
1111
  <polyline
1099
1112
  points={polyline_str}
@@ -1142,44 +1155,41 @@
1142
1155
  {#if show_points}
1143
1156
  {@const clickable = on_bar_click || on_point_click}
1144
1157
  {@const get_pt = (evt: Event) => {
1145
- const attr = evt.target instanceof Element
1146
- ? evt.target.closest(`[data-bar-idx]`)?.getAttribute(
1147
- `data-bar-idx`,
1148
- )
1149
- : null
1150
- return points.find((pt) => pt.idx === parseInt(attr ?? ``, 10))
1151
- }}
1158
+ const attr =
1159
+ evt.target instanceof Element
1160
+ ? evt.target.closest(`[data-bar-idx]`)?.getAttribute(`data-bar-idx`)
1161
+ : null
1162
+ return points.find((pt) => pt.idx === parseInt(attr ?? ``, 10))
1163
+ }}
1152
1164
  {@const set_hover = (
1153
- pt: LineSeriesPoint | null,
1154
- evt: MouseEvent | FocusEvent,
1155
- ) => {
1156
- if (pt) {
1157
- hovered = true
1158
- const fill = line_point_fill(pt, color)
1159
- hover_info = get_bar_data(series_idx, pt.idx, fill)
1160
- change(hover_info)
1161
- } else {
1162
- change(null)
1163
- hover_info = null
1164
- }
1165
- on_bar_hover?.(pt ? { ...hover_info!, event: evt } : null)
1166
- on_point_hover?.(
1167
- pt ? { ...hover_info!, event: evt, point: pt } : null,
1168
- )
1169
- }}
1165
+ pt: LineSeriesPoint | null,
1166
+ evt: MouseEvent | FocusEvent,
1167
+ ) => {
1168
+ if (pt) {
1169
+ hovered = true
1170
+ const fill = line_point_fill(pt, color)
1171
+ hover_info = get_bar_data(series_idx, pt.idx, fill)
1172
+ change(hover_info)
1173
+ } else {
1174
+ change(null)
1175
+ hover_info = null
1176
+ }
1177
+ on_bar_hover?.(pt ? { ...hover_info!, event: evt } : null)
1178
+ on_point_hover?.(pt ? { ...hover_info!, event: evt, point: pt } : null)
1179
+ }}
1170
1180
  {@const do_click = (
1171
- pt: LineSeriesPoint,
1172
- evt: MouseEvent | KeyboardEvent,
1173
- ) => {
1174
- const fill = line_point_fill(pt, color)
1175
- const bar_data = get_bar_data(series_idx, pt.idx, fill)
1176
- on_bar_click?.({ ...bar_data, event: evt })
1177
- on_point_click?.({ ...bar_data, event: evt, point: pt })
1178
- }}
1181
+ pt: LineSeriesPoint,
1182
+ evt: MouseEvent | KeyboardEvent,
1183
+ ) => {
1184
+ const fill = line_point_fill(pt, color)
1185
+ const bar_data = get_bar_data(series_idx, pt.idx, fill)
1186
+ on_bar_click?.({ ...bar_data, event: evt })
1187
+ on_point_click?.({ ...bar_data, event: evt, point: pt })
1188
+ }}
1179
1189
  {@const leaving = (evt: MouseEvent | FocusEvent) =>
1180
- (evt.relatedTarget instanceof Element
1181
- ? evt.relatedTarget.closest(`.line-points`)
1182
- : null) !== evt.currentTarget}
1190
+ (evt.relatedTarget instanceof Element
1191
+ ? evt.relatedTarget.closest(`.line-points`)
1192
+ : null) !== evt.currentTarget}
1183
1193
  <!-- svelte-ignore a11y_no_noninteractive_element_interactions, a11y_mouse_events_have_key_events -->
1184
1194
  <g
1185
1195
  class="line-points"
@@ -1213,11 +1223,13 @@
1213
1223
  {#each points as pt (pt.idx)}
1214
1224
  {@const sty = pt.point_style}
1215
1225
  {@const fl = line_point_fill(pt, color)}
1216
- {@const rad = pt.size_value != null
1217
- ? size_scale_fn(pt.size_value)
1218
- : sty?.radius ?? 4}
1219
- {@const hov = hover_info?.series_idx === series_idx &&
1220
- hover_info?.bar_idx === pt.idx}
1226
+ {@const rad =
1227
+ pt.size_value != null
1228
+ ? size_scale_fn(pt.size_value)
1229
+ : (sty?.radius ?? 4)}
1230
+ {@const hov =
1231
+ hover_info?.series_idx === series_idx &&
1232
+ hover_info?.bar_idx === pt.idx}
1221
1233
  <ScatterPoint
1222
1234
  x={pt.x}
1223
1235
  y={pt.y}
@@ -1248,31 +1260,30 @@
1248
1260
  <!-- Render as bars -->
1249
1261
  {#each srs.x as x_val, bar_idx (bar_idx)}
1250
1262
  {@const y_val = srs.y[bar_idx]}
1251
- {@const base = mode === `stacked`
1252
- ? (stacked_offsets[series_idx]?.[bar_idx] ?? 0)
1253
- : 0}
1263
+ {@const base =
1264
+ mode === `stacked` ? (stacked_offsets[series_idx]?.[bar_idx] ?? 0) : 0}
1254
1265
  {@const color = srs.color ?? bar_state.color ?? `steelblue`}
1255
1266
  {@const bar_width_val = Array.isArray(srs.bar_width)
1256
- ? (srs.bar_width[bar_idx] ?? 0.5)
1257
- : (srs.bar_width ?? 0.5)}
1267
+ ? (srs.bar_width[bar_idx] ?? 0.5)
1268
+ : (srs.bar_width ?? 0.5)}
1258
1269
  {@const is_vertical = orientation === `vertical`}
1259
1270
  {@const x_scale_bar = srs.x_axis === `x2` ? scales.x2 : scales.x}
1260
1271
  {@const [cat_scale, val_scale] = is_vertical
1261
- ? [x_scale_bar, srs.y_axis === `y2` ? scales.y2 : scales.y]
1262
- : [scales.y, x_scale_bar]}
1272
+ ? [x_scale_bar, srs.y_axis === `y2` ? scales.y2 : scales.y]
1273
+ : [scales.y, x_scale_bar]}
1263
1274
  {@const { c0, c1, v0, v1, rect_x, rect_y, rect_w, rect_h } =
1264
- compute_bar_rect({
1265
- cat_val: x_val,
1266
- val: y_val,
1267
- base,
1268
- bar_width_val,
1269
- series_idx,
1270
- mode,
1271
- orientation,
1272
- group_info,
1273
- cat_scale,
1274
- val_scale,
1275
- })}
1275
+ compute_bar_rect({
1276
+ cat_val: x_val,
1277
+ val: y_val,
1278
+ base,
1279
+ bar_width_val,
1280
+ series_idx,
1281
+ mode,
1282
+ orientation,
1283
+ group_info,
1284
+ cat_scale,
1285
+ val_scale,
1286
+ })}
1276
1287
  {#if (is_vertical ? rect_h : rect_w) > 0}
1277
1288
  <path
1278
1289
  d={bar_path(
@@ -1299,10 +1310,10 @@
1299
1310
  on_bar_hover?.(null)
1300
1311
  }}
1301
1312
  onclick={(evt) =>
1302
- on_bar_click?.({
1303
- ...get_bar_data(series_idx, bar_idx, color),
1304
- event: evt,
1305
- })}
1313
+ on_bar_click?.({
1314
+ ...get_bar_data(series_idx, bar_idx, color),
1315
+ event: evt,
1316
+ })}
1306
1317
  onkeydown={(evt) => {
1307
1318
  if (evt.key === `Enter` || evt.key === ` `) {
1308
1319
  evt.preventDefault()
@@ -1334,17 +1345,44 @@
1334
1345
 
1335
1346
  {@render ref_lines_layer(ref_lines_by_z.below_points)}
1336
1347
  {@render ref_lines_layer(ref_lines_by_z.above_all)}
1348
+
1349
+ <!-- Marginal distribution strips -->
1350
+ <PlotMarginals
1351
+ marginals={resolved_marginals}
1352
+ series={marginal_series}
1353
+ {width}
1354
+ {height}
1355
+ {pad}
1356
+ has_axis={marginal_has_axis}
1357
+ axes={{
1358
+ x1: marginal_axis(
1359
+ scales.x,
1360
+ ranges.current.x,
1361
+ x_axis,
1362
+ cat_axis === `x` ? (pos) => category_list[Math.round(pos)] : undefined,
1363
+ ),
1364
+ x2: marginal_axis(scales.x2, ranges.current.x2, x2_axis),
1365
+ y1: marginal_axis(
1366
+ scales.y,
1367
+ ranges.current.y,
1368
+ y_axis,
1369
+ cat_axis === `y` ? (pos) => category_list[Math.round(pos)] : undefined,
1370
+ ),
1371
+ y2: marginal_axis(scales.y2, ranges.current.y2, y2_axis),
1372
+ }}
1373
+ id={clip_path_id}
1374
+ />
1337
1375
  </svg>
1338
1376
 
1339
1377
  <!-- Legend -->
1340
1378
  {#if legend && (show_legend !== undefined ? show_legend : series.length > 1)}
1341
1379
  {@const legend_pos = placed_coords(
1342
- legend_auto_outside,
1343
- { x: legend_outside_x, y: legend_outside_y },
1344
- legend_placement,
1345
- legend_tween.coords.current,
1346
- { x: pad.l + 10, y: pad.t + 10 },
1347
- )}
1380
+ legend_auto_outside,
1381
+ { x: legend_outside_x, y: legend_outside_y },
1382
+ legend_placement,
1383
+ legend_tween.coords.current,
1384
+ { x: pad.l + 10, y: pad.t + 10 },
1385
+ )}
1348
1386
  <PlotLegend
1349
1387
  bind:root_element={legend_element}
1350
1388
  {...legend}
@@ -1354,9 +1392,8 @@
1354
1392
  on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
1355
1393
  on_hover_change={legend_tween.set_locked}
1356
1394
  on_item_hover={(item) =>
1357
- (hovered_legend_series_idx = item != null && item.series_idx >= 0
1358
- ? item.series_idx
1359
- : null)}
1395
+ (hovered_legend_series_idx =
1396
+ item != null && item.series_idx >= 0 ? item.series_idx : null)}
1360
1397
  active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
1361
1398
  style={`
1362
1399
  position: absolute;
@@ -1370,11 +1407,11 @@
1370
1407
 
1371
1408
  {#if hover_info && hovered}
1372
1409
  {@const cx = (hover_info.active_x_axis === `x2` ? scales.x2 : scales.x)(
1373
- hover_info.orient_x,
1374
- )}
1410
+ hover_info.orient_x,
1411
+ )}
1375
1412
  {@const cy = (hover_info.active_y_axis === `y2` ? scales.y2 : scales.y)(
1376
- hover_info.orient_y,
1377
- )}
1413
+ hover_info.orient_y,
1414
+ )}
1378
1415
  <PlotTooltip
1379
1416
  x={cx}
1380
1417
  y={cy}
@@ -1391,16 +1428,16 @@
1391
1428
  <div><strong>{series_label}</strong></div>
1392
1429
  {/if}
1393
1430
  <div>
1394
- {@html sanitize_html(hover_info.x_axis.label || `x`)}: {
1395
- (cat_axis === `x` ? hover_info.category_label : undefined) ??
1396
- format_value(hover_info.orient_x, hover_info.x_axis.format || `.3~s`)
1397
- }
1431
+ {@html sanitize_html(hover_info.x_axis.label || `x`)}: {(cat_axis === `x`
1432
+ ? hover_info.category_label
1433
+ : undefined) ??
1434
+ format_value_or_num(hover_info.orient_x, hover_info.x_axis.format)}
1398
1435
  </div>
1399
1436
  <div>
1400
- {@html sanitize_html(hover_info.y_axis.label || `y`)}: {
1401
- (cat_axis === `y` ? hover_info.category_label : undefined) ??
1402
- format_value(hover_info.orient_y, hover_info.y_axis.format || `.3~s`)
1403
- }
1437
+ {@html sanitize_html(hover_info.y_axis.label || `y`)}: {(cat_axis === `y`
1438
+ ? hover_info.category_label
1439
+ : undefined) ??
1440
+ format_value_or_num(hover_info.orient_y, hover_info.y_axis.format)}
1404
1441
  </div>
1405
1442
  {/if}
1406
1443
  </PlotTooltip>
@@ -1424,12 +1461,12 @@
1424
1461
  bind:y_axis
1425
1462
  bind:y2_axis={y2_axis_prop}
1426
1463
  bind:display
1427
- auto_x_range={auto_ranges.x as Vec2}
1428
- auto_x2_range={auto_ranges.x2 as Vec2}
1429
- auto_y_range={auto_ranges.y as Vec2}
1430
- auto_y2_range={auto_ranges.y2 as Vec2}
1431
- has_x2_points={x2_series.length > 0}
1432
- has_y2_points={y2_series.length > 0}
1464
+ auto_x_range={auto_ranges.x}
1465
+ auto_x2_range={auto_ranges.x2}
1466
+ auto_y_range={auto_ranges.y}
1467
+ auto_y2_range={auto_ranges.y2}
1468
+ has_x2_points={show_x2}
1469
+ has_y2_points={show_y2}
1433
1470
  children={controls_extra}
1434
1471
  />
1435
1472
  {/if}
@@ -1489,7 +1526,9 @@
1489
1526
  .bar-plot :global(.pane-toggle),
1490
1527
  .bar-plot .header-controls {
1491
1528
  opacity: 0;
1492
- transition: opacity 0.2s, background-color 0.2s;
1529
+ transition:
1530
+ opacity 0.2s,
1531
+ background-color 0.2s;
1493
1532
  }
1494
1533
  .bar-plot:hover :global(.pane-toggle),
1495
1534
  .bar-plot:hover .header-controls,