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
@@ -0,0 +1,907 @@
1
+ <script
2
+ lang="ts"
3
+ generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
4
+ >
5
+ import type { D3InterpolateName } from '../../colors'
6
+ import { format_value } from '../../labels'
7
+ import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
8
+ import type { Vec2 } from '../../math'
9
+ import type {
10
+ BasePlotProps,
11
+ LegendConfig,
12
+ LegendItem,
13
+ SunburstLabelText,
14
+ SunburstSort,
15
+ SunburstValueMode,
16
+ } from '..'
17
+ import { ColorBar, PlotLegend, PlotTooltip, TreemapControls } from '..'
18
+ import { closest_data_idx } from '../core/interactions'
19
+ import { compute_element_placement, filter_padding } from '../core/layout'
20
+ import {
21
+ ancestor_chain,
22
+ compute_metric_colors,
23
+ compute_node_dim,
24
+ compute_node_infos,
25
+ handle_hierarchy_escape,
26
+ hierarchy_legend_items,
27
+ is_activation_key,
28
+ node_handler_props,
29
+ pointer_pos,
30
+ prune_muted_ids,
31
+ safe_hierarchy_layout,
32
+ selection_within,
33
+ svg_label_font,
34
+ toggle_muted,
35
+ } from '../core/utils/hierarchy-chart'
36
+ import {
37
+ export_hierarchy_chart,
38
+ make_cached_contrast,
39
+ make_cached_text_width,
40
+ } from '../core/utils/hierarchy-labels'
41
+ import { SCALE_DEFAULTS } from '../core/types'
42
+ import type { Rect, Sides } from '../core/layout'
43
+ import { arrow_nav_target } from '../sunburst/render'
44
+ import type { PositionedArc } from '../sunburst/sunburst'
45
+ import type { TreemapNode, TreemapNodeHandlerProps } from './treemap'
46
+ import { lerp_rects, tile_rects } from './treemap'
47
+ import { DEFAULTS } from '../../settings'
48
+ import type { ComponentProps, Snippet } from 'svelte'
49
+ import { tick, untrack } from 'svelte'
50
+ import { cubicInOut } from 'svelte/easing'
51
+ import type { HTMLAttributes } from 'svelte/elements'
52
+ import { Tween, type TweenOptions } from 'svelte/motion'
53
+ import { SvelteSet } from 'svelte/reactivity'
54
+
55
+ const DEFAULT_PADDING: Required<Sides> = { t: 10, b: 10, l: 10, r: 10 }
56
+
57
+ let {
58
+ data = $bindable([]),
59
+ value_mode = $bindable(DEFAULTS.treemap.value_mode),
60
+ sort = `none`,
61
+ level_lighten = 0,
62
+ min_fraction = $bindable(DEFAULTS.treemap.min_fraction),
63
+ other_label = `Other`,
64
+ max_depth = $bindable(DEFAULTS.treemap.max_depth),
65
+ padding_inner = $bindable(DEFAULTS.treemap.padding_inner),
66
+ padding_top = $bindable(DEFAULTS.treemap.padding_top),
67
+ padding_outer = $bindable(DEFAULTS.treemap.padding_outer),
68
+ show_labels = $bindable(DEFAULTS.treemap.show_labels),
69
+ label_text = $bindable(DEFAULTS.treemap.label_text),
70
+ zoom_on_click = $bindable(DEFAULTS.treemap.zoom_on_click),
71
+ zoom_root_id = $bindable(null),
72
+ show_breadcrumbs = $bindable(DEFAULTS.treemap.show_breadcrumbs),
73
+ color_values,
74
+ color_scale = SCALE_DEFAULTS.scheme,
75
+ color_range,
76
+ colorbar = {},
77
+ export_buttons = true,
78
+ export_filename = `treemap`,
79
+ tween,
80
+ value_format = `,`,
81
+ padding = DEFAULT_PADDING,
82
+ legend = {},
83
+ show_legend = false,
84
+ tooltip,
85
+ cell_content,
86
+ hovered = $bindable(false),
87
+ change = () => {},
88
+ on_node_click,
89
+ on_node_hover,
90
+ on_zoom,
91
+ show_controls = $bindable(true),
92
+ controls_open = $bindable(false),
93
+ controls_toggle_props,
94
+ controls_pane_props,
95
+ fullscreen = $bindable(false),
96
+ fullscreen_toggle = true,
97
+ children,
98
+ header_controls,
99
+ controls_extra,
100
+ ...rest
101
+ }: HTMLAttributes<HTMLDivElement> &
102
+ Omit<BasePlotProps, `change`> & {
103
+ data?: TreemapNode<Metadata> | TreemapNode<Metadata>[]
104
+ value_mode?: SunburstValueMode
105
+ sort?: SunburstSort
106
+ level_lighten?: number
107
+ // Aggregate sibling cells below this fraction of the total into one 'Other'
108
+ // cell per parent (only when >= 2 qualify); 0 disables
109
+ min_fraction?: number
110
+ other_label?: string
111
+ max_depth?: number // levels shown below the current zoom root (0 = all)
112
+ padding_inner?: number // px gap between sibling cells
113
+ padding_top?: number // px header strip on branch cells (0 = no headers)
114
+ padding_outer?: number // px inset of children within their parent (plotly marker.pad)
115
+ show_labels?: boolean
116
+ label_text?: SunburstLabelText // what labels display (plotly textinfo equivalent)
117
+ zoom_on_click?: boolean
118
+ zoom_root_id?: string | number | null // id of the cell the view is rooted on
119
+ show_breadcrumbs?: boolean // clickable ancestor trail when zoomed
120
+ // Color cells by a numeric metric (continuous colormap) instead of categorical
121
+ // inheritance; return null to keep a cell's categorical color
122
+ color_values?: (rect: PositionedArc<Metadata>) => number | null
123
+ color_scale?: D3InterpolateName
124
+ color_range?: Vec2 // defaults to the metric's [min, max]
125
+ colorbar?: ComponentProps<typeof ColorBar> | null // null hides it
126
+ export_buttons?: boolean // SVG/PNG download buttons in the controls pane
127
+ export_filename?: string
128
+ // Zoom transition timing (resizes/data swaps snap instantly, plotly-style).
129
+ // interpolate is not overridable: the component's rect interpolator also
130
+ // handles rect-array length changes on data swaps (default would throw)
131
+ tween?: Omit<TweenOptions<Rect[]>, `interpolate`>
132
+ value_format?: string
133
+ padding?: Sides
134
+ legend?: LegendConfig | null
135
+ show_legend?: boolean
136
+ tooltip?: Snippet<[TreemapNodeHandlerProps<Metadata>]>
137
+ // Fully replace the default cell rect + labels. NOTE: this also replaces the
138
+ // built-in hover/focus/click + tooltip wiring, so re-implement any
139
+ // interactivity you need inside the snippet.
140
+ cell_content?: Snippet<[{ arc: PositionedArc<Metadata>; rect: Rect }]>
141
+ change?: (data: TreemapNodeHandlerProps<Metadata> | null) => void
142
+ on_node_click?: (
143
+ data: TreemapNodeHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
144
+ ) => void
145
+ on_node_hover?: (
146
+ data: (TreemapNodeHandlerProps<Metadata> & { event: MouseEvent | FocusEvent }) | null,
147
+ ) => void
148
+ on_zoom?: (data: { root: TreemapNodeHandlerProps<Metadata> | null }) => void
149
+ header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
150
+ controls_extra?: Snippet<[{ zoom_root_id: string | number | null }]>
151
+ } = $props()
152
+
153
+ let [width, height] = $state([0, 0])
154
+ let wrapper: HTMLDivElement | undefined = $state()
155
+ let svg_element: SVGSVGElement | null = $state(null)
156
+ // Unique per instance so multiple treemaps on one page don't collide on the
157
+ // hatch pattern's SVG id.
158
+ const uid = $props.id()
159
+ const hatch_pattern_id = `treemap-hatch-${uid}`
160
+
161
+ let hovered_idx = $state<number | null>(null)
162
+ let hover_info = $state<TreemapNodeHandlerProps<Metadata> | null>(null)
163
+ let hover_pos = $state<{ x: number; y: number }>({ x: 0, y: 0 })
164
+ // Depth-1 category ids muted via legend toggle (dimmed, not removed - keeps layout stable)
165
+ let muted_ids = new SvelteSet<string | number>()
166
+
167
+ let pad = $derived(filter_padding(padding, DEFAULT_PADDING))
168
+ let inner_width = $derived(Math.max(0, width - pad.l - pad.r))
169
+ let avail_height = $derived(Math.max(0, height - pad.t - pad.b))
170
+ // measured height of the bottom colorbar, reserved from the chart so it never
171
+ // overlaps the cells; reset to 0 when hidden (effect below); capped at half the
172
+ // area so a bad measurement can't collapse the chart
173
+ let colorbar_height = $state(0)
174
+ let colorbar_reserve = $derived(
175
+ colorbar_height > 0 ? Math.min(colorbar_height + 16, avail_height / 2) : 0,
176
+ )
177
+ let inner_height = $derived(avail_height - colorbar_reserve)
178
+
179
+ // Tree semantics (values, colors, ids, pre-order indexing) are shared with
180
+ // Sunburst; only the pixel tiling below is treemap-specific.
181
+ let layout = $derived(
182
+ safe_hierarchy_layout(data, {
183
+ value_mode,
184
+ sort,
185
+ level_lighten,
186
+ min_fraction,
187
+ other_label,
188
+ }),
189
+ )
190
+
191
+ // Resolve the zoom root; stale ids (e.g. after a data swap) fall back to the root
192
+ let zoom_root = $derived(layout.arcs.find((arc) => arc.id === zoom_root_id) ?? layout.root)
193
+ let zoomed = $derived((zoom_root?.depth ?? 0) > 0)
194
+
195
+ // Drop muted ids that no longer exist when data changes (untrack avoids a
196
+ // self-trigger loop from reading/writing muted_ids in the same effect).
197
+ // Hover/focus state is index-based, so a layout swap would otherwise leave a stale
198
+ // tooltip and highlight whatever unrelated node now occupies the old index.
199
+ $effect(() => {
200
+ const { arcs } = layout
201
+ untrack(() => {
202
+ prune_muted_ids(arcs, muted_ids)
203
+ set_cell_hover(null)
204
+ focused_idx = null
205
+ })
206
+ })
207
+
208
+ // Re-tile the zoom root's subtree to the full plot area (plotly behavior:
209
+ // squarified aspect ratios stay correct at every zoom level, unlike projecting
210
+ // one fixed tiling through a window, which would distort cells and scale the
211
+ // fixed-px header strips). Zoom animation interpolates between tilings.
212
+ let target_rects = $derived(
213
+ tile_rects(
214
+ layout.arcs,
215
+ zoom_root?.node_idx ?? 0,
216
+ { width: inner_width, height: inner_height },
217
+ { padding_inner, padding_top, padding_outer },
218
+ ),
219
+ )
220
+ // Seeded at the current target (charts load fully drawn); untrack reads the
221
+ // tween options once at init (not meant to be reactive). The rect interpolator
222
+ // is applied after the user's options: Svelte's default interpolator throws
223
+ // when the rect-array length changes (data swaps), so it must not be overridable.
224
+ const rects_tween = new Tween<Rect[]>(
225
+ untrack(() => target_rects),
226
+ untrack(() => ({
227
+ duration: 400,
228
+ easing: cubicInOut,
229
+ ...tween,
230
+ interpolate: (from: Rect[], to: Rect[]) => (t: number) => lerp_rects(from, to, t),
231
+ })),
232
+ )
233
+ // Animate only zoom transitions; resizes, data swaps and padding tweaks snap
234
+ // instantly (animating a container drag-resize would chase the pointer with a
235
+ // 400ms lerp on every width change, and morphing between unrelated datasets
236
+ // is meaningless)
237
+ let prev_zoom_idx = untrack(() => zoom_root?.node_idx ?? 0)
238
+ let prev_arcs = untrack(() => layout.arcs)
239
+ $effect(() => {
240
+ const [target, zoom_idx, arcs] = [target_rects, zoom_root?.node_idx ?? 0, layout.arcs]
241
+ const zoom_changed = zoom_idx !== prev_zoom_idx && arcs === prev_arcs
242
+ ;[prev_zoom_idx, prev_arcs] = [zoom_idx, arcs]
243
+ rects_tween.set(target, zoom_changed ? undefined : { duration: 0 })
244
+ })
245
+ let rects = $derived(rects_tween.current)
246
+
247
+ // Deepest level rendered below the current zoom root (0 = unlimited)
248
+ let depth_cutoff = $derived(max_depth > 0 ? (zoom_root?.depth ?? 0) + max_depth : Infinity)
249
+ // Shared by rendering and legend placement. The zoom root fills the viewport
250
+ // and is represented by the breadcrumbs, not a cell — except when it's a leaf
251
+ // (e.g. programmatic zoom_root_id onto a compound), which renders as one full-
252
+ // viewport cell instead of a blank chart. Nodes outside the zoomed subtree
253
+ // hold zero rects from tile_rects.
254
+ const cell_visible = (arc: PositionedArc<Metadata>, rect: Rect): boolean =>
255
+ arc.depth > 0 &&
256
+ (arc.node_idx !== zoom_root?.node_idx || arc.is_leaf) &&
257
+ arc.depth <= depth_cutoff &&
258
+ rect.width > 0.5 &&
259
+ rect.height > 0.5
260
+ const idx_visible = (idx: number): boolean => {
261
+ const [arc, rect] = [layout.arcs[idx], rects[idx]]
262
+ return Boolean(arc && rect && cell_visible(arc, rect))
263
+ }
264
+ // Indices (= keys) of cells to render. A plain number array: arcs/rects/
265
+ // cell_info/cell_dim are all index-aligned, so rendering reads them by idx
266
+ // instead of rebuilding N wrapper objects per tween frame.
267
+ let visible_idxs = $derived.by(() => {
268
+ const idxs: number[] = []
269
+ for (let idx = 0; idx < layout.arcs.length; idx++) {
270
+ if (idx_visible(idx)) idxs.push(idx)
271
+ }
272
+ return idxs
273
+ })
274
+
275
+ // Roving tabindex: exactly one cell is in the tab order (the last-focused one,
276
+ // else the first visible cell); arrow keys move focus between cells. Every
277
+ // visible cell is focusable (not just clickable ones) so keyboard users can
278
+ // reach tooltips, and so zooming into a branch of plain leaves doesn't strand
279
+ // focus outside the chart. role="button" stays limited to clickable cells.
280
+ let focused_idx = $state<number | null>(null)
281
+ let roving_idx = $derived(
282
+ focused_idx != null && idx_visible(focused_idx) ? focused_idx : (visible_idxs[0] ?? null),
283
+ )
284
+
285
+ // Continuous metric coloring: when color_values is given, cells are colored by
286
+ // their metric on a d3 colormap (cells returning null keep their categorical color)
287
+ let metric = $derived(
288
+ compute_metric_colors(layout.arcs, color_values, color_scale, color_range),
289
+ )
290
+ const cell_color = (arc: PositionedArc<Metadata>): string =>
291
+ metric?.colors[arc.node_idx] ?? arc.color
292
+ // release the colorbar's reserved chart space when it's not rendered
293
+ $effect(() => {
294
+ if (!metric || colorbar == null) colorbar_height = 0
295
+ })
296
+
297
+ // Hovered cell + its ancestors/descendants stay fully opaque, others dim
298
+ let cell_dim = $derived(compute_node_dim(layout.arcs, muted_ids, hovered_idx))
299
+
300
+ const contrast_for = make_cached_contrast()
301
+
302
+ const make_node_props = (arc: PositionedArc<Metadata>): TreemapNodeHandlerProps<Metadata> =>
303
+ node_handler_props(layout.arcs, arc, cell_color(arc))
304
+
305
+ // Rect center in container (pad-offset) pixel space, for tooltip + legend placement
306
+ const rect_center = (rect: Rect): { x: number; y: number } => ({
307
+ x: pad.l + rect.x + rect.width / 2,
308
+ y: pad.t + rect.y + rect.height / 2,
309
+ })
310
+
311
+ function set_cell_hover(idx: number | null, event?: MouseEvent | FocusEvent) {
312
+ // Same cell as before: only the cursor anchor moves - skip rebuilding the
313
+ // handler payload and re-firing change/on_node_hover on every mousemove.
314
+ // Requires hover_info: legend item hover sets hovered_idx alone (for dimming).
315
+ if (idx != null && idx === hovered_idx && hover_info) {
316
+ hover_pos = pointer_pos(event, svg_element) ?? hover_pos
317
+ return
318
+ }
319
+ if (idx != null) {
320
+ hovered = true
321
+ hovered_idx = idx
322
+ hover_info = make_node_props(layout.arcs[idx])
323
+ hover_pos =
324
+ pointer_pos(event, svg_element) ?? (rects[idx] ? rect_center(rects[idx]) : hover_pos)
325
+ change(hover_info)
326
+ if (event) on_node_hover?.({ ...hover_info, event })
327
+ } else {
328
+ if (hovered_idx == null && hover_info == null) return
329
+ hovered = false
330
+ hovered_idx = null
331
+ hover_info = null
332
+ change(null)
333
+ on_node_hover?.(null)
334
+ }
335
+ }
336
+
337
+ // Node idx carried by the event's nearest [data-treemap-node-idx] element
338
+ const idx_from_event = (event: Event): number | null => {
339
+ const idx = closest_data_idx(event, `data-treemap-node-idx`, svg_element)
340
+ return idx != null && layout.arcs[idx] ? idx : null
341
+ }
342
+
343
+ function handle_cell_hover_event(event: MouseEvent | FocusEvent) {
344
+ const idx = idx_from_event(event)
345
+ // roving tabindex follows keyboard focus
346
+ if (event.type === `focusin` && idx != null) focused_idx = idx
347
+ set_cell_hover(idx, event)
348
+ }
349
+
350
+ // Re-root the view on the given arc (or the data root when null) and notify
351
+ function zoom_to(arc: PositionedArc<Metadata> | null) {
352
+ const root = arc && arc.depth > 0 ? arc : null
353
+ zoom_root_id = root?.id ?? null
354
+ // The clicked cell expands to fill the viewport - drop the stale hover/tooltip
355
+ set_cell_hover(null)
356
+ on_zoom?.({ root: root && make_node_props(root) })
357
+ }
358
+
359
+ // Plotly semantics: clicking any cell (leaves included) zooms into it;
360
+ // clicking the current zoom root (the full-viewport cell a leaf zoom renders)
361
+ // zooms back out one level
362
+ function handle_cell_click(event: MouseEvent | KeyboardEvent) {
363
+ if (event instanceof MouseEvent && selection_within(wrapper)) return
364
+ const idx = idx_from_event(event)
365
+ if (idx == null) return
366
+ const arc = layout.arcs[idx]
367
+ on_node_click?.({ ...make_node_props(arc), event })
368
+ if (!zoom_on_click) return
369
+ if (arc.id === zoom_root?.id) zoom_out()
370
+ else zoom_to(arc)
371
+ }
372
+
373
+ function zoom_out() {
374
+ if (!zoomed) return
375
+ zoom_to(breadcrumb_arcs.at(-2) ?? null)
376
+ }
377
+
378
+ // Double-clicking empty chart background resets the zoom to the root (double-
379
+ // clicking a cell or label is click-to-zoom/text-selection territory, not a reset)
380
+ function handle_dblclick(event: MouseEvent) {
381
+ if (idx_from_event(event) != null || selection_within(wrapper)) return
382
+ if (zoomed) zoom_to(null)
383
+ }
384
+
385
+ const focus_cell = (idx: number | null) => {
386
+ if (idx == null) return
387
+ svg_element
388
+ ?.querySelector<SVGRectElement>(`.cells [data-treemap-node-idx="${idx}"]`)
389
+ ?.focus()
390
+ }
391
+
392
+ // Arrow-key navigation: left/right cycle through visible siblings (wrapping),
393
+ // down enters the first child, up returns to the parent (shared with Sunburst
394
+ // via the pre-order walk in render.ts)
395
+ const nav_target_from_event = (event: KeyboardEvent): number | null => {
396
+ const cur_idx = idx_from_event(event)
397
+ if (cur_idx == null) return null
398
+ return arrow_nav_target(layout.arcs, idx_visible, cur_idx, event.key)
399
+ }
400
+
401
+ function handle_cell_keydown(event: KeyboardEvent) {
402
+ const nav_target = nav_target_from_event(event)
403
+ if (nav_target != null) {
404
+ event.preventDefault()
405
+ focus_cell(nav_target)
406
+ return
407
+ }
408
+ if (!is_activation_key(event)) return
409
+ event.preventDefault()
410
+ const prev_root = zoom_root_id
411
+ handle_cell_click(event)
412
+ // Zooming via keyboard unmounts the focused cell - move focus to the new
413
+ // root's first child (pre-order: node_idx + 1), or the root cell itself for
414
+ // leaf zooms (rendered full-viewport), so keyboard users stay in the chart
415
+ if (zoom_root_id !== prev_root) {
416
+ tick().then(() => {
417
+ if (!zoom_root) return focus_cell(roving_idx)
418
+ focus_cell(zoom_root.is_leaf ? zoom_root.node_idx : zoom_root.node_idx + 1)
419
+ })
420
+ }
421
+ }
422
+
423
+ // Uniform now that any cell zooms (plotly semantics), not just branches
424
+ let cells_clickable = $derived(Boolean(on_node_click) || zoom_on_click)
425
+
426
+ let label_font = $derived(svg_label_font(svg_element))
427
+ const cached_text_width = make_cached_text_width()
428
+
429
+ // Per-cell label text, measured width, fill/label colors and aria string -
430
+ // all zoom-independent, so computed once per layout/option change
431
+ let cell_info = $derived(
432
+ compute_node_infos(layout.arcs, {
433
+ label_text,
434
+ value_format,
435
+ label_font,
436
+ color_for: cell_color,
437
+ text_width: cached_text_width,
438
+ contrast: contrast_for,
439
+ }),
440
+ )
441
+
442
+ const LABEL_MARGIN = 6 // px clearance between label text and cell edges
443
+ // Label text + placement for a cell; null = no variant fits, hide the label.
444
+ // Branch cells label their header strip (top-left); leaves (and branches at the
445
+ // depth cutoff, which render as plain cells) center their label, rotating 90°
446
+ // in thin-but-tall cells like the icicle shape does. Richest label variant
447
+ // that fits wins: extended -> label -> label_short.
448
+ function label_attrs(
449
+ arc: PositionedArc<Metadata>,
450
+ rect: Rect,
451
+ ): { x: number; y: number; text: string; transform?: string; header: boolean } | null {
452
+ const { variants } = cell_info[arc.node_idx]
453
+ const { x, y, width: cell_w, height: cell_h } = rect
454
+ // Branches with visible children only ever label their header strip: a
455
+ // centered label would paint over the descendant cells that cover the rest
456
+ // of the cell, so when the strip is missing/too thin the label is dropped
457
+ const has_visible_children = !arc.is_leaf && arc.depth < depth_cutoff
458
+ if (has_visible_children) {
459
+ if (padding_top < 12) return null
460
+ const fitting = variants.find((entry) => entry.width <= cell_w - 2 * LABEL_MARGIN)
461
+ if (!fitting) return null
462
+ return { x: x + LABEL_MARGIN, y: y + padding_top / 2, text: fitting.text, header: true }
463
+ }
464
+ const fits = (text_w: number, along: number, across: number) =>
465
+ text_w <= along - 2 * LABEL_MARGIN && across >= 12
466
+ const [cx, cy] = [x + cell_w / 2, y + cell_h / 2]
467
+ for (const { text, width: text_w } of variants) {
468
+ if (fits(text_w, cell_w, cell_h)) return { x: cx, y: cy, text, header: false }
469
+ if (fits(text_w, cell_h, cell_w)) {
470
+ return { x: cx, y: cy, text, transform: `rotate(-90, ${cx}, ${cy})`, header: false }
471
+ }
472
+ }
473
+ return null
474
+ }
475
+
476
+ // Legend: one item per depth-1 category, toggling mutes (dims) rather than removes.
477
+ let depth1_arcs = $derived(layout.arcs.filter((arc) => arc.depth === 1))
478
+ let legend_visible = $derived(show_legend && legend != null && depth1_arcs.length > 1)
479
+ let legend_element = $state<HTMLDivElement | undefined>()
480
+ let legend_placement = $derived.by(() => {
481
+ if (!legend_visible || !width || !height) return null
482
+ // Place against the settled (target) tiling, not the animated one - placement
483
+ // is stable during zoom tweens. Same visibility rule as rendering so hidden
484
+ // cells (zoom root, beyond max_depth) don't repel the legend.
485
+ const settled = layout.arcs.flatMap((arc, idx) =>
486
+ cell_visible(arc, target_rects[idx]) ? [rect_center(target_rects[idx])] : [],
487
+ )
488
+ return compute_element_placement({
489
+ plot_bounds: { x: pad.l, y: pad.t, width: inner_width, height: inner_height },
490
+ element: legend_element,
491
+ element_size: { width: 120, height: 60 },
492
+ axis_clearance: legend?.axis_clearance,
493
+ exclude_rects: [],
494
+ points: settled,
495
+ })
496
+ })
497
+ let legend_data: LegendItem[] = $derived(
498
+ hierarchy_legend_items(depth1_arcs, muted_ids, cell_color),
499
+ )
500
+
501
+ const toggle_category = (series_idx: number) =>
502
+ toggle_muted(muted_ids, depth1_arcs[series_idx]?.id)
503
+
504
+ $effect(() => set_fullscreen_bg(wrapper, fullscreen, `--treemap-fullscreen-bg`))
505
+
506
+ let breadcrumb_arcs = $derived(ancestor_chain(layout.arcs, zoom_root))
507
+
508
+ const export_chart = (format: `svg` | `png`) =>
509
+ export_hierarchy_chart(svg_element, export_filename, format)
510
+ </script>
511
+
512
+ <svelte:window
513
+ onkeydown={(evt) =>
514
+ handle_hierarchy_escape(evt, {
515
+ wrapper,
516
+ fullscreen,
517
+ zoomed,
518
+ zoom_out,
519
+ exit_fullscreen: () => (fullscreen = false),
520
+ })}
521
+ />
522
+
523
+ <div
524
+ bind:this={wrapper}
525
+ bind:clientWidth={width}
526
+ bind:clientHeight={height}
527
+ {...rest}
528
+ class={[`treemap`, rest.class]}
529
+ class:fullscreen
530
+ >
531
+ {#if width && height}
532
+ <div class="header-controls">
533
+ {@render header_controls?.({ height, width, fullscreen })}
534
+ {#if show_controls}
535
+ <TreemapControls
536
+ chart="treemap"
537
+ toggle_props={{
538
+ ...controls_toggle_props,
539
+ // join the header flex row instead of absolute positioning
540
+ style: `position: static; ${controls_toggle_props?.style ?? ``}`,
541
+ }}
542
+ pane_props={controls_pane_props}
543
+ bind:show_controls
544
+ bind:controls_open
545
+ bind:value_mode
546
+ bind:max_depth
547
+ bind:padding_inner
548
+ bind:padding_top
549
+ bind:padding_outer
550
+ bind:min_fraction
551
+ bind:show_labels
552
+ bind:label_text
553
+ bind:zoom_on_click
554
+ bind:show_breadcrumbs
555
+ {export_buttons}
556
+ on_export={export_chart}
557
+ >
558
+ {@render controls_extra?.({ zoom_root_id })}
559
+ </TreemapControls>
560
+ {/if}
561
+ {#if fullscreen_toggle}
562
+ <FullscreenToggle bind:fullscreen />
563
+ {/if}
564
+ </div>
565
+ {#if show_breadcrumbs && breadcrumb_arcs.length > 0}
566
+ <!-- plotly-style pathbar: chevron segments, current root last (disabled) -->
567
+ <nav class="breadcrumbs" aria-label="zoom path">
568
+ {#each breadcrumb_arcs as crumb, crumb_idx (crumb.node_idx)}
569
+ <button
570
+ type="button"
571
+ class="breadcrumb"
572
+ disabled={crumb_idx === breadcrumb_arcs.length - 1}
573
+ onclick={() => zoom_to(crumb)}
574
+ >
575
+ {crumb.depth === 0 ? `all` : (crumb.label ?? crumb.id)}
576
+ </button>
577
+ {/each}
578
+ </nav>
579
+ {/if}
580
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
581
+ <svg
582
+ bind:this={svg_element}
583
+ viewBox="0 0 {width} {height}"
584
+ role="application"
585
+ aria-label={rest[`aria-label`] ?? `Treemap chart`}
586
+ onmouseleave={() => set_cell_hover(null)}
587
+ ondblclick={handle_dblclick}
588
+ >
589
+ <!-- inert unless some cell references it via fill -->
590
+ <defs>
591
+ <pattern id={hatch_pattern_id} patternUnits="userSpaceOnUse" width="8" height="8">
592
+ <path class="hatch-pattern-line" d="M-1,1 l2,-2 M0,8 l8,-8 M7,9 l2,-2" />
593
+ </pattern>
594
+ </defs>
595
+ <!-- Hover/click delegation sits on the chart group (not the cells group) so
596
+ labels - which carry the same data-treemap-node-idx and are selectable text -
597
+ forward interactions to their cell instead of swallowing them -->
598
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
599
+ <g
600
+ transform={`translate(${pad.l}, ${pad.t})`}
601
+ onmousemove={handle_cell_hover_event}
602
+ onmouseleave={() => set_cell_hover(null)}
603
+ onfocusin={handle_cell_hover_event}
604
+ onfocusout={() => set_cell_hover(null)}
605
+ onclick={handle_cell_click}
606
+ onkeydown={handle_cell_keydown}
607
+ >
608
+ <!-- Cells: pre-order document order paints parents first, children on top -->
609
+ <g class="cells">
610
+ {#each visible_idxs as idx (idx)}
611
+ {@const rect = rects[idx]}
612
+ {#if cell_content}
613
+ {@render cell_content({ arc: layout.arcs[idx], rect })}
614
+ {:else}
615
+ {@const info = cell_info[idx]}
616
+ {@const opacity = cell_dim[idx].opacity}
617
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
618
+ <rect
619
+ x={rect.x}
620
+ y={rect.y}
621
+ width={rect.width}
622
+ height={rect.height}
623
+ data-treemap-node-idx={idx}
624
+ fill={info.fill}
625
+ fill-opacity={opacity}
626
+ role={cells_clickable ? `button` : undefined}
627
+ tabindex={idx === roving_idx ? 0 : -1}
628
+ aria-label={info.aria}
629
+ style:cursor={cells_clickable ? `pointer` : `default`}
630
+ />
631
+ {#if layout.arcs[idx].hatch}
632
+ <!-- Decorative texture overlay; ignores pointer events -->
633
+ <rect
634
+ class="cell-hatch"
635
+ aria-hidden="true"
636
+ x={rect.x}
637
+ y={rect.y}
638
+ width={rect.width}
639
+ height={rect.height}
640
+ fill="url(#{hatch_pattern_id})"
641
+ fill-opacity={opacity}
642
+ />
643
+ {/if}
644
+ {/if}
645
+ {/each}
646
+ </g>
647
+
648
+ <!-- Cell labels: selectable text; data-treemap-node-idx forwards hover/click
649
+ to the underlying cell via the chart-group delegation above -->
650
+ {#if show_labels && !cell_content}
651
+ <g class="cell-labels">
652
+ {#each visible_idxs as idx (idx)}
653
+ {@const lbl = label_attrs(layout.arcs[idx], rects[idx])}
654
+ {#if lbl}
655
+ <text
656
+ class="cell-label"
657
+ class:header={lbl.header}
658
+ data-treemap-node-idx={idx}
659
+ x={lbl.x}
660
+ y={lbl.y}
661
+ transform={lbl.transform}
662
+ fill={cell_info[idx].label_fill}
663
+ fill-opacity={cell_dim[idx].label_opacity}
664
+ style:cursor={cells_clickable ? `pointer` : `text`}
665
+ >
666
+ {lbl.text}
667
+ </text>
668
+ {/if}
669
+ {/each}
670
+ </g>
671
+ {/if}
672
+ </g>
673
+ </svg>
674
+ {/if}
675
+
676
+ {#if hover_info}
677
+ <PlotTooltip
678
+ x={hover_pos.x}
679
+ y={hover_pos.y}
680
+ offset={{ x: 10, y: 5 }}
681
+ constrain_to={{ width, height }}
682
+ fallback_size={{ width: 140, height: 44 }}
683
+ bg_color={hover_info.color}
684
+ >
685
+ {#if tooltip}
686
+ {@render tooltip(hover_info)}
687
+ {:else}
688
+ <strong>{hover_info.label_path.join(` › `)}</strong>: {format_value(
689
+ hover_info.value,
690
+ value_format,
691
+ )}
692
+ ({format_value(hover_info.fraction, `.1%`)} of total{hover_info.depth > 1
693
+ ? `, ${format_value(hover_info.parent_fraction, `.1%`)} of parent`
694
+ : ``})
695
+ {/if}
696
+ </PlotTooltip>
697
+ {/if}
698
+
699
+ {#if legend_visible}
700
+ {@const legend_left = legend_placement?.x ?? pad.l + 10}
701
+ {@const legend_top = legend_placement?.y ?? pad.t + 10}
702
+ <PlotLegend
703
+ bind:root_element={legend_element}
704
+ {...legend}
705
+ series_data={legend_data}
706
+ on_toggle={legend?.on_toggle ?? toggle_category}
707
+ on_item_hover={(item) =>
708
+ (hovered_idx =
709
+ item != null && item.series_idx >= 0
710
+ ? (depth1_arcs[item.series_idx]?.node_idx ?? null)
711
+ : null)}
712
+ style={`position: absolute; left: ${legend_left}px; top: ${legend_top}px; pointer-events: auto; ${
713
+ legend?.style ?? ``
714
+ }`}
715
+ />
716
+ {/if}
717
+
718
+ {#if metric && colorbar != null}
719
+ <div
720
+ bind:clientHeight={colorbar_height}
721
+ style="position: absolute; bottom: var(--treemap-colorbar-bottom, 8px); left: 50%; transform: translateX(-50%); width: var(--treemap-colorbar-width, 40%); min-width: 120px; pointer-events: auto;"
722
+ >
723
+ <ColorBar
724
+ {color_scale}
725
+ range={metric.range}
726
+ {...colorbar}
727
+ wrapper_style={`width: 100%; ${colorbar?.wrapper_style ?? ``}`}
728
+ />
729
+ </div>
730
+ {/if}
731
+
732
+ {@render children?.({ height, width, fullscreen })}
733
+ </div>
734
+
735
+ <style>
736
+ .treemap {
737
+ position: relative;
738
+ width: var(--treemap-width, 100%);
739
+ height: var(--treemap-height, auto);
740
+ min-height: var(--treemap-min-height, 300px);
741
+ container-type: size;
742
+ z-index: var(--treemap-z-index, auto);
743
+ /* flex-basis auto (not 1 = 0%) so an authored height wins over flex sizing in
744
+ column-flex parents while the chart still grows/shrinks to fill fixed layouts */
745
+ flex: var(--treemap-flex, 1 1 auto);
746
+ display: var(--treemap-display, flex);
747
+ flex-direction: column;
748
+ background: var(--treemap-bg, var(--plot-bg));
749
+ border-radius: var(--treemap-border-radius, var(--border-radius, 3pt));
750
+ }
751
+ .treemap.fullscreen {
752
+ position: fixed;
753
+ top: 0;
754
+ left: 0;
755
+ width: 100vw !important;
756
+ height: 100vh !important;
757
+ z-index: var(--treemap-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
758
+ margin: 0;
759
+ border-radius: 0;
760
+ background: var(--treemap-fullscreen-bg, var(--treemap-bg, var(--plot-bg)));
761
+ max-height: none !important;
762
+ overflow: hidden;
763
+ /* border-top (not padding-top): bind:clientHeight includes padding but excludes
764
+ borders */
765
+ border-top: var(--plot-fullscreen-padding-top, 2em) solid
766
+ var(--treemap-fullscreen-bg, var(--treemap-bg, var(--plot-bg, transparent)));
767
+ box-sizing: border-box;
768
+ }
769
+ .header-controls {
770
+ position: absolute;
771
+ top: var(--ctrl-btn-top, 5pt);
772
+ right: var(--fullscreen-btn-right, 4px);
773
+ z-index: var(--fullscreen-btn-z-index, 10);
774
+ display: flex;
775
+ align-items: center;
776
+ gap: 8px;
777
+ }
778
+ .header-controls :global(.fullscreen-toggle) {
779
+ position: static;
780
+ opacity: 1;
781
+ }
782
+ /* plotly-pathbar look: right-pointing chevron segments with a matching left
783
+ notch on all but the first, slightly overlapped so they read as one bar.
784
+ Opaque background: the pathbar overlays arbitrarily-colored cells, and a
785
+ translucent one would be illegible over dark fills */
786
+ .breadcrumb {
787
+ background: var(--treemap-btn-bg, light-dark(#e3e6ea, #33383f));
788
+ color: inherit;
789
+ border: none;
790
+ padding: 2px 14px 2px 12px;
791
+ cursor: pointer;
792
+ font: inherit;
793
+ clip-path: polygon(
794
+ 0 0,
795
+ calc(100% - 7px) 0,
796
+ 100% 50%,
797
+ calc(100% - 7px) 100%,
798
+ 0 100%,
799
+ 7px 50%
800
+ );
801
+ }
802
+ .breadcrumb:first-child {
803
+ border-radius: 3pt 0 0 3pt;
804
+ padding-inline-start: 8px;
805
+ clip-path: polygon(0 0, calc(100% - 7px) 0, 100% 50%, calc(100% - 7px) 100%, 0 100%);
806
+ }
807
+ .breadcrumb:hover:not(:disabled) {
808
+ background: var(--treemap-btn-hover-bg, light-dark(#d0d5db, #454b54));
809
+ }
810
+ /* inset focus ring: native outlines get clipped by the chevron clip-path and
811
+ hidden under the next overlapped segment */
812
+ .breadcrumb:focus-visible {
813
+ position: relative;
814
+ z-index: 1;
815
+ outline: none;
816
+ box-shadow: inset 0 0 0 2px var(--accent-color, Highlight);
817
+ }
818
+ .breadcrumbs {
819
+ position: absolute;
820
+ top: var(--treemap-breadcrumbs-top, 5pt);
821
+ left: var(--treemap-breadcrumbs-left, 8px);
822
+ z-index: 9;
823
+ display: flex;
824
+ align-items: stretch;
825
+ flex-wrap: wrap;
826
+ max-width: 75%;
827
+ font-size: var(--treemap-breadcrumbs-font-size, 0.85em);
828
+ }
829
+ /* negative gap: each chevron tip tucks into the next segment's left notch */
830
+ .breadcrumb + .breadcrumb {
831
+ margin-left: -6px;
832
+ }
833
+ .breadcrumb:disabled {
834
+ cursor: default;
835
+ font-weight: bold;
836
+ }
837
+ .treemap :global(.pane-toggle),
838
+ .treemap .header-controls {
839
+ opacity: 0;
840
+ transition:
841
+ opacity 0.2s,
842
+ background-color 0.2s;
843
+ }
844
+ .treemap:hover :global(.pane-toggle),
845
+ .treemap:hover .header-controls,
846
+ .treemap :global(.pane-toggle:focus-visible),
847
+ .treemap :global(.pane-toggle[aria-expanded='true']),
848
+ .treemap .header-controls:has(:global([aria-expanded='true'])),
849
+ .treemap .header-controls:focus-within {
850
+ opacity: 1;
851
+ }
852
+ svg {
853
+ width: var(--treemap-svg-width, 100%);
854
+ height: var(--treemap-svg-height, 100%);
855
+ flex: var(--treemap-svg-flex, 1);
856
+ overflow: var(--treemap-svg-overflow, visible);
857
+ fill: var(--text-color);
858
+ font-size: var(--treemap-font-size, 11px);
859
+ }
860
+ .cells rect {
861
+ /* stroke via CSS (not presentation attributes): var() substitution in SVG
862
+ presentation attributes is not reliably supported across browsers.
863
+ Dividers matter more here than in the sunburst: sibling gaps show the
864
+ parent's fill (parents paint under their children), which is identical to
865
+ the children's inherited color - without a stroke, same-color siblings
866
+ blend into one shape. Not defaulted to --plot-bg because that is often
867
+ semi-transparent (near-invisible as a line); light-dark keeps dividers
868
+ paper-colored in both themes, like plotly */
869
+ stroke: var(--treemap-cell-stroke, light-dark(white, #16181d));
870
+ stroke-width: var(--treemap-cell-stroke-width, 1);
871
+ transition: fill-opacity 0.15s ease;
872
+ }
873
+ .cells rect:hover {
874
+ filter: brightness(var(--treemap-hover-brightness, 1.08));
875
+ }
876
+ /* decorative overlay: never intercepts pointer events, no hover effect */
877
+ .cells rect.cell-hatch {
878
+ stroke: none;
879
+ pointer-events: none;
880
+ }
881
+ /* subtle by default: thin stripes inheriting the cell border color (itself
882
+ defaulting to the chart bg) at low opacity, so hatching matches the gaps
883
+ between cells instead of reading as solid white */
884
+ .hatch-pattern-line {
885
+ stroke: var(
886
+ --treemap-hatch-stroke,
887
+ color-mix(
888
+ in srgb,
889
+ var(--treemap-cell-stroke, light-dark(white, #16181d)) 30%,
890
+ transparent
891
+ )
892
+ );
893
+ stroke-width: var(--treemap-hatch-stroke-width, 0.35);
894
+ }
895
+ .cell-label {
896
+ text-anchor: middle;
897
+ dominant-baseline: central;
898
+ /* selectable so labels can be copied; clicks/hover still reach the underlying
899
+ cell via data-treemap-node-idx + delegation on the chart group */
900
+ -webkit-user-select: text;
901
+ user-select: text;
902
+ }
903
+ .cell-label.header {
904
+ text-anchor: start;
905
+ font-weight: 600;
906
+ }
907
+ </style>