matterviz 0.4.1 → 0.4.3

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 (499) hide show
  1. package/dist/EmptyState.svelte +10 -4
  2. package/dist/FilePicker.svelte +48 -60
  3. package/dist/Icon.svelte +7 -4
  4. package/dist/MillerIndexInput.svelte +2 -2
  5. package/dist/api/optimade.js +10 -7
  6. package/dist/app.css +68 -0
  7. package/dist/brillouin/BrillouinZone.svelte +90 -91
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
  9. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
  11. package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
  12. package/dist/brillouin/BrillouinZoneScene.svelte +46 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  14. package/dist/brillouin/compute.js +10 -16
  15. package/dist/brillouin/geometry.js +2 -2
  16. package/dist/chempot-diagram/ChemPotDiagram.svelte +48 -50
  17. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +61 -169
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +559 -755
  20. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
  21. package/dist/chempot-diagram/async-compute.svelte.js +13 -14
  22. package/dist/chempot-diagram/color.d.ts +21 -1
  23. package/dist/chempot-diagram/color.js +70 -1
  24. package/dist/chempot-diagram/compute.d.ts +7 -0
  25. package/dist/chempot-diagram/compute.js +85 -26
  26. package/dist/chempot-diagram/controls-state.svelte.js +1 -1
  27. package/dist/chempot-diagram/export.js +1 -6
  28. package/dist/chempot-diagram/temperature.js +9 -10
  29. package/dist/colors/index.d.ts +0 -4
  30. package/dist/colors/index.js +19 -20
  31. package/dist/composition/BarChart.svelte +30 -26
  32. package/dist/composition/BubbleChart.svelte +17 -17
  33. package/dist/composition/Composition.svelte +10 -19
  34. package/dist/composition/Formula.svelte +20 -27
  35. package/dist/composition/FormulaFilter.svelte +115 -184
  36. package/dist/composition/PieChart.svelte +55 -41
  37. package/dist/composition/format.js +7 -9
  38. package/dist/composition/index.d.ts +2 -0
  39. package/dist/composition/index.js +14 -0
  40. package/dist/composition/parse.d.ts +2 -2
  41. package/dist/composition/parse.js +61 -115
  42. package/dist/constants.d.ts +0 -1
  43. package/dist/constants.js +17 -26
  44. package/dist/convex-hull/ConvexHull.svelte +12 -15
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +93 -94
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +127 -186
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +131 -180
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
  53. package/dist/convex-hull/ConvexHullControls.svelte +46 -48
  54. package/dist/convex-hull/ConvexHullStats.svelte +193 -195
  55. package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
  56. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  57. package/dist/convex-hull/StructurePopup.svelte +18 -22
  58. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  59. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  60. package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
  61. package/dist/convex-hull/gas-thermodynamics.js +21 -53
  62. package/dist/convex-hull/helpers.d.ts +8 -10
  63. package/dist/convex-hull/helpers.js +37 -43
  64. package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
  65. package/dist/convex-hull/thermodynamics.js +37 -44
  66. package/dist/coordination/CoordinationBarPlot.svelte +49 -53
  67. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  68. package/dist/coordination/calc-coordination.d.ts +1 -1
  69. package/dist/coordination/calc-coordination.js +28 -25
  70. package/dist/element/BohrAtom.svelte +5 -9
  71. package/dist/element/ElementHeading.svelte +4 -1
  72. package/dist/element/ElementPhoto.svelte +11 -9
  73. package/dist/element/ElementStats.svelte +12 -10
  74. package/dist/element/ElementTile.svelte +44 -67
  75. package/dist/element/Nucleus.svelte +4 -2
  76. package/dist/element/data.d.ts +3 -2
  77. package/dist/element/data.js +5 -1
  78. package/dist/element/helpers.d.ts +4 -0
  79. package/dist/element/helpers.js +18 -0
  80. package/dist/element/index.d.ts +2 -5
  81. package/dist/element/index.js +4 -19
  82. package/dist/feedback/DragOverlay.svelte +5 -5
  83. package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
  84. package/dist/feedback/StatusMessage.svelte +7 -4
  85. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  86. package/dist/fermi-surface/FermiSurface.svelte +35 -44
  87. package/dist/fermi-surface/FermiSurface.svelte.d.ts +7 -13
  88. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  89. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  90. package/dist/fermi-surface/FermiSurfaceScene.svelte +64 -136
  91. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  92. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  93. package/dist/fermi-surface/compute.js +25 -58
  94. package/dist/fermi-surface/parse.js +54 -88
  95. package/dist/file-viewer/JsonBrowser.svelte +1043 -0
  96. package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
  97. package/dist/file-viewer/PlotPanel.svelte +350 -0
  98. package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
  99. package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
  100. package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
  101. package/dist/file-viewer/detect.d.ts +11 -0
  102. package/dist/file-viewer/detect.js +381 -0
  103. package/dist/file-viewer/eligibility.d.ts +4 -0
  104. package/dist/file-viewer/eligibility.js +53 -0
  105. package/dist/file-viewer/host-protocol.d.ts +50 -0
  106. package/dist/file-viewer/host-transfer.d.ts +25 -0
  107. package/dist/file-viewer/host-transfer.js +42 -0
  108. package/dist/file-viewer/index.d.ts +5 -0
  109. package/dist/file-viewer/index.js +5 -0
  110. package/dist/file-viewer/main.d.ts +44 -0
  111. package/dist/file-viewer/main.js +535 -0
  112. package/dist/file-viewer/parse.d.ts +9 -0
  113. package/dist/file-viewer/parse.js +170 -0
  114. package/dist/file-viewer/plot-utils.d.ts +24 -0
  115. package/dist/file-viewer/plot-utils.js +191 -0
  116. package/dist/file-viewer/types.d.ts +4 -0
  117. package/dist/file-viewer/types.js +5 -0
  118. package/dist/heatmap-matrix/HeatmapMatrix.svelte +153 -219
  119. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
  120. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
  121. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  122. package/dist/heatmap-matrix/index.d.ts +5 -0
  123. package/dist/icons.d.ts +4 -0
  124. package/dist/icons.js +4 -0
  125. package/dist/index.d.ts +2 -2
  126. package/dist/index.js +1 -1
  127. package/dist/io/ExportPane.svelte +11 -7
  128. package/dist/io/decompress.d.ts +4 -1
  129. package/dist/io/decompress.js +24 -36
  130. package/dist/io/export.d.ts +11 -4
  131. package/dist/io/export.js +75 -19
  132. package/dist/io/fetch.d.ts +0 -3
  133. package/dist/io/fetch.js +0 -11
  134. package/dist/io/file-drop.js +60 -35
  135. package/dist/io/index.d.ts +0 -1
  136. package/dist/io/index.js +2 -9
  137. package/dist/io/is-binary.d.ts +9 -0
  138. package/dist/io/is-binary.js +45 -1
  139. package/dist/io/url-drop.d.ts +2 -0
  140. package/dist/io/url-drop.js +40 -40
  141. package/dist/isosurface/Isosurface.svelte +700 -215
  142. package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
  143. package/dist/isosurface/IsosurfaceControls.svelte +590 -116
  144. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
  145. package/dist/isosurface/VolumeSlice.svelte +224 -0
  146. package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
  147. package/dist/isosurface/coloring.d.ts +20 -0
  148. package/dist/isosurface/coloring.js +118 -0
  149. package/dist/isosurface/geometry-worker-types.d.ts +38 -0
  150. package/dist/isosurface/geometry.worker.d.ts +1 -0
  151. package/dist/isosurface/geometry.worker.js +59 -0
  152. package/dist/isosurface/grid.d.ts +10 -0
  153. package/dist/isosurface/grid.js +31 -0
  154. package/dist/isosurface/index.d.ts +4 -0
  155. package/dist/isosurface/index.js +4 -0
  156. package/dist/isosurface/parse-vaspwave.d.ts +3 -0
  157. package/dist/isosurface/parse-vaspwave.js +138 -0
  158. package/dist/isosurface/parse.js +6 -6
  159. package/dist/isosurface/profile.d.ts +12 -0
  160. package/dist/isosurface/profile.js +19 -0
  161. package/dist/isosurface/sampling.d.ts +34 -0
  162. package/dist/isosurface/sampling.js +408 -0
  163. package/dist/isosurface/slice-rendering.d.ts +13 -0
  164. package/dist/isosurface/slice-rendering.js +79 -0
  165. package/dist/isosurface/slice.d.ts +22 -2
  166. package/dist/isosurface/slice.js +200 -116
  167. package/dist/isosurface/types.d.ts +24 -1
  168. package/dist/isosurface/types.js +120 -8
  169. package/dist/json-path.d.ts +4 -0
  170. package/dist/json-path.js +89 -0
  171. package/dist/labels.d.ts +6 -2
  172. package/dist/labels.js +44 -14
  173. package/dist/layout/FullscreenButton.svelte +2 -1
  174. package/dist/layout/FullscreenToggle.svelte +10 -4
  175. package/dist/layout/InfoCard.svelte +8 -21
  176. package/dist/layout/InfoTag.svelte +1 -3
  177. package/dist/layout/NumberRangeInput.svelte +46 -0
  178. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  179. package/dist/layout/PropertyFilter.svelte +2 -6
  180. package/dist/layout/SettingsSection.svelte +57 -62
  181. package/dist/layout/SettingsSection.svelte.d.ts +6 -7
  182. package/dist/layout/SubpageGrid.svelte +3 -1
  183. package/dist/layout/ViewerChrome.svelte +16 -11
  184. package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
  185. package/dist/layout/fullscreen.js +3 -5
  186. package/dist/layout/fullscreen.svelte.js +1 -2
  187. package/dist/layout/index.d.ts +1 -0
  188. package/dist/layout/index.js +1 -0
  189. package/dist/layout/json-tree/JsonNode.svelte +22 -59
  190. package/dist/layout/json-tree/JsonTree.svelte +101 -172
  191. package/dist/layout/json-tree/JsonValue.svelte +9 -32
  192. package/dist/layout/json-tree/utils.d.ts +0 -3
  193. package/dist/layout/json-tree/utils.js +50 -188
  194. package/dist/marching-cubes.d.ts +8 -1
  195. package/dist/marching-cubes.js +115 -48
  196. package/dist/math.d.ts +5 -2
  197. package/dist/math.js +48 -38
  198. package/dist/overlays/ContextMenu.svelte +19 -34
  199. package/dist/overlays/CopyButton.svelte +11 -5
  200. package/dist/overlays/DragControlTab.svelte +6 -1
  201. package/dist/overlays/DraggablePane.svelte +110 -74
  202. package/dist/overlays/DraggablePane.svelte.d.ts +2 -1
  203. package/dist/overlays/GlassChip.svelte +32 -0
  204. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  205. package/dist/overlays/InfoPaneCards.svelte +11 -23
  206. package/dist/overlays/index.d.ts +7 -0
  207. package/dist/overlays/index.js +26 -0
  208. package/dist/overlays/portal.d.ts +2 -0
  209. package/dist/overlays/portal.js +14 -0
  210. package/dist/periodic-table/PeriodicTable.svelte +113 -145
  211. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  212. package/dist/periodic-table/PeriodicTableControls.svelte +37 -115
  213. package/dist/periodic-table/TableInset.svelte +9 -4
  214. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +68 -105
  215. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  216. package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
  217. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  218. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  219. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
  220. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  221. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  222. package/dist/phase-diagram/build-diagram.js +4 -9
  223. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  224. package/dist/phase-diagram/parse.js +15 -15
  225. package/dist/phase-diagram/svg-to-diagram.js +28 -27
  226. package/dist/phase-diagram/utils.js +2 -3
  227. package/dist/plot/bar/BarPlot.svelte +461 -426
  228. package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
  229. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  230. package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
  231. package/dist/plot/bar/SpacegroupBarPlot.svelte +46 -77
  232. package/dist/plot/bar/data.d.ts +5 -4
  233. package/dist/plot/bar/data.js +2 -1
  234. package/dist/plot/box/BoxPlot.svelte +322 -226
  235. package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
  236. package/dist/plot/box/Violin.svelte.d.ts +1 -0
  237. package/dist/plot/box/box-plot.js +1 -1
  238. package/dist/plot/core/auto-place.d.ts +1 -1
  239. package/dist/plot/core/auto-place.js +2 -2
  240. package/dist/plot/core/axis-utils.d.ts +12 -7
  241. package/dist/plot/core/axis-utils.js +48 -48
  242. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  243. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  244. package/dist/plot/core/components/ColorBar.svelte +101 -106
  245. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  246. package/dist/plot/core/components/FillArea.svelte +10 -17
  247. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  248. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  249. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  250. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  251. package/dist/plot/core/components/Line.svelte +18 -9
  252. package/dist/plot/core/components/Line.svelte.d.ts +2 -0
  253. package/dist/plot/core/components/PlotAxis.svelte +40 -17
  254. package/dist/plot/core/components/PlotControls.svelte +65 -41
  255. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  256. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  257. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  258. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  259. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  260. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  261. package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
  262. package/dist/plot/core/components/ReferencePlane.svelte +7 -8
  263. package/dist/plot/core/components/ZeroLines.svelte +4 -8
  264. package/dist/plot/core/components/ZoomRect.svelte +1 -2
  265. package/dist/plot/core/components/index.d.ts +1 -0
  266. package/dist/plot/core/components/index.js +1 -0
  267. package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
  268. package/dist/plot/core/data-cleaning-signal.js +467 -0
  269. package/dist/plot/core/data-cleaning.d.ts +2 -42
  270. package/dist/plot/core/data-cleaning.js +27 -474
  271. package/dist/plot/core/data-transform.d.ts +1 -2
  272. package/dist/plot/core/data-transform.js +9 -15
  273. package/dist/plot/core/fill-utils.d.ts +3 -1
  274. package/dist/plot/core/fill-utils.js +28 -6
  275. package/dist/plot/core/index.d.ts +1 -0
  276. package/dist/plot/core/index.js +1 -0
  277. package/dist/plot/core/interactions.js +0 -2
  278. package/dist/plot/core/layout.d.ts +15 -6
  279. package/dist/plot/core/layout.js +57 -38
  280. package/dist/plot/core/marginals.d.ts +170 -0
  281. package/dist/plot/core/marginals.js +436 -0
  282. package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
  283. package/dist/plot/core/pan-zoom.svelte.js +34 -17
  284. package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
  285. package/dist/plot/core/placed-tween.svelte.js +68 -21
  286. package/dist/plot/core/reference-line.js +1 -1
  287. package/dist/plot/core/scales.d.ts +23 -0
  288. package/dist/plot/core/scales.js +39 -13
  289. package/dist/plot/core/svg.js +0 -1
  290. package/dist/plot/core/types/fills.d.ts +101 -0
  291. package/dist/plot/core/types/fills.js +15 -0
  292. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  293. package/dist/plot/core/types/plot-3d.js +4 -0
  294. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  295. package/dist/plot/core/types/reference-lines.js +9 -0
  296. package/dist/plot/core/types.d.ts +29 -324
  297. package/dist/plot/core/types.js +23 -20
  298. package/dist/plot/core/utils/hierarchy-chart.d.ts +52 -0
  299. package/dist/plot/core/utils/hierarchy-chart.js +191 -0
  300. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  301. package/dist/plot/core/utils/hierarchy-labels.js +100 -0
  302. package/dist/plot/core/utils/label-placement.js +2 -2
  303. package/dist/plot/histogram/Histogram.svelte +314 -340
  304. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  305. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  306. package/dist/plot/histogram/histogram.d.ts +31 -0
  307. package/dist/plot/histogram/histogram.js +70 -0
  308. package/dist/plot/histogram/index.d.ts +1 -0
  309. package/dist/plot/histogram/index.js +1 -0
  310. package/dist/plot/index.d.ts +1 -0
  311. package/dist/plot/index.js +1 -0
  312. package/dist/plot/sankey/Sankey.svelte +78 -69
  313. package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
  314. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  315. package/dist/plot/sankey/sankey.js +13 -9
  316. package/dist/plot/scatter/BinnedScatterPlot.svelte +311 -141
  317. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
  318. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  319. package/dist/plot/scatter/ScatterPlot.svelte +517 -410
  320. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
  321. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  322. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  323. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  324. package/dist/plot/scatter/adaptive-density.d.ts +1 -1
  325. package/dist/plot/scatter/adaptive-density.js +36 -20
  326. package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
  327. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  328. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  329. package/dist/plot/scatter/scatter-data.js +8 -7
  330. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
  331. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
  332. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
  333. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
  334. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
  335. package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
  336. package/dist/plot/sunburst/Sunburst.svelte +269 -347
  337. package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
  338. package/dist/plot/sunburst/index.d.ts +1 -1
  339. package/dist/plot/sunburst/index.js +2 -1
  340. package/dist/plot/sunburst/render.d.ts +2 -2
  341. package/dist/plot/sunburst/render.js +47 -22
  342. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  343. package/dist/plot/sunburst/sunburst.js +10 -3
  344. package/dist/plot/treemap/Treemap.svelte +1022 -0
  345. package/dist/plot/treemap/Treemap.svelte.d.ts +90 -0
  346. package/dist/plot/treemap/index.d.ts +4 -0
  347. package/dist/plot/treemap/index.js +4 -0
  348. package/dist/plot/treemap/labels.d.ts +38 -0
  349. package/dist/plot/treemap/labels.js +80 -0
  350. package/dist/plot/treemap/treemap.d.ts +28 -0
  351. package/dist/plot/treemap/treemap.js +102 -0
  352. package/dist/rdf/RdfPlot.svelte +37 -59
  353. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  354. package/dist/rdf/calc-rdf.js +63 -52
  355. package/dist/rdf/index.d.ts +0 -1
  356. package/dist/sanitize.js +85 -27
  357. package/dist/scene/SceneCamera.svelte +1 -1
  358. package/dist/scene/SceneCamera.svelte.d.ts +1 -1
  359. package/dist/scene/bind-renderer.svelte.js +14 -6
  360. package/dist/scene/index.d.ts +1 -2
  361. package/dist/scene/index.js +1 -1
  362. package/dist/scene/{props.js → props.svelte.js} +15 -1
  363. package/dist/settings.d.ts +19 -9
  364. package/dist/settings.js +229 -525
  365. package/dist/spectral/Bands.svelte +207 -221
  366. package/dist/spectral/BandsAndDos.svelte +11 -9
  367. package/dist/spectral/BrillouinBandsDos.svelte +19 -42
  368. package/dist/spectral/Dos.svelte +92 -93
  369. package/dist/spectral/helpers.d.ts +3 -3
  370. package/dist/spectral/helpers.js +65 -140
  371. package/dist/state.svelte.js +5 -18
  372. package/dist/structure/Arrow.svelte +5 -18
  373. package/dist/structure/ArrowInstances.svelte +160 -0
  374. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  375. package/dist/structure/AtomLegend.svelte +120 -85
  376. package/dist/structure/AtomLegend.svelte.d.ts +2 -3
  377. package/dist/structure/Bond.svelte +31 -23
  378. package/dist/structure/CanvasTooltip.svelte +15 -3
  379. package/dist/structure/CellSelect.svelte +45 -35
  380. package/dist/structure/Cylinder.svelte +24 -40
  381. package/dist/structure/InstancedAtoms.svelte +114 -0
  382. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  383. package/dist/structure/Lattice.svelte +85 -149
  384. package/dist/structure/SiteLabels.svelte +122 -0
  385. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  386. package/dist/structure/Structure.svelte +636 -504
  387. package/dist/structure/Structure.svelte.d.ts +4 -2
  388. package/dist/structure/StructureCarousel.svelte +637 -0
  389. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  390. package/dist/structure/StructureControls.svelte +165 -425
  391. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  392. package/dist/structure/StructureExportPane.svelte +17 -21
  393. package/dist/structure/StructureInfoPane.svelte +128 -133
  394. package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
  395. package/dist/structure/StructureScene.svelte +535 -578
  396. package/dist/structure/StructureScene.svelte.d.ts +6 -2
  397. package/dist/structure/StructureViewport.svelte +451 -0
  398. package/dist/structure/StructureViewport.svelte.d.ts +65 -0
  399. package/dist/structure/atom-label-placement.d.ts +16 -12
  400. package/dist/structure/atom-label-placement.js +70 -37
  401. package/dist/structure/atom-properties.d.ts +2 -0
  402. package/dist/structure/atom-properties.js +105 -38
  403. package/dist/structure/bond-order-perception.js +3 -8
  404. package/dist/structure/bonding.d.ts +9 -3
  405. package/dist/structure/bonding.js +95 -46
  406. package/dist/structure/edit-history.d.ts +6 -0
  407. package/dist/structure/edit-history.js +27 -0
  408. package/dist/structure/export.d.ts +1 -0
  409. package/dist/structure/export.js +89 -105
  410. package/dist/structure/format-detect.d.ts +1 -0
  411. package/dist/structure/format-detect.js +20 -0
  412. package/dist/structure/geometry.d.ts +9 -0
  413. package/dist/structure/geometry.js +27 -0
  414. package/dist/structure/index.d.ts +16 -1
  415. package/dist/structure/index.js +15 -10
  416. package/dist/structure/measure.d.ts +1 -1
  417. package/dist/structure/measure.js +6 -12
  418. package/dist/structure/parse.d.ts +2 -1
  419. package/dist/structure/parse.js +272 -313
  420. package/dist/structure/partial-occupancy.js +10 -5
  421. package/dist/structure/pbc.js +85 -107
  422. package/dist/structure/polyhedra.d.ts +0 -1
  423. package/dist/structure/polyhedra.js +7 -21
  424. package/dist/structure/prop-groups.d.ts +85 -0
  425. package/dist/structure/prop-groups.js +18 -0
  426. package/dist/structure/supercell.d.ts +1 -2
  427. package/dist/structure/supercell.js +7 -13
  428. package/dist/symmetry/SymmetryElements.svelte +11 -15
  429. package/dist/symmetry/SymmetryStats.svelte +55 -71
  430. package/dist/symmetry/WyckoffTable.svelte +10 -28
  431. package/dist/symmetry/cell-transform.js +6 -14
  432. package/dist/symmetry/index.d.ts +1 -0
  433. package/dist/symmetry/index.js +13 -10
  434. package/dist/symmetry/spacegroups.js +3 -2
  435. package/dist/table/HeatmapTable.svelte +1142 -447
  436. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  437. package/dist/table/ToggleMenu.svelte +168 -53
  438. package/dist/table/ToggleMenu.svelte.d.ts +1 -1
  439. package/dist/table/index.d.ts +21 -6
  440. package/dist/table/index.js +34 -21
  441. package/dist/theme/ThemeControl.svelte +16 -8
  442. package/dist/theme/embedded.d.ts +18 -0
  443. package/dist/theme/embedded.js +206 -0
  444. package/dist/theme/index.d.ts +5 -5
  445. package/dist/theme/index.js +5 -12
  446. package/dist/theme/themes.mjs +44 -66
  447. package/dist/tooltip/KCoords.svelte +4 -1
  448. package/dist/tooltip/TooltipContent.svelte +3 -6
  449. package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
  450. package/dist/tooltip/index.d.ts +8 -1
  451. package/dist/trajectory/Trajectory.svelte +613 -386
  452. package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
  453. package/dist/trajectory/TrajectoryError.svelte +12 -74
  454. package/dist/trajectory/TrajectoryExportPane.svelte +14 -32
  455. package/dist/trajectory/TrajectoryInfoPane.svelte +127 -164
  456. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
  457. package/dist/trajectory/extract.js +61 -49
  458. package/dist/trajectory/format-detect.d.ts +2 -0
  459. package/dist/trajectory/format-detect.js +17 -13
  460. package/dist/trajectory/frame-reader.d.ts +2 -0
  461. package/dist/trajectory/frame-reader.js +35 -32
  462. package/dist/trajectory/helpers.d.ts +2 -0
  463. package/dist/trajectory/helpers.js +34 -56
  464. package/dist/trajectory/index.d.ts +1 -0
  465. package/dist/trajectory/index.js +5 -8
  466. package/dist/trajectory/parse/ase.js +1 -1
  467. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  468. package/dist/trajectory/parse/h5-utils.js +109 -0
  469. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  470. package/dist/trajectory/parse/hdf5.js +116 -106
  471. package/dist/trajectory/parse/index.d.ts +6 -6
  472. package/dist/trajectory/parse/index.js +25 -33
  473. package/dist/trajectory/parse/lammps.js +8 -12
  474. package/dist/trajectory/parse/vasp.js +10 -7
  475. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  476. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  477. package/dist/trajectory/parse/vaspout-h5.d.ts +4 -0
  478. package/dist/trajectory/parse/vaspout-h5.js +281 -0
  479. package/dist/trajectory/parse/xyz.js +59 -9
  480. package/dist/trajectory/plotting.d.ts +8 -9
  481. package/dist/trajectory/plotting.js +113 -64
  482. package/dist/utils.d.ts +3 -0
  483. package/dist/utils.js +7 -0
  484. package/dist/xrd/XrdPlot.svelte +79 -118
  485. package/dist/xrd/broadening.js +2 -1
  486. package/dist/xrd/calc-xrd.js +24 -62
  487. package/dist/xrd/parse.d.ts +0 -1
  488. package/dist/xrd/parse.js +69 -95
  489. package/package.json +94 -34
  490. package/readme.md +6 -5
  491. package/dist/api/mp.d.ts +0 -6
  492. package/dist/api/mp.js +0 -22
  493. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
  494. package/dist/scene/types.d.ts +0 -26
  495. package/dist/tooltip/types.d.ts +0 -8
  496. package/dist/trajectory/constants.d.ts +0 -6
  497. package/dist/trajectory/constants.js +0 -11
  498. /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
  499. /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
@@ -2,7 +2,7 @@
2
2
  lang="ts"
3
3
  generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
4
4
  >
5
- import { format_value } from '../../labels'
5
+ import { format_value_or_num } from '../../labels'
6
6
  import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
7
7
  import type { Vec2 } from '../../math'
8
8
  import type {
@@ -28,8 +28,17 @@
28
28
  compute_element_placement,
29
29
  PlotAxis,
30
30
  PlotLegend,
31
+ PlotMarginals,
31
32
  ReferenceLine,
32
33
  } from '..'
34
+ import type { MarginalSeriesInput, MarginalsProp } from '../core/marginals'
35
+ import {
36
+ add_sides,
37
+ marginal_axis,
38
+ marginal_axis_presence,
39
+ normalize_marginals,
40
+ reserve_marginal_pad,
41
+ } from '../core/marginals'
33
42
  import {
34
43
  build_obstacles_norm,
35
44
  clip_bar,
@@ -49,7 +58,9 @@
49
58
  resolve_axis_ranges,
50
59
  } from '../core/interactions'
51
60
  import {
61
+ AXIS_TITLE_OFFSET,
52
62
  calc_auto_padding,
63
+ DEFAULT_PLOT_PADDING,
53
64
  filter_padding,
54
65
  LABEL_GAP_DEFAULT,
55
66
  y2_axis_label_x,
@@ -59,7 +70,7 @@
59
70
  import type { IndexedRefLine } from '../core/reference-line'
60
71
  import { group_ref_lines_by_z, index_ref_lines } from '../core/reference-line'
61
72
  import {
62
- create_scale,
73
+ create_axis_scales,
63
74
  generate_ticks,
64
75
  get_nice_data_range,
65
76
  get_tick_label,
@@ -114,8 +125,8 @@
114
125
  y_axis = $bindable({}),
115
126
  y2_axis: y2_axis_prop = $bindable({}),
116
127
  display = $bindable(DEFAULTS.box.display),
117
- range_padding = 0.05,
118
- padding = { t: 20, b: 60, l: 60, r: 20 },
128
+ range_padding = 0,
129
+ padding = DEFAULT_PLOT_PADDING,
119
130
  legend = {},
120
131
  show_legend,
121
132
  box = {},
@@ -129,7 +140,7 @@
129
140
  show_mean = $bindable(DEFAULTS.box.show_mean),
130
141
  show_value_labels = false,
131
142
  value_label_stat = `median`,
132
- value_label_format = `.3~s`,
143
+ value_label_format = ``,
133
144
  kind = $bindable(DEFAULTS.box.kind),
134
145
  side = $bindable(DEFAULTS.box.side),
135
146
  bandwidth = DEFAULTS.box.bandwidth,
@@ -158,51 +169,55 @@
158
169
  header_controls,
159
170
  controls_extra,
160
171
  pan = {},
172
+ marginals = false,
161
173
  ...rest
162
- }: HTMLAttributes<HTMLDivElement> & BasePlotProps & PlotConfig & {
163
- series?: BoxPlotSeries<Metadata>[]
164
- orientation?: Orientation
165
- legend?: LegendConfig | null
166
- show_legend?: boolean
167
- box?: BoxStyle
168
- whisker?: WhiskerStyle
169
- median_style?: BoxLineStyle
170
- outlier_style?: OutlierStyle
171
- whisker_mode?: WhiskerMode
172
- whisker_range?: number
173
- whisker_percentiles?: Vec2
174
- show_outliers?: boolean
175
- show_mean?: boolean
176
- show_value_labels?: boolean
177
- value_label_stat?: `median` | `mean`
178
- value_label_format?: string
179
- kind?: ViolinKind
180
- side?: ViolinSide
181
- bandwidth?: BandwidthOption
182
- violin_width?: number
183
- violin_style?: ViolinStyle
184
- kde_points?: number
185
- kde_cut?: number
186
- kde_max_samples?: number
187
- kde_clip?: [number | null, number | null]
188
- tooltip?: Snippet<[BoxHandlerProps<Metadata>]>
189
- user_content?: Snippet<[UserContentProps]>
190
- header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
191
- controls_extra?: Snippet<[{ orientation: Orientation } & Required<PlotConfig>]>
192
- change?: (data: BoxHandlerProps<Metadata> | null) => void
193
- on_box_click?: (
194
- data: BoxHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
195
- ) => void
196
- on_box_hover?: (
197
- data:
198
- | (BoxHandlerProps<Metadata> & { event: MouseEvent | FocusEvent | KeyboardEvent })
199
- | null,
200
- ) => void
201
- ref_lines?: RefLine[]
202
- on_ref_line_click?: (event: RefLineEvent) => void
203
- on_ref_line_hover?: (event: RefLineEvent | null) => void
204
- pan?: PanConfig
205
- } = $props()
174
+ }: HTMLAttributes<HTMLDivElement> &
175
+ BasePlotProps &
176
+ PlotConfig & {
177
+ series?: BoxPlotSeries<Metadata>[]
178
+ orientation?: Orientation
179
+ legend?: LegendConfig | null
180
+ show_legend?: boolean
181
+ box?: BoxStyle
182
+ whisker?: WhiskerStyle
183
+ median_style?: BoxLineStyle
184
+ outlier_style?: OutlierStyle
185
+ whisker_mode?: WhiskerMode
186
+ whisker_range?: number
187
+ whisker_percentiles?: Vec2
188
+ show_outliers?: boolean
189
+ show_mean?: boolean
190
+ show_value_labels?: boolean
191
+ value_label_stat?: `median` | `mean`
192
+ value_label_format?: string
193
+ kind?: ViolinKind
194
+ side?: ViolinSide
195
+ bandwidth?: BandwidthOption
196
+ violin_width?: number
197
+ violin_style?: ViolinStyle
198
+ kde_points?: number
199
+ kde_cut?: number
200
+ kde_max_samples?: number
201
+ kde_clip?: [number | null, number | null]
202
+ tooltip?: Snippet<[BoxHandlerProps<Metadata>]>
203
+ user_content?: Snippet<[UserContentProps]>
204
+ header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
205
+ controls_extra?: Snippet<[{ orientation: Orientation } & Required<PlotConfig>]>
206
+ change?: (data: BoxHandlerProps<Metadata> | null) => void
207
+ on_box_click?: (
208
+ data: BoxHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
209
+ ) => void
210
+ on_box_hover?: (
211
+ data:
212
+ | (BoxHandlerProps<Metadata> & { event: MouseEvent | FocusEvent | KeyboardEvent })
213
+ | null,
214
+ ) => void
215
+ ref_lines?: RefLine[]
216
+ on_ref_line_click?: (event: RefLineEvent) => void
217
+ on_ref_line_hover?: (event: RefLineEvent | null) => void
218
+ pan?: PanConfig
219
+ marginals?: MarginalsProp
220
+ } = $props()
206
221
 
207
222
  let box_state = $derived({ ...DEFAULTS.box.box, ...box })
208
223
  let whisker_state = $derived({ ...DEFAULTS.box.whisker, ...whisker })
@@ -212,24 +227,20 @@
212
227
 
213
228
  // Merge secondary-axis defaults as deriveds instead of assigning back into the
214
229
  // $bindable props (which would push library defaults into the parent's bound state)
215
- let y2_axis = $derived(
216
- {
217
- format: ``,
218
- scale_type: `linear`,
219
- ticks: 5,
220
- range: [null, null],
221
- ...y2_axis_prop,
222
- } as typeof y2_axis_prop,
223
- )
224
- let x2_axis = $derived(
225
- {
226
- format: ``,
227
- scale_type: `linear`,
228
- ticks: 5,
229
- range: [null, null],
230
- ...x2_axis_prop,
231
- } as typeof x2_axis_prop,
232
- )
230
+ let y2_axis = $derived({
231
+ format: ``,
232
+ scale_type: `linear`,
233
+ ticks: 5,
234
+ range: [null, null],
235
+ ...y2_axis_prop,
236
+ } as typeof y2_axis_prop)
237
+ let x2_axis = $derived({
238
+ format: ``,
239
+ scale_type: `linear`,
240
+ ticks: 5,
241
+ range: [null, null],
242
+ ...x2_axis_prop,
243
+ } as typeof x2_axis_prop)
233
244
 
234
245
  let [width, height] = $state([0, 0])
235
246
  let wrapper: HTMLDivElement | undefined = $state()
@@ -256,7 +267,7 @@
256
267
  whisker_range: srs.whisker_range ?? whisker_range,
257
268
  whisker_percentiles: srs.whisker_percentiles ?? whisker_percentiles,
258
269
  collect_outliers: show_outliers && draws_box(srs) && (srs.visible ?? true),
259
- })
270
+ }),
260
271
  ),
261
272
  )
262
273
 
@@ -309,9 +320,8 @@
309
320
  // KDE per visible violin series, keyed by series index (bandwidth from the full sample)
310
321
  let violin_kdes = $derived.by(() => {
311
322
  const map = new SvelteMap<number, KdeResult>()
312
- const [val_axis, val_axis2] = orientation === `vertical`
313
- ? [y_axis, y2_axis]
314
- : [x_axis, x2_axis]
323
+ const [val_axis, val_axis2] =
324
+ orientation === `vertical` ? [y_axis, y2_axis] : [x_axis, x2_axis]
315
325
  for (const box_item of visible_boxes) {
316
326
  if (!draws_violin(box_item.series)) continue
317
327
  const samples = box_item.series.y ?? []
@@ -319,7 +329,10 @@
319
329
  // On a log value axis the KDE grid tail (data_min - cut*bandwidth) is usually <= 0 →
320
330
  // NaN pixels + LOG_EPS range pollution. Clamp the grid to the smallest positive sample.
321
331
  if ((is_secondary(box_item.series) ? val_axis2 : val_axis).scale_type === `log`) {
322
- const min_pos = samples.reduce((min, val) => (val > 0 && val < min ? val : min), Infinity)
332
+ const min_pos = samples.reduce(
333
+ (min, val) => (val > 0 && val < min ? val : min),
334
+ Infinity,
335
+ )
323
336
  // Guard: no positive samples → min_pos is Infinity; leave clip unchanged so the KDE
324
337
  // never receives a non-finite lower bound
325
338
  if (Number.isFinite(min_pos)) {
@@ -343,7 +356,11 @@
343
356
  // The horizontal category pixel axis is inverted, so flip the half-violin side to keep
344
357
  // `positive` meaning "above the center line" (vertical/`both` pass through unchanged)
345
358
  const to_screen_side = (eff_side: ViolinSide, vertical: boolean): ViolinSide =>
346
- vertical || eff_side === `both` ? eff_side : eff_side === `positive` ? `negative` : `positive`
359
+ vertical || eff_side === `both`
360
+ ? eff_side
361
+ : eff_side === `positive`
362
+ ? `negative`
363
+ : `positive`
347
364
 
348
365
  // Peak density per violin, computed once on data change (avoids spreading kde.density into
349
366
  // Math.max — unsafe for large kde_points — and re-deriving it on every render/hover).
@@ -360,8 +377,15 @@
360
377
  // Which boxes live on the secondary value axis (y2 for vertical, x2 for horizontal)
361
378
  const is_secondary = (srs: BoxPlotSeries<Metadata>): boolean =>
362
379
  orientation === `vertical` ? srs.y_axis === `y2` : srs.x_axis === `x2`
363
- let secondary_boxes = $derived(visible_boxes.filter((box_item) => is_secondary(box_item.series)))
380
+ let secondary_boxes = $derived(
381
+ visible_boxes.filter((box_item) => is_secondary(box_item.series)),
382
+ )
364
383
  let has_secondary = $derived(secondary_boxes.length > 0)
384
+ // The secondary value axis renders transposed by orientation: x2 (top) when horizontal, y2
385
+ // (right) when vertical. Derive once so axis rendering, ticks, range writes, point picking, and
386
+ // marginal placement all stay provably in sync.
387
+ let show_x2 = $derived(has_secondary && orientation === `horizontal`)
388
+ let show_y2 = $derived(has_secondary && orientation === `vertical`)
365
389
 
366
390
  // Collect value-axis points (whiskers, quartiles, outliers, KDE tails) for auto-range
367
391
  const value_points = (boxes: Box[]): { x: number; y: number }[] =>
@@ -407,8 +431,8 @@
407
431
  )
408
432
 
409
433
  return vertical
410
- ? ({ x: cat_range, x2: [0, 1] as Vec2, y: value_primary, y2: value_secondary })
411
- : ({ x: value_primary, x2: value_secondary, y: cat_range, y2: [0, 1] as Vec2 })
434
+ ? { x: cat_range, x2: [0, 1] as Vec2, y: value_primary, y2: value_secondary }
435
+ : { x: value_primary, x2: value_secondary, y: cat_range, y2: [0, 1] as Vec2 }
412
436
  })
413
437
 
414
438
  let ranges = $state<{
@@ -419,10 +443,14 @@
419
443
  current: { x: [0, 1], x2: [0, 1], y: [0, 1], y2: [0, 1] },
420
444
  })
421
445
 
422
- $effect(() => { // sync ranges from axis.range overrides / auto ranges
446
+ $effect(() => {
447
+ // sync ranges from axis.range overrides / auto ranges
423
448
  // resolve_axis_ranges returns null for transient non-finite bounds (skip: writing
424
449
  // NaN breaks scales and, since NaN !== NaN, loops the effect)
425
- const next = resolve_axis_ranges({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis }, auto_ranges)
450
+ const next = resolve_axis_ranges(
451
+ { x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis },
452
+ auto_ranges,
453
+ )
426
454
  if (!next) return
427
455
  // untrack the read of `ranges` so the assignment can't re-trigger this effect
428
456
  // (reading + writing the same state otherwise causes effect_update_depth_exceeded).
@@ -432,34 +460,42 @@
432
460
  }
433
461
  })
434
462
 
435
- const default_padding = { t: 20, b: 60, l: 60, r: 20 }
436
- let base_pad = $derived(filter_padding(padding, default_padding))
437
-
438
- $effect(() => { // dynamic padding from tick label widths
439
- const new_pad = width && height && ticks.y.length > 0
440
- ? calc_auto_padding({
441
- padding,
442
- default_padding,
443
- x2_axis: { ...x2_axis, tick_values: ticks.x2 },
444
- y_axis: { ...y_axis, tick_values: ticks.y },
445
- y2_axis: { ...y2_axis, tick_values: ticks.y2 },
446
- })
447
- : filter_padding(padding, default_padding)
448
- if (
449
- width && height && orientation === `vertical` && has_secondary && ticks.y2.length > 0
450
- ) {
463
+ let base_pad = $derived(filter_padding(padding, DEFAULT_PLOT_PADDING))
464
+
465
+ $effect(() => {
466
+ // dynamic padding from tick label widths
467
+ const new_pad =
468
+ width && height && ticks.y.length > 0
469
+ ? calc_auto_padding({
470
+ padding,
471
+ default_padding: DEFAULT_PLOT_PADDING,
472
+ x2_axis: { ...x2_axis, tick_values: ticks.x2 },
473
+ y_axis: { ...y_axis, tick_values: ticks.y },
474
+ y2_axis: { ...y2_axis, tick_values: ticks.y2 },
475
+ })
476
+ : filter_padding(padding, DEFAULT_PLOT_PADDING)
477
+ if (width && height && show_y2 && ticks.y2.length > 0) {
451
478
  const inside = y2_axis.tick?.label?.inside ?? false
452
479
  const tick_shift = inside ? 0 : (y2_axis.tick?.label?.shift?.x ?? 0) + 8
453
480
  const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max
454
481
  const label_space = y2_axis.label ? 20 : 0
455
482
  new_pad.r = Math.max(new_pad.r, tick_shift + tick_width_contribution + 30 + label_space)
456
483
  }
457
- if (base_pad.t !== new_pad.t || base_pad.b !== new_pad.b ||
458
- base_pad.l !== new_pad.l || base_pad.r !== new_pad.r) base_pad = new_pad
484
+ if (
485
+ base_pad.t !== new_pad.t ||
486
+ base_pad.b !== new_pad.b ||
487
+ base_pad.l !== new_pad.l ||
488
+ base_pad.r !== new_pad.r
489
+ )
490
+ base_pad = new_pad
459
491
  })
460
492
 
461
493
  let legend_element = $state<HTMLDivElement | undefined>()
462
- const legend_footprint = $derived(measured_footprint(legend_element, { width: 120, height: 60 }))
494
+ let legend_size_revision = $state(0)
495
+ const legend_footprint = $derived.by(() => {
496
+ void legend_size_revision
497
+ return measured_footprint(legend_element, { width: 120, height: 60 })
498
+ })
463
499
  const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
464
500
 
465
501
  // Obstacle field in normalized [0,1] coords: each box modeled as a whisker-spanning segment
@@ -476,8 +512,12 @@
476
512
  const secondary = is_secondary(box_item.series)
477
513
  const cat_rng = vertical ? ranges.current.x : ranges.current.y
478
514
  const val_rng = vertical
479
- ? (secondary ? ranges.current.y2 : ranges.current.y)
480
- : (secondary ? ranges.current.x2 : ranges.current.x)
515
+ ? secondary
516
+ ? ranges.current.y2
517
+ : ranges.current.y
518
+ : secondary
519
+ ? ranges.current.x2
520
+ : ranges.current.x
481
521
  const cat_span = cat_rng[1] - cat_rng[0]
482
522
  const val_span = val_rng[1] - val_rng[0]
483
523
  if (cat_span === 0 || val_span === 0) continue
@@ -493,43 +533,73 @@
493
533
  })
494
534
 
495
535
  const should_show_legend = $derived(show_legend ?? false)
496
- const decor = $derived.by(() =>
536
+ const decor = $derived(
497
537
  place_decorations({
498
538
  base_pad,
499
539
  width,
500
540
  height,
501
541
  obstacles_norm,
502
- legend: legend != null && should_show_legend && legend_element != null &&
503
- !legend_has_explicit_pos
504
- ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
505
- : null,
506
- })
542
+ legend:
543
+ legend != null &&
544
+ should_show_legend &&
545
+ legend_element != null &&
546
+ !legend_has_explicit_pos
547
+ ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
548
+ : null,
549
+ }),
550
+ )
551
+ // Marginals are opt-in (default prop `false`) and bind to the VALUE axis, pooling each box's
552
+ // raw samples. The default side follows orientation (value axis = y when vertical, x when
553
+ // horizontal) so the `marginals` boolean / type-string shorthand land on a meaningful side.
554
+ // Each box is tagged with its value axis so a primary-axis marginal ignores secondary boxes.
555
+ const marginal_vertical = $derived(orientation === `vertical`)
556
+ const resolved_marginals = $derived(
557
+ normalize_marginals(marginals, marginal_vertical ? { right: true } : { top: true }),
507
558
  )
508
- const pad = $derived(decor.pad)
509
- const legend_auto_outside = $derived(decor.legend_outside)
510
- const legend_outside_x = $derived(decor.legend_pos.x)
511
- const legend_outside_y = $derived(decor.legend_pos.y)
559
+ const pad = $derived(add_sides(decor.pad, reserve_marginal_pad(resolved_marginals)))
560
+ const marginal_series = $derived<MarginalSeriesInput[]>(
561
+ visible_boxes.map((box_item) => {
562
+ const secondary = is_secondary(box_item.series)
563
+ return {
564
+ x: marginal_vertical ? undefined : (box_item.series.y ?? []),
565
+ y: marginal_vertical ? (box_item.series.y ?? []) : undefined,
566
+ color: box_color(box_item.idx),
567
+ label: box_item.series.label,
568
+ visible: true,
569
+ x_axis: marginal_vertical ? `x1` : secondary ? `x2` : `x1`,
570
+ y_axis: marginal_vertical ? (secondary ? `y2` : `y1`) : `y1`,
571
+ }
572
+ }),
573
+ )
574
+ const marginal_has_axis = $derived(marginal_axis_presence(show_x2, show_y2))
512
575
  const chart_width = $derived(Math.max(1, width - pad.l - pad.r))
513
576
  const chart_height = $derived(Math.max(1, height - pad.t - pad.b))
514
577
 
515
- let scales = $derived({
516
- x: create_scale(x_axis.scale_type ?? `linear`, ranges.current.x, [pad.l, width - pad.r]),
517
- x2: create_scale(x2_axis.scale_type ?? `linear`, ranges.current.x2, [pad.l, width - pad.r]),
518
- y: create_scale(y_axis.scale_type ?? `linear`, ranges.current.y, [height - pad.b, pad.t]),
519
- y2: create_scale(y2_axis.scale_type ?? `linear`, ranges.current.y2, [height - pad.b, pad.t]),
520
- })
578
+ let scales = $derived(
579
+ create_axis_scales(
580
+ { x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis },
581
+ ranges.current,
582
+ pad,
583
+ width,
584
+ height,
585
+ ),
586
+ )
521
587
 
522
588
  // Value scale for a box (vertical -> y/y2, horizontal -> x/x2), made log-safe: on a
523
589
  // log value axis, stats at values <= 0 (whisker_low is often exactly 0; negative
524
590
  // outliers) have no finite pixel. Clamp to LOG_EPS so whiskers/boxes/labels draw
525
591
  // toward the plot edge (the clip group crops the overshoot) instead of NaN coords.
526
- const box_val_scale = (srs: BoxPlotSeries<Metadata>): (val: number) => number => {
592
+ const box_val_scale = (srs: BoxPlotSeries<Metadata>): ((val: number) => number) => {
527
593
  const vertical = orientation === `vertical`
528
594
  const secondary = is_secondary(srs)
529
595
  const scale = vertical
530
- ? (secondary ? scales.y2 : scales.y)
531
- : (secondary ? scales.x2 : scales.x)
532
- const axis = vertical ? (secondary ? y2_axis : y_axis) : (secondary ? x2_axis : x_axis)
596
+ ? secondary
597
+ ? scales.y2
598
+ : scales.y
599
+ : secondary
600
+ ? scales.x2
601
+ : scales.x
602
+ const axis = vertical ? (secondary ? y2_axis : y_axis) : secondary ? x2_axis : x_axis
533
603
  return axis.scale_type === `log` ? (val) => scale(Math.max(val, LOG_EPS)) : scale
534
604
  }
535
605
 
@@ -543,48 +613,50 @@
543
613
  return Object.fromEntries(slot_list.map((cat, idx) => [idx, cat]))
544
614
  })
545
615
 
546
- let ticks = $derived({
547
- x: width && height
548
- ? (cat_axis === `x` ? slot_indices : generate_ticks(
549
- ranges.current.x,
550
- x_axis.scale_type ?? `linear`,
551
- x_axis.ticks,
552
- scales.x,
553
- { default_count: 8 },
554
- ))
555
- : [],
556
- y: width && height
557
- ? (cat_axis === `y` ? slot_indices : generate_ticks(
558
- ranges.current.y,
559
- y_axis.scale_type ?? `linear`,
560
- y_axis.ticks,
561
- scales.y,
562
- { default_count: 6 },
563
- ))
564
- : [],
565
- y2: width && height && has_secondary && orientation === `vertical`
566
- ? generate_ticks(ranges.current.y2, y2_axis.scale_type ?? `linear`, y2_axis.ticks, scales.y2, {
567
- default_count: 6,
568
- })
569
- : [],
570
- x2: width && height && has_secondary && orientation === `horizontal`
571
- ? generate_ticks(ranges.current.x2, x2_axis.scale_type ?? `linear`, x2_axis.ticks, scales.x2, {
572
- default_count: 8,
573
- })
574
- : [],
616
+ let ticks = $derived.by(() => {
617
+ const axis_ticks = (
618
+ axis: typeof x_axis,
619
+ range: Vec2,
620
+ scale: typeof scales.x,
621
+ default_count: number,
622
+ show = true,
623
+ ) =>
624
+ width && height && show
625
+ ? generate_ticks(range, axis.scale_type ?? `linear`, axis.ticks, scale, {
626
+ default_count,
627
+ })
628
+ : []
629
+ // categorical axes show one tick per slot instead of generated numeric ticks
630
+ return {
631
+ x:
632
+ cat_axis === `x` && width && height
633
+ ? slot_indices
634
+ : axis_ticks(x_axis, ranges.current.x, scales.x, 8),
635
+ y:
636
+ cat_axis === `y` && width && height
637
+ ? slot_indices
638
+ : axis_ticks(y_axis, ranges.current.y, scales.y, 6),
639
+ y2: axis_ticks(y2_axis, ranges.current.y2, scales.y2, 6, show_y2),
640
+ x2: axis_ticks(x2_axis, ranges.current.x2, scales.x2, 8, show_x2),
641
+ }
575
642
  })
576
643
 
577
644
  let tick_label_widths = $derived({
578
645
  y_max: measure_max_tick_width(ticks.y, y_axis.format ?? ``),
579
646
  y2_max: measure_max_tick_width(ticks.y2, y2_axis.format ?? ``),
580
- x2_max: measure_max_tick_width(ticks.x2, x2_axis.format ?? ``),
581
647
  })
582
648
 
583
649
  // Shared pan/zoom/touch/drag-rect interaction controller
584
650
  const pan_zoom = create_pan_zoom({
585
651
  ranges: () => ranges.current,
586
- scale_type: (axis) => ({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
587
- plot_dims: () => ({ width: chart_width, height: chart_height }),
652
+ scale_type: (axis) =>
653
+ ({ x: x_axis, x2: x2_axis, y: y_axis, y2: y2_axis })[axis].scale_type,
654
+ plot_bounds: () => ({
655
+ x: pad.l,
656
+ y: pad.t,
657
+ width: chart_width,
658
+ height: chart_height,
659
+ }),
588
660
  pan: () => pan,
589
661
  set_range: (axis, range) => (ranges.current[axis] = range),
590
662
  svg: () => svg_element,
@@ -595,15 +667,11 @@
595
667
  // the secondary value axis is x2 only in horizontal mode, y2 only in vertical
596
668
  // (is_secondary keys off orientation); writing the off-orientation axis would
597
669
  // store a phantom range from its [0, 1] sentinel scale into the bound prop
598
- const next_x2 = has_secondary && orientation === `horizontal`
599
- ? invert_rect_range(scales.x2, start.x, current.x)
600
- : null
670
+ const next_x2 = show_x2 ? invert_rect_range(scales.x2, start.x, current.x) : null
601
671
  if (next_x2) x2_axis_prop = { ...x2_axis_prop, range: next_x2 }
602
672
  const next_y = invert_rect_range(scales.y, start.y, current.y)
603
673
  if (next_y) y_axis = { ...y_axis, range: next_y }
604
- const next_y2 = has_secondary && orientation === `vertical`
605
- ? invert_rect_range(scales.y2, start.y, current.y)
606
- : null
674
+ const next_y2 = show_y2 ? invert_rect_range(scales.y2, start.y, current.y) : null
607
675
  if (next_y2) y2_axis_prop = { ...y2_axis_prop, range: next_y2 }
608
676
  },
609
677
  on_reset: () => {
@@ -632,7 +700,10 @@
632
700
  })),
633
701
  )
634
702
 
635
- const legend_vis = create_legend_visibility(() => series, (next) => (series = next))
703
+ const legend_vis = create_legend_visibility(
704
+ () => series,
705
+ (next) => (series = next),
706
+ )
636
707
 
637
708
  let box_points_for_placement = $derived.by(() => {
638
709
  if (!width || !height || visible_boxes.length === 0) return []
@@ -650,7 +721,7 @@
650
721
 
651
722
  let hovered_legend_series_idx = $state<number | null>(null)
652
723
 
653
- let legend_placement = $derived.by(() => {
724
+ const get_legend_placement = () => {
654
725
  if (!should_show_legend || !width || !height) return null
655
726
  return compute_element_placement({
656
727
  plot_bounds: { x: pad.l, y: pad.t, width: chart_width, height: chart_height },
@@ -660,15 +731,16 @@
660
731
  exclude_rects: [],
661
732
  points: box_points_for_placement,
662
733
  })
663
- })
734
+ }
664
735
 
665
736
  // Tweened legend coordinates with shared placement stability gating
666
737
  const legend_tween = create_placed_tween({
667
- placement: () => legend_placement,
738
+ placement: get_legend_placement,
668
739
  dims: () => ({ width, height }),
669
740
  responsive: () => legend?.responsive ?? false,
670
741
  element: () => legend_element,
671
742
  tween: () => legend?.tween,
743
+ on_element_resize: () => (legend_size_revision += 1),
672
744
  })
673
745
 
674
746
  // === Tooltip / hover ===
@@ -720,21 +792,24 @@
720
792
  on_box_hover?.({ ...hover_info, event })
721
793
  }
722
794
 
795
+ const clear_hover = () => {
796
+ hover_info = null
797
+ change(null)
798
+ on_box_hover?.(null)
799
+ }
800
+
723
801
  // Set theme-aware background when entering fullscreen
724
802
  $effect(() => set_fullscreen_bg(wrapper, fullscreen, `--boxplot-fullscreen-bg`))
725
803
 
726
804
  // Value label helper
727
805
  const value_label_for = (stats: Box[`stats`]): string =>
728
- format_value(value_label_stat === `mean` ? stats.mean : stats.median, value_label_format)
806
+ format_value_or_num(
807
+ value_label_stat === `mean` ? stats.mean : stats.median,
808
+ value_label_format,
809
+ )
729
810
  </script>
730
811
 
731
- {#snippet seg(
732
- p1: Vec2,
733
- p2: Vec2,
734
- stroke: string,
735
- sw: number,
736
- dash?: string,
737
- )}
812
+ {#snippet seg(p1: Vec2, p2: Vec2, stroke: string, sw: number, dash?: string)}
738
813
  <line
739
814
  x1={p1[0]}
740
815
  y1={p1[1]}
@@ -790,7 +865,7 @@
790
865
  bind:clientWidth={width}
791
866
  bind:clientHeight={height}
792
867
  {...rest}
793
- class="box-plot {rest.class ?? ``}"
868
+ class={[`box-plot`, rest.class]}
794
869
  class:fullscreen
795
870
  >
796
871
  {#if width && height}
@@ -806,7 +881,7 @@
806
881
  bind:this={svg_element}
807
882
  role="application"
808
883
  aria-label={rest[`aria-label`] ??
809
- ([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Box plot`)}
884
+ ([x_axis.label, y_axis.label].filter(Boolean).join(` vs `) || `Box plot`)}
810
885
  tabindex="0"
811
886
  onfocusin={() => pan_zoom.set_focused(true)}
812
887
  onfocusout={() => pan_zoom.set_focused(false)}
@@ -815,9 +890,7 @@
815
890
  onkeydown={pan_zoom.on_key_down}
816
891
  onmouseleave={() => {
817
892
  hovered = false
818
- hover_info = null
819
- change(null)
820
- on_box_hover?.(null)
893
+ clear_hover()
821
894
  }}
822
895
  onwheel={pan_zoom.on_wheel}
823
896
  ontouchstart={pan_zoom.on_touch_start}
@@ -847,65 +920,67 @@
847
920
 
848
921
  <PlotAxis
849
922
  side="x"
850
- ticks={ticks.x as number[]}
923
+ ticks={ticks.x}
851
924
  place={scales.x}
852
925
  axis={x_axis}
853
- domain={ranges.current.x as Vec2}
926
+ domain={ranges.current.x}
854
927
  {pad}
855
928
  {width}
856
929
  {height}
857
930
  show_grid={display.x_grid}
858
931
  tick_label={(tick) =>
859
- get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
932
+ get_tick_label(tick, cat_axis === `x` ? effective_cat_ticks : x_axis.ticks)}
860
933
  tick_color={cat_axis === `x` ? (tick) => slot_colors.get(tick) : undefined}
861
934
  label_x={pad.l + chart_width / 2 + (x_axis.label_shift?.x ?? 0)}
862
- label_y={height - pad.b / 3 + (x_axis.label_shift?.y ?? 0)}
935
+ label_y={height - pad.b + AXIS_TITLE_OFFSET + (x_axis.label_shift?.y ?? 0)}
863
936
  />
864
937
 
865
- {#if has_secondary && orientation === `horizontal`}
938
+ {#if show_x2}
866
939
  <PlotAxis
867
940
  side="x2"
868
- ticks={ticks.x2 as number[]}
941
+ ticks={ticks.x2}
869
942
  place={scales.x2}
870
943
  axis={x2_axis}
871
- domain={ranges.current.x2 as Vec2}
944
+ domain={ranges.current.x2}
872
945
  {pad}
873
946
  {width}
874
947
  {height}
875
948
  show_grid={display.x2_grid}
876
949
  tick_label={(tick) => get_tick_label(tick, x2_axis.ticks)}
877
950
  label_x={pad.l + chart_width / 2 + (x2_axis.label_shift?.x ?? 0)}
878
- label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? 40))}
951
+ label_y={Math.max(12, pad.t - (x2_axis.label_shift?.y ?? AXIS_TITLE_OFFSET))}
879
952
  />
880
953
  {/if}
881
954
 
882
955
  <PlotAxis
883
956
  side="y"
884
- ticks={ticks.y as number[]}
957
+ ticks={ticks.y}
885
958
  place={scales.y}
886
959
  axis={y_axis}
887
- domain={ranges.current.y as Vec2}
960
+ domain={ranges.current.y}
888
961
  {pad}
889
962
  {width}
890
963
  {height}
891
964
  show_grid={display.y_grid}
892
965
  tick_label={(tick) =>
893
- get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
966
+ get_tick_label(tick, cat_axis === `y` ? effective_cat_ticks : y_axis.ticks)}
894
967
  tick_color={cat_axis === `y` ? (tick) => slot_colors.get(tick) : undefined}
895
968
  label_x={Math.max(
896
969
  12,
897
- pad.l - (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) - LABEL_GAP_DEFAULT,
970
+ pad.l -
971
+ (y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
972
+ LABEL_GAP_DEFAULT,
898
973
  ) + (y_axis.label_shift?.x ?? 0)}
899
974
  label_y={pad.t + chart_height / 2 + (y_axis.label_shift?.y ?? 0)}
900
975
  />
901
976
 
902
- {#if has_secondary && orientation === `vertical`}
977
+ {#if show_y2}
903
978
  <PlotAxis
904
979
  side="y2"
905
- ticks={ticks.y2 as number[]}
980
+ ticks={ticks.y2}
906
981
  place={scales.y2}
907
982
  axis={y2_axis}
908
- domain={ranges.current.y2 as Vec2}
983
+ domain={ranges.current.y2}
909
984
  {pad}
910
985
  {width}
911
986
  {height}
@@ -941,8 +1016,8 @@
941
1016
  x2_scale_type={x2_axis.scale_type}
942
1017
  y_scale_type={y_axis.scale_type}
943
1018
  y2_scale_type={y2_axis.scale_type}
944
- has_x2={has_secondary && orientation === `horizontal`}
945
- has_y2={has_secondary && orientation === `vertical`}
1019
+ has_x2={show_x2}
1020
+ has_y2={show_y2}
946
1021
  {width}
947
1022
  {height}
948
1023
  {pad}
@@ -963,12 +1038,13 @@
963
1038
  {@const draw_box = draws_box(box_item.series)}
964
1039
  {@const kde = violin_kdes.get(box_item.idx)}
965
1040
  {@const eff_side = box_item.series.side ?? side}
966
- {@const bw = box_item.series.box_width ??
967
- (kde ? DEFAULTS.box.violin_box_width : DEFAULTS.box.box_width)}
1041
+ {@const bw =
1042
+ box_item.series.box_width ??
1043
+ (kde ? DEFAULTS.box.violin_box_width : DEFAULTS.box.box_width)}
968
1044
  {@const c_lo = cat_scale(box_item.slot - bw / 2)}
969
1045
  {@const c_hi = cat_scale(box_item.slot + bw / 2)}
970
1046
  {@const c_center = cat_scale(box_item.slot)}
971
- {@const cap = Math.abs(c_hi - c_lo) * (whisker_state.cap_fraction ?? 0.5) / 2}
1047
+ {@const cap = (Math.abs(c_hi - c_lo) * (whisker_state.cap_fraction ?? 0.5)) / 2}
972
1048
  {@const cap_lo = c_center - cap}
973
1049
  {@const cap_hi = c_center + cap}
974
1050
  {@const v_q1 = val_scale(stats.q1)}
@@ -978,7 +1054,7 @@
978
1054
  {@const v_wh = val_scale(stats.whisker_high)}
979
1055
  {@const v_mean = val_scale(stats.mean)}
980
1056
  {@const pt = (cross: number, val: number): Vec2 =>
981
- vertical ? [cross, val] : [val, cross]}
1057
+ vertical ? [cross, val] : [val, cross]}
982
1058
  {@const [q1x, q1y] = pt(c_lo, v_q1)}
983
1059
  {@const [q3x, q3y] = pt(c_hi, v_q3)}
984
1060
  {@const [wlx, wly] = pt(c_lo, v_wl)}
@@ -992,12 +1068,12 @@
992
1068
  {@const hit_w = Math.abs(whx - wlx)}
993
1069
  {@const hit_h = Math.abs(why - wly)}
994
1070
  {@const [label_x, label_y] = vertical
995
- ? [c_center, Math.min(v_wh, v_wl) - 6]
996
- : [Math.max(v_wh, v_wl) + 6, c_center]}
1071
+ ? [c_center, Math.min(v_wh, v_wl) - 6]
1072
+ : [Math.max(v_wh, v_wl) + 6, c_center]}
997
1073
  {@const violin_half = Math.abs(
998
- cat_scale(box_item.slot + (box_item.series.violin_width ?? violin_width) / 2) -
999
- c_center,
1000
- )}
1074
+ cat_scale(box_item.slot + (box_item.series.violin_width ?? violin_width) / 2) -
1075
+ c_center,
1076
+ )}
1001
1077
  {@const max_density = kde ? (violin_max_density.get(box_item.idx) ?? 0) : 0}
1002
1078
  <!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
1003
1079
  <g
@@ -1008,16 +1084,13 @@
1008
1084
  aria-label={`box ${box_item.idx + 1}: ${box_item.series.label ?? ``}`}
1009
1085
  style:cursor={on_box_click ? `pointer` : undefined}
1010
1086
  opacity={hovered_legend_series_idx !== null &&
1011
- hovered_legend_series_idx !== box_item.idx
1087
+ hovered_legend_series_idx !== box_item.idx
1012
1088
  ? 0.25
1013
1089
  : 1}
1014
1090
  onmousemove={handle_box_hover(box_item, color)}
1015
- onmouseleave={() => {
1016
- hover_info = null
1017
- change(null)
1018
- on_box_hover?.(null)
1019
- }}
1020
- onclick={(evt) => on_box_click?.({ ...get_box_data(box_item, color), event: evt })}
1091
+ onmouseleave={clear_hover}
1092
+ onclick={(evt) =>
1093
+ on_box_click?.({ ...get_box_data(box_item, color), event: evt })}
1021
1094
  onkeydown={(evt) => {
1022
1095
  if (evt.key === `Enter` || evt.key === ` `) {
1023
1096
  evt.preventDefault()
@@ -1064,7 +1137,12 @@
1064
1137
  stroke-width={box_state.stroke_width}
1065
1138
  />
1066
1139
  <!-- median (solid) and mean (dashed) -->
1067
- {@render seg(pt(c_lo, v_med), pt(c_hi, v_med), median_state.color, median_state.width)}
1140
+ {@render seg(
1141
+ pt(c_lo, v_med),
1142
+ pt(c_hi, v_med),
1143
+ median_state.color,
1144
+ median_state.width,
1145
+ )}
1068
1146
  {#if show_mean}
1069
1147
  {@render seg(
1070
1148
  pt(c_lo, v_mean),
@@ -1120,16 +1198,33 @@
1120
1198
 
1121
1199
  {@render ref_lines_layer(ref_lines_by_z.below_points)}
1122
1200
  {@render ref_lines_layer(ref_lines_by_z.above_all)}
1201
+
1202
+ <!-- Marginal distribution strips -->
1203
+ <PlotMarginals
1204
+ marginals={resolved_marginals}
1205
+ series={marginal_series}
1206
+ {width}
1207
+ {height}
1208
+ {pad}
1209
+ has_axis={marginal_has_axis}
1210
+ axes={{
1211
+ x1: marginal_axis(scales.x, ranges.current.x, x_axis),
1212
+ x2: marginal_axis(scales.x2, ranges.current.x2, x2_axis),
1213
+ y1: marginal_axis(scales.y, ranges.current.y, y_axis),
1214
+ y2: marginal_axis(scales.y2, ranges.current.y2, y2_axis),
1215
+ }}
1216
+ id={clip_path_id}
1217
+ />
1123
1218
  </svg>
1124
1219
 
1125
1220
  {#if legend && should_show_legend}
1126
1221
  {@const legend_pos = placed_coords(
1127
- legend_auto_outside,
1128
- { x: legend_outside_x, y: legend_outside_y },
1129
- legend_placement,
1130
- legend_tween.coords.current,
1131
- { x: pad.l + 10, y: pad.t + 10 },
1132
- )}
1222
+ decor.legend_outside,
1223
+ decor.legend_pos,
1224
+ legend_tween.placed(),
1225
+ legend_tween.coords.current,
1226
+ { x: pad.l + 10, y: pad.t + 10 },
1227
+ )}
1133
1228
  <PlotLegend
1134
1229
  bind:root_element={legend_element}
1135
1230
  {...legend}
@@ -1139,9 +1234,8 @@
1139
1234
  on_double_click={legend?.on_double_click ?? legend_vis.on_double_click}
1140
1235
  on_hover_change={legend_tween.set_locked}
1141
1236
  on_item_hover={(item) =>
1142
- (hovered_legend_series_idx = item != null && item.series_idx >= 0
1143
- ? item.series_idx
1144
- : null)}
1237
+ (hovered_legend_series_idx =
1238
+ item != null && item.series_idx >= 0 ? item.series_idx : null)}
1145
1239
  active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
1146
1240
  style={`position: absolute; left: ${legend_pos.x}px; top: ${legend_pos.y}px; pointer-events: auto; ${
1147
1241
  legend?.style || ``
@@ -1161,7 +1255,7 @@
1161
1255
  {#if tooltip}
1162
1256
  {@render tooltip({ ...hover_info, fullscreen })}
1163
1257
  {:else}
1164
- {@const fmt = (orientation === `vertical` ? y_axis.format : x_axis.format) || `.3~s`}
1258
+ {@const fmt = orientation === `vertical` ? y_axis.format : x_axis.format}
1165
1259
  {@const stat = hover_info.stats}
1166
1260
  {@const rows = [
1167
1261
  [`max`, stat.whisker_high],
@@ -1175,7 +1269,7 @@
1175
1269
  <div><strong>{hover_info.category_label}</strong></div>
1176
1270
  {/if}
1177
1271
  {#each rows as [label, value] (label)}
1178
- <div>{label}: {format_value(value, fmt)}</div>
1272
+ <div>{label}: {format_value_or_num(value, fmt)}</div>
1179
1273
  {/each}
1180
1274
  {#if show_outliers && stat.outliers.length > 0}
1181
1275
  <div>outliers: {stat.outliers.length}</div>
@@ -1206,12 +1300,12 @@
1206
1300
  bind:y_axis
1207
1301
  bind:y2_axis={y2_axis_prop}
1208
1302
  bind:display
1209
- auto_x_range={auto_ranges.x as Vec2}
1210
- auto_x2_range={auto_ranges.x2 as Vec2}
1211
- auto_y_range={auto_ranges.y as Vec2}
1212
- auto_y2_range={auto_ranges.y2 as Vec2}
1213
- has_x2_points={has_secondary && orientation === `horizontal`}
1214
- has_y2_points={has_secondary && orientation === `vertical`}
1303
+ auto_x_range={auto_ranges.x}
1304
+ auto_x2_range={auto_ranges.x2}
1305
+ auto_y_range={auto_ranges.y}
1306
+ auto_y2_range={auto_ranges.y2}
1307
+ has_x2_points={show_x2}
1308
+ has_y2_points={show_y2}
1215
1309
  children={controls_extra}
1216
1310
  />
1217
1311
  {/if}
@@ -1268,7 +1362,9 @@
1268
1362
  .box-plot :global(.pane-toggle),
1269
1363
  .box-plot .header-controls {
1270
1364
  opacity: 0;
1271
- transition: opacity 0.2s, background-color 0.2s;
1365
+ transition:
1366
+ opacity 0.2s,
1367
+ background-color 0.2s;
1272
1368
  }
1273
1369
  .box-plot:hover :global(.pane-toggle),
1274
1370
  .box-plot:hover .header-controls,