matterviz 0.4.0 → 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 (527) 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 +132 -219
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +9 -20
  8. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  9. package/dist/brillouin/BrillouinZoneExportPane.svelte +47 -96
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  11. package/dist/brillouin/BrillouinZoneInfoPane.svelte +15 -42
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
  13. package/dist/brillouin/BrillouinZoneScene.svelte +63 -230
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
  15. package/dist/brillouin/ReciprocalVectors.svelte +39 -0
  16. package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
  17. package/dist/brillouin/compute.d.ts +2 -0
  18. package/dist/brillouin/compute.js +80 -77
  19. package/dist/brillouin/geometry.d.ts +8 -0
  20. package/dist/brillouin/geometry.js +57 -0
  21. package/dist/brillouin/index.d.ts +2 -0
  22. package/dist/brillouin/index.js +2 -0
  23. package/dist/brillouin/types.d.ts +2 -2
  24. package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
  25. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
  26. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +143 -246
  27. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
  28. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +652 -976
  29. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
  30. package/dist/chempot-diagram/async-compute.svelte.js +1 -1
  31. package/dist/chempot-diagram/color.d.ts +3 -6
  32. package/dist/chempot-diagram/color.js +5 -5
  33. package/dist/chempot-diagram/compute.d.ts +3 -3
  34. package/dist/chempot-diagram/compute.js +3 -1
  35. package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
  36. package/dist/chempot-diagram/controls-state.svelte.js +42 -0
  37. package/dist/chempot-diagram/export.d.ts +47 -0
  38. package/dist/chempot-diagram/export.js +133 -0
  39. package/dist/chempot-diagram/index.d.ts +1 -0
  40. package/dist/chempot-diagram/index.js +1 -0
  41. package/dist/chempot-diagram/pointer.d.ts +0 -10
  42. package/dist/chempot-diagram/pointer.js +4 -4
  43. package/dist/chempot-diagram/types.d.ts +3 -3
  44. package/dist/colors/index.d.ts +0 -4
  45. package/dist/colors/index.js +21 -22
  46. package/dist/composition/BarChart.svelte +37 -31
  47. package/dist/composition/BubbleChart.svelte +17 -17
  48. package/dist/composition/Composition.svelte +7 -11
  49. package/dist/composition/Formula.svelte +18 -25
  50. package/dist/composition/FormulaFilter.svelte +96 -98
  51. package/dist/composition/PieChart.svelte +60 -46
  52. package/dist/composition/chem-sys.js +3 -2
  53. package/dist/composition/format.js +4 -3
  54. package/dist/composition/index.d.ts +2 -0
  55. package/dist/composition/index.js +14 -0
  56. package/dist/composition/parse.d.ts +2 -3
  57. package/dist/composition/parse.js +27 -28
  58. package/dist/constants.js +11 -9
  59. package/dist/controls.d.ts +1 -0
  60. package/dist/controls.js +0 -1
  61. package/dist/convex-hull/ConvexHull.svelte +20 -25
  62. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
  63. package/dist/convex-hull/ConvexHull2D.svelte +176 -256
  64. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  65. package/dist/convex-hull/ConvexHull3D.svelte +269 -804
  66. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  67. package/dist/convex-hull/ConvexHull4D.svelte +236 -755
  68. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  69. package/dist/convex-hull/ConvexHullChrome.svelte +264 -0
  70. package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
  71. package/dist/convex-hull/ConvexHullControls.svelte +128 -49
  72. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
  73. package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
  74. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
  75. package/dist/convex-hull/ConvexHullStats.svelte +210 -351
  76. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
  77. package/dist/convex-hull/ConvexHullTooltip.svelte +16 -14
  78. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
  79. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  80. package/dist/convex-hull/StructurePopup.svelte +17 -21
  81. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  82. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  83. package/dist/convex-hull/barycentric-coords.d.ts +2 -4
  84. package/dist/convex-hull/barycentric-coords.js +6 -33
  85. package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
  86. package/dist/convex-hull/canvas-interactions.svelte.js +279 -0
  87. package/dist/convex-hull/helpers.d.ts +47 -17
  88. package/dist/convex-hull/helpers.js +186 -101
  89. package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
  90. package/dist/convex-hull/hull-state.svelte.js +124 -0
  91. package/dist/convex-hull/index.d.ts +9 -7
  92. package/dist/convex-hull/index.js +7 -2
  93. package/dist/convex-hull/thermodynamics.js +109 -937
  94. package/dist/convex-hull/types.d.ts +12 -4
  95. package/dist/convex-hull/types.js +12 -0
  96. package/dist/coordination/CoordinationBarPlot.svelte +41 -43
  97. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  98. package/dist/coordination/calc-coordination.d.ts +1 -1
  99. package/dist/coordination/calc-coordination.js +28 -25
  100. package/dist/element/BohrAtom.svelte +6 -8
  101. package/dist/element/ElementHeading.svelte +4 -1
  102. package/dist/element/ElementPhoto.svelte +11 -9
  103. package/dist/element/ElementStats.svelte +12 -10
  104. package/dist/element/ElementTile.svelte +24 -29
  105. package/dist/element/ElementTile.svelte.d.ts +1 -1
  106. package/dist/element/Nucleus.svelte +4 -2
  107. package/dist/element/data.d.ts +1 -2
  108. package/dist/element/helpers.d.ts +4 -0
  109. package/dist/element/helpers.js +18 -0
  110. package/dist/element/index.d.ts +1 -0
  111. package/dist/element/index.js +3 -0
  112. package/dist/feedback/DragOverlay.svelte +5 -3
  113. package/dist/feedback/DragOverlay.svelte.d.ts +2 -1
  114. package/dist/feedback/StatusMessage.svelte +20 -7
  115. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  116. package/dist/fermi-surface/FermiSurface.svelte +78 -165
  117. package/dist/fermi-surface/FermiSurface.svelte.d.ts +9 -20
  118. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  119. package/dist/fermi-surface/FermiSurfaceScene.svelte +93 -272
  120. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
  121. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  122. package/dist/fermi-surface/compute.js +14 -13
  123. package/dist/fermi-surface/export.js +4 -15
  124. package/dist/fermi-surface/index.d.ts +0 -1
  125. package/dist/fermi-surface/index.js +0 -1
  126. package/dist/fermi-surface/parse.d.ts +1 -1
  127. package/dist/fermi-surface/parse.js +84 -97
  128. package/dist/fermi-surface/types.d.ts +2 -2
  129. package/dist/heatmap-matrix/HeatmapMatrix.svelte +161 -185
  130. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +6 -5
  131. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +12 -9
  132. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
  133. package/dist/heatmap-matrix/index.d.ts +8 -2
  134. package/dist/icons.d.ts +4 -0
  135. package/dist/icons.js +4 -0
  136. package/dist/index.d.ts +1 -0
  137. package/dist/index.js +1 -0
  138. package/dist/io/ExportPane.svelte +170 -0
  139. package/dist/io/ExportPane.svelte.d.ts +17 -0
  140. package/dist/io/decompress.d.ts +2 -1
  141. package/dist/io/decompress.js +22 -36
  142. package/dist/io/export.d.ts +9 -1
  143. package/dist/io/export.js +37 -29
  144. package/dist/io/fetch.d.ts +2 -4
  145. package/dist/io/fetch.js +0 -11
  146. package/dist/io/file-drop.d.ts +7 -0
  147. package/dist/io/file-drop.js +13 -0
  148. package/dist/io/index.d.ts +1 -0
  149. package/dist/io/index.js +3 -0
  150. package/dist/io/is-binary.d.ts +9 -0
  151. package/dist/io/is-binary.js +45 -1
  152. package/dist/io/types.d.ts +13 -0
  153. package/dist/io/url-drop.js +13 -25
  154. package/dist/isosurface/Isosurface.svelte +17 -23
  155. package/dist/isosurface/IsosurfaceControls.svelte +37 -42
  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 +50 -48
  159. package/dist/labels.d.ts +6 -2
  160. package/dist/labels.js +44 -14
  161. package/dist/layout/FullscreenButton.svelte +34 -0
  162. package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
  163. package/dist/layout/FullscreenToggle.svelte +12 -12
  164. package/dist/layout/InfoCard.svelte +7 -18
  165. package/dist/layout/InfoTag.svelte +1 -3
  166. package/dist/layout/NumberRangeInput.svelte +46 -0
  167. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  168. package/dist/layout/PropertyFilter.svelte +2 -6
  169. package/dist/layout/SettingsSection.svelte +21 -17
  170. package/dist/layout/SubpageGrid.svelte +3 -1
  171. package/dist/layout/ViewerChrome.svelte +115 -0
  172. package/dist/layout/ViewerChrome.svelte.d.ts +18 -0
  173. package/dist/layout/fullscreen.d.ts +4 -0
  174. package/dist/layout/fullscreen.js +3 -5
  175. package/dist/layout/fullscreen.svelte.d.ts +8 -0
  176. package/dist/layout/fullscreen.svelte.js +36 -0
  177. package/dist/layout/index.d.ts +4 -0
  178. package/dist/layout/index.js +4 -0
  179. package/dist/layout/json-tree/JsonNode.svelte +8 -11
  180. package/dist/layout/json-tree/JsonTree.svelte +53 -77
  181. package/dist/layout/json-tree/JsonValue.svelte +7 -15
  182. package/dist/layout/json-tree/utils.js +14 -23
  183. package/dist/math.d.ts +9 -3
  184. package/dist/math.js +37 -21
  185. package/dist/overlays/ContextMenu.svelte +4 -2
  186. package/dist/overlays/CopyButton.svelte +11 -5
  187. package/dist/overlays/DragControlTab.svelte +6 -1
  188. package/dist/overlays/DraggablePane.svelte +81 -26
  189. package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
  190. package/dist/overlays/GlassChip.svelte +30 -0
  191. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  192. package/dist/overlays/InfoPaneCards.svelte +11 -23
  193. package/dist/overlays/index.d.ts +11 -0
  194. package/dist/overlays/index.js +26 -0
  195. package/dist/overlays/portal.d.ts +2 -0
  196. package/dist/overlays/portal.js +14 -0
  197. package/dist/periodic-table/PeriodicTable.svelte +115 -146
  198. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  199. package/dist/periodic-table/PeriodicTableControls.svelte +11 -63
  200. package/dist/periodic-table/TableInset.svelte +9 -4
  201. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
  202. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  203. package/dist/phase-diagram/PhaseDiagramControls.svelte +73 -96
  204. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
  205. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +4 -2
  206. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
  207. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +49 -134
  208. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
  209. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  210. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  211. package/dist/phase-diagram/build-diagram.js +3 -3
  212. package/dist/phase-diagram/colors.js +1 -1
  213. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  214. package/dist/phase-diagram/parse.d.ts +2 -1
  215. package/dist/phase-diagram/parse.js +15 -15
  216. package/dist/phase-diagram/svg-to-diagram.js +21 -15
  217. package/dist/phase-diagram/utils.js +2 -3
  218. package/dist/plot/bar/BarPlot.svelte +494 -692
  219. package/dist/plot/bar/BarPlot.svelte.d.ts +9 -15
  220. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  221. package/dist/plot/bar/BarPlotControls.svelte.d.ts +5 -7
  222. package/dist/plot/bar/SpacegroupBarPlot.svelte +31 -37
  223. package/dist/plot/bar/data.d.ts +5 -4
  224. package/dist/plot/bar/data.js +2 -1
  225. package/dist/plot/box/BoxPlot.svelte +361 -439
  226. package/dist/plot/box/BoxPlot.svelte.d.ts +6 -3
  227. package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
  228. package/dist/plot/box/Violin.svelte.d.ts +2 -1
  229. package/dist/plot/box/box-plot.d.ts +3 -2
  230. package/dist/plot/box/box-plot.js +6 -3
  231. package/dist/plot/box/kde.d.ts +2 -1
  232. package/dist/plot/box/kde.js +4 -4
  233. package/dist/plot/core/auto-place.d.ts +1 -1
  234. package/dist/plot/core/auto-place.js +5 -2
  235. package/dist/plot/core/axis-utils.d.ts +12 -7
  236. package/dist/plot/core/axis-utils.js +48 -48
  237. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  238. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  239. package/dist/plot/core/components/ColorBar.svelte +106 -111
  240. package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
  241. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  242. package/dist/plot/core/components/FillArea.svelte +10 -17
  243. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  244. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  245. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  246. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  247. package/dist/plot/core/components/Line.svelte +21 -11
  248. package/dist/plot/core/components/Line.svelte.d.ts +5 -2
  249. package/dist/plot/core/components/PlotAxis.svelte +42 -18
  250. package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
  251. package/dist/plot/core/components/PlotControls.svelte +65 -41
  252. package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
  253. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  254. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  255. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  256. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  257. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  258. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  259. package/dist/plot/core/components/ReferenceLine3D.svelte +56 -59
  260. package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
  261. package/dist/plot/core/components/ReferencePlane.svelte +9 -10
  262. package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
  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 +29 -476
  271. package/dist/plot/core/data-transform.js +9 -5
  272. package/dist/plot/core/fill-utils.d.ts +7 -4
  273. package/dist/plot/core/fill-utils.js +34 -9
  274. package/dist/plot/core/index.d.ts +1 -0
  275. package/dist/plot/core/index.js +1 -0
  276. package/dist/plot/core/interactions.d.ts +5 -1
  277. package/dist/plot/core/interactions.js +14 -2
  278. package/dist/plot/core/layout.d.ts +3 -1
  279. package/dist/plot/core/layout.js +22 -13
  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 +35 -0
  283. package/dist/plot/core/pan-zoom.svelte.js +221 -0
  284. package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
  285. package/dist/plot/core/placed-tween.svelte.js +68 -0
  286. package/dist/plot/core/reference-line.d.ts +10 -10
  287. package/dist/plot/core/reference-line.js +7 -7
  288. package/dist/plot/core/scales.d.ts +40 -25
  289. package/dist/plot/core/scales.js +49 -21
  290. package/dist/plot/core/svg.d.ts +2 -1
  291. package/dist/plot/core/types/fills.d.ts +101 -0
  292. package/dist/plot/core/types/fills.js +15 -0
  293. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  294. package/dist/plot/core/types/plot-3d.js +4 -0
  295. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  296. package/dist/plot/core/types/reference-lines.js +9 -0
  297. package/dist/plot/core/types.d.ts +47 -331
  298. package/dist/plot/core/types.js +23 -20
  299. package/dist/plot/core/utils/hierarchy-chart.d.ts +51 -0
  300. package/dist/plot/core/utils/hierarchy-chart.js +179 -0
  301. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  302. package/dist/plot/core/utils/hierarchy-labels.js +99 -0
  303. package/dist/plot/core/utils/label-placement.d.ts +1 -1
  304. package/dist/plot/core/utils/label-placement.js +5 -5
  305. package/dist/plot/core/utils.d.ts +2 -1
  306. package/dist/plot/histogram/Histogram.svelte +373 -631
  307. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  308. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  309. package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
  310. package/dist/plot/histogram/histogram.d.ts +31 -0
  311. package/dist/plot/histogram/histogram.js +70 -0
  312. package/dist/plot/histogram/index.d.ts +1 -0
  313. package/dist/plot/histogram/index.js +1 -0
  314. package/dist/plot/index.d.ts +1 -0
  315. package/dist/plot/index.js +1 -0
  316. package/dist/plot/sankey/Sankey.svelte +79 -73
  317. package/dist/plot/sankey/Sankey.svelte.d.ts +3 -3
  318. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  319. package/dist/plot/sankey/sankey.js +3 -1
  320. package/dist/plot/scatter/BinnedScatterPlot.svelte +264 -112
  321. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +11 -4
  322. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  323. package/dist/plot/scatter/ScatterPlot.svelte +623 -821
  324. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +9 -15
  325. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  326. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  327. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  328. package/dist/plot/scatter/binned-scatter-types.d.ts +6 -12
  329. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  330. package/dist/plot/scatter/index.d.ts +1 -1
  331. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  332. package/dist/plot/scatter/scatter-data.js +8 -7
  333. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +40 -60
  334. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
  335. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +17 -15
  336. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
  337. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +232 -234
  338. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
  339. package/dist/plot/scatter-3d/Surface3D.svelte +9 -17
  340. package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
  341. package/dist/plot/sunburst/Sunburst.svelte +256 -338
  342. package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
  343. package/dist/plot/sunburst/index.d.ts +1 -1
  344. package/dist/plot/sunburst/index.js +2 -1
  345. package/dist/plot/sunburst/render.d.ts +2 -2
  346. package/dist/plot/sunburst/render.js +47 -22
  347. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  348. package/dist/plot/sunburst/sunburst.js +14 -4
  349. package/dist/plot/treemap/Treemap.svelte +907 -0
  350. package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
  351. package/dist/plot/treemap/index.d.ts +3 -0
  352. package/dist/plot/treemap/index.js +4 -0
  353. package/dist/plot/treemap/treemap.d.ts +26 -0
  354. package/dist/plot/treemap/treemap.js +99 -0
  355. package/dist/rdf/RdfPlot.svelte +22 -26
  356. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  357. package/dist/rdf/calc-rdf.js +4 -8
  358. package/dist/sanitize.js +14 -3
  359. package/dist/scene/SceneCamera.svelte +62 -0
  360. package/dist/scene/SceneCamera.svelte.d.ts +19 -0
  361. package/dist/scene/bind-renderer.svelte.d.ts +2 -0
  362. package/dist/scene/bind-renderer.svelte.js +22 -0
  363. package/dist/scene/index.d.ts +4 -0
  364. package/dist/scene/index.js +5 -0
  365. package/dist/scene/props.js +52 -0
  366. package/dist/scene/types.d.ts +26 -0
  367. package/dist/scene/types.js +1 -0
  368. package/dist/settings.d.ts +33 -11
  369. package/dist/settings.js +136 -51
  370. package/dist/spectral/Bands.svelte +231 -237
  371. package/dist/spectral/Bands.svelte.d.ts +3 -2
  372. package/dist/spectral/BandsAndDos.svelte +29 -29
  373. package/dist/spectral/BrillouinBandsDos.svelte +21 -29
  374. package/dist/spectral/Dos.svelte +75 -77
  375. package/dist/spectral/Dos.svelte.d.ts +2 -1
  376. package/dist/spectral/helpers.d.ts +9 -9
  377. package/dist/spectral/helpers.js +66 -70
  378. package/dist/state.svelte.d.ts +0 -7
  379. package/dist/state.svelte.js +5 -24
  380. package/dist/structure/Arrow.svelte +6 -21
  381. package/dist/structure/ArrowInstances.svelte +160 -0
  382. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  383. package/dist/structure/AtomLegend.svelte +115 -75
  384. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  385. package/dist/structure/Bond.svelte +31 -23
  386. package/dist/structure/CanvasTooltip.svelte +16 -3
  387. package/dist/structure/CellSelect.svelte +22 -29
  388. package/dist/structure/CellSelect.svelte.d.ts +2 -1
  389. package/dist/structure/Cylinder.svelte +24 -40
  390. package/dist/structure/InstancedAtoms.svelte +114 -0
  391. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  392. package/dist/structure/Lattice.svelte +85 -149
  393. package/dist/structure/SiteLabels.svelte +122 -0
  394. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  395. package/dist/structure/Structure.svelte +725 -735
  396. package/dist/structure/Structure.svelte.d.ts +8 -16
  397. package/dist/structure/StructureCarousel.svelte +630 -0
  398. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  399. package/dist/structure/StructureControls.svelte +298 -325
  400. package/dist/structure/StructureControls.svelte.d.ts +5 -3
  401. package/dist/structure/StructureExportPane.svelte +62 -168
  402. package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
  403. package/dist/structure/StructureInfoPane.svelte +77 -69
  404. package/dist/structure/StructureInfoPane.svelte.d.ts +6 -6
  405. package/dist/structure/StructureScene.svelte +758 -600
  406. package/dist/structure/StructureScene.svelte.d.ts +24 -20
  407. package/dist/structure/StructureViewport.svelte +386 -0
  408. package/dist/structure/StructureViewport.svelte.d.ts +63 -0
  409. package/dist/structure/atom-label-placement.d.ts +18 -0
  410. package/dist/structure/atom-label-placement.js +115 -0
  411. package/dist/structure/atom-properties.d.ts +3 -1
  412. package/dist/structure/atom-properties.js +115 -54
  413. package/dist/structure/bond-order-perception.js +2 -4
  414. package/dist/structure/bonding.d.ts +12 -2
  415. package/dist/structure/bonding.js +172 -73
  416. package/dist/structure/edit-history.d.ts +6 -0
  417. package/dist/structure/edit-history.js +27 -0
  418. package/dist/structure/export.d.ts +25 -4
  419. package/dist/structure/export.js +103 -153
  420. package/dist/structure/format-detect.d.ts +1 -0
  421. package/dist/structure/format-detect.js +22 -0
  422. package/dist/structure/geometry.d.ts +9 -0
  423. package/dist/structure/geometry.js +27 -0
  424. package/dist/structure/index.d.ts +18 -1
  425. package/dist/structure/index.js +15 -2
  426. package/dist/structure/measure.d.ts +1 -1
  427. package/dist/structure/measure.js +6 -12
  428. package/dist/structure/parse.d.ts +5 -3
  429. package/dist/structure/parse.js +551 -567
  430. package/dist/structure/partial-occupancy.d.ts +0 -1
  431. package/dist/structure/partial-occupancy.js +5 -3
  432. package/dist/structure/pbc.d.ts +1 -1
  433. package/dist/structure/pbc.js +204 -51
  434. package/dist/structure/polyhedra.d.ts +40 -0
  435. package/dist/structure/polyhedra.js +587 -0
  436. package/dist/structure/site.d.ts +4 -0
  437. package/dist/structure/site.js +1 -0
  438. package/dist/structure/supercell.d.ts +1 -2
  439. package/dist/structure/supercell.js +10 -15
  440. package/dist/structure/validation.js +5 -6
  441. package/dist/symmetry/SymmetryElementControls.svelte +69 -0
  442. package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
  443. package/dist/symmetry/SymmetryElements.svelte +350 -0
  444. package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
  445. package/dist/symmetry/SymmetryStats.svelte +142 -49
  446. package/dist/symmetry/WyckoffTable.svelte +58 -16
  447. package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
  448. package/dist/symmetry/cell-transform.js +8 -14
  449. package/dist/symmetry/index.d.ts +15 -4
  450. package/dist/symmetry/index.js +308 -81
  451. package/dist/symmetry/spacegroups.d.ts +5 -1
  452. package/dist/symmetry/spacegroups.js +18 -3
  453. package/dist/symmetry/symmetry-elements.d.ts +33 -0
  454. package/dist/symmetry/symmetry-elements.js +521 -0
  455. package/dist/symmetry/wyckoff-db.d.ts +9 -0
  456. package/dist/symmetry/wyckoff-db.js +87 -0
  457. package/dist/table/HeatmapTable.svelte +1104 -378
  458. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  459. package/dist/table/ToggleMenu.svelte +5 -12
  460. package/dist/table/index.d.ts +21 -6
  461. package/dist/table/index.js +34 -21
  462. package/dist/theme/ThemeControl.svelte +16 -8
  463. package/dist/theme/index.d.ts +1 -5
  464. package/dist/theme/index.js +4 -10
  465. package/dist/theme/themes.mjs +14 -7
  466. package/dist/tooltip/KCoords.svelte +4 -1
  467. package/dist/tooltip/TooltipContent.svelte +2 -5
  468. package/dist/trajectory/Trajectory.svelte +556 -352
  469. package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
  470. package/dist/trajectory/TrajectoryError.svelte +11 -8
  471. package/dist/trajectory/TrajectoryExportPane.svelte +28 -54
  472. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
  473. package/dist/trajectory/TrajectoryInfoPane.svelte +88 -99
  474. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
  475. package/dist/trajectory/constants.js +6 -2
  476. package/dist/trajectory/extract.js +73 -71
  477. package/dist/trajectory/format-detect.d.ts +0 -1
  478. package/dist/trajectory/format-detect.js +13 -17
  479. package/dist/trajectory/frame-reader.d.ts +2 -1
  480. package/dist/trajectory/frame-reader.js +74 -136
  481. package/dist/trajectory/helpers.d.ts +12 -2
  482. package/dist/trajectory/helpers.js +68 -37
  483. package/dist/trajectory/index.d.ts +1 -0
  484. package/dist/trajectory/index.js +3 -0
  485. package/dist/trajectory/parse/ase.d.ts +9 -1
  486. package/dist/trajectory/parse/ase.js +47 -32
  487. package/dist/trajectory/parse/diagnostics.d.ts +3 -0
  488. package/dist/trajectory/parse/diagnostics.js +14 -0
  489. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  490. package/dist/trajectory/parse/h5-utils.js +109 -0
  491. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  492. package/dist/trajectory/parse/hdf5.js +116 -106
  493. package/dist/trajectory/parse/index.d.ts +3 -4
  494. package/dist/trajectory/parse/index.js +68 -119
  495. package/dist/trajectory/parse/lammps.d.ts +0 -2
  496. package/dist/trajectory/parse/lammps.js +14 -16
  497. package/dist/trajectory/parse/pymatgen.d.ts +2 -0
  498. package/dist/trajectory/parse/pymatgen.js +74 -0
  499. package/dist/trajectory/parse/vasp.js +13 -9
  500. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  501. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  502. package/dist/trajectory/parse/vaspout-h5.d.ts +5 -0
  503. package/dist/trajectory/parse/vaspout-h5.js +284 -0
  504. package/dist/trajectory/parse/xyz.d.ts +9 -21
  505. package/dist/trajectory/parse/xyz.js +38 -39
  506. package/dist/trajectory/plotting.d.ts +8 -10
  507. package/dist/trajectory/plotting.js +102 -127
  508. package/dist/utils.d.ts +4 -0
  509. package/dist/utils.js +8 -1
  510. package/dist/xrd/XrdPlot.svelte +56 -85
  511. package/dist/xrd/broadening.d.ts +2 -1
  512. package/dist/xrd/broadening.js +2 -1
  513. package/dist/xrd/calc-xrd.js +8 -15
  514. package/dist/xrd/index.d.ts +2 -2
  515. package/dist/xrd/parse.d.ts +0 -1
  516. package/dist/xrd/parse.js +56 -73
  517. package/package.json +77 -36
  518. package/dist/api/mp.d.ts +0 -6
  519. package/dist/api/mp.js +0 -22
  520. package/dist/element/data.json +0 -11864
  521. package/dist/fermi-surface/marching-cubes.d.ts +0 -2
  522. package/dist/fermi-surface/marching-cubes.js +0 -2
  523. package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
  524. package/dist/plot/core/hover-lock.svelte.js +0 -45
  525. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
  526. package/dist/structure/label-placement.d.ts +0 -14
  527. package/dist/structure/label-placement.js +0 -72
@@ -3,11 +3,9 @@
3
3
  generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
4
4
  >
5
5
  import type { D3InterpolateName } from '../../colors'
6
- import { pick_contrast_color } from '../../colors'
7
- import { export_svg_as_png, export_svg_as_svg } from '../../io/export'
8
6
  import { format_value } from '../../labels'
9
7
  import { FullscreenToggle, set_fullscreen_bg } from '../../layout'
10
- import { DEG_TO_RAD } from '../../math'
8
+ import { DEG_TO_RAD, type Vec2 } from '../../math'
11
9
  import type {
12
10
  BasePlotProps,
13
11
  LegendConfig,
@@ -21,20 +19,38 @@
21
19
  SunburstValueMode,
22
20
  } from '..'
23
21
  import { ColorBar, PlotLegend, PlotTooltip, SunburstControls } from '..'
24
- import { closest_data_idx, get_relative_coords } from '../core/interactions'
25
- import {
26
- compute_element_placement,
27
- filter_padding,
28
- measure_text_width,
29
- } from '../core/layout'
22
+ import { closest_data_idx } from '../core/interactions'
23
+ import { compute_element_placement, filter_padding } from '../core/layout'
30
24
  import type { Sides } from '../core/layout'
31
- import { create_color_scale } from '../core/scales'
25
+ import { SCALE_DEFAULTS } from '../core/types'
26
+ import {
27
+ ancestor_chain,
28
+ compute_metric_colors,
29
+ compute_node_dim,
30
+ compute_node_infos,
31
+ handle_hierarchy_escape,
32
+ hierarchy_legend_items,
33
+ is_activation_key,
34
+ node_handler_props,
35
+ pointer_pos,
36
+ prune_muted_ids,
37
+ safe_hierarchy_layout,
38
+ selection_within,
39
+ svg_label_font,
40
+ toggle_muted,
41
+ } from '../core/utils/hierarchy-chart'
42
+ import {
43
+ export_hierarchy_chart,
44
+ make_cached_contrast,
45
+ make_cached_text_width,
46
+ node_display_name,
47
+ } from '../core/utils/hierarchy-labels'
32
48
  import {
33
49
  arc_label_transform,
34
50
  arrow_nav_target,
35
51
  project_arcs,
36
- type ScreenArc as ScreenArcOf,
37
52
  } from './render'
53
+ import type { ScreenArc as ScreenArcOf } from './render'
38
54
  import { compute_sunburst_layout, type PositionedArc } from './sunburst'
39
55
  import { DEFAULTS } from '../../settings'
40
56
  import { arc as d3_arc } from 'd3-shape'
@@ -67,7 +83,7 @@
67
83
  zoom_root_id = $bindable(null),
68
84
  show_breadcrumbs = $bindable(DEFAULTS.sunburst.show_breadcrumbs),
69
85
  color_values,
70
- color_scale = `interpolateViridis`,
86
+ color_scale = SCALE_DEFAULTS.scheme,
71
87
  color_range,
72
88
  colorbar = {},
73
89
  export_buttons = true,
@@ -95,66 +111,69 @@
95
111
  header_controls,
96
112
  controls_extra,
97
113
  ...rest
98
- }: HTMLAttributes<HTMLDivElement> & Omit<BasePlotProps, `change`> & {
99
- data?: SunburstNode<Metadata> | SunburstNode<Metadata>[]
100
- shape?: SunburstShape // polar rings (sunburst) or stacked rows (icicle)
101
- value_mode?: SunburstValueMode
102
- sort?: SunburstSort
103
- level_lighten?: number
104
- // Aggregate sibling arcs below this fraction of the total into one 'Other' leaf
105
- // per parent (only when >= 2 qualify); 0 disables
106
- min_fraction?: number
107
- other_label?: string
108
- max_depth?: number // rings shown below the current zoom root (0 = all)
109
- inner_radius?: number // center hole as fraction of outer radius
110
- pad_angle?: number // degrees between sibling arcs
111
- show_labels?: boolean
112
- label_rotation?: SunburstLabelRotation
113
- label_text?: SunburstLabelText // what labels display (plotly textinfo equivalent)
114
- zoom_on_click?: boolean
115
- zoom_root_id?: string | number | null // id of the arc the view is rooted on
116
- show_breadcrumbs?: boolean // clickable ancestor trail when zoomed
117
- // Color arcs by a numeric metric (continuous colormap) instead of categorical
118
- // inheritance; return null to keep an arc's categorical color
119
- color_values?: (arc: PositionedArc<Metadata>) => number | null
120
- color_scale?: D3InterpolateName
121
- color_range?: [number, number] // defaults to the metric's [min, max]
122
- colorbar?: ComponentProps<typeof ColorBar> | null // null hides it
123
- export_buttons?: boolean // SVG/PNG download buttons in the controls pane
124
- export_filename?: string
125
- tween?: TweenOptions<{ x0: number; x1: number; y0: number; n_rings: number }>
126
- value_format?: string
127
- padding?: Sides
128
- legend?: LegendConfig | null
129
- show_legend?: boolean
130
- tooltip?: Snippet<[SunburstNodeHandlerProps<Metadata>]>
131
- // Fully replace the default arc path. NOTE: this also replaces the built-in
132
- // hover/focus/click + tooltip wiring, so re-implement any interactivity you
133
- // need inside the snippet.
134
- arc_content?: Snippet<
135
- [{ arc: PositionedArc<Metadata>; a0: number; a1: number; r0: number; r1: number }]
136
- >
137
- center_content?: Snippet<
138
- [{ root: PositionedArc<Metadata> | null; radius: number; zoomed: boolean }]
139
- >
140
- change?: (data: SunburstNodeHandlerProps<Metadata> | null) => void
141
- on_node_click?: (
142
- data: SunburstNodeHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
143
- ) => void
144
- on_node_hover?: (
145
- data:
146
- | (SunburstNodeHandlerProps<Metadata> & { event: MouseEvent | FocusEvent })
147
- | null,
148
- ) => void
149
- on_zoom?: (data: { root: SunburstNodeHandlerProps<Metadata> | null }) => void
150
- header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
151
- controls_extra?: Snippet<[{ zoom_root_id: string | number | null }]>
152
- } = $props()
114
+ }: HTMLAttributes<HTMLDivElement> &
115
+ Omit<BasePlotProps, `change`> & {
116
+ data?: SunburstNode<Metadata> | SunburstNode<Metadata>[]
117
+ shape?: SunburstShape // polar rings (sunburst) or stacked rows (icicle)
118
+ value_mode?: SunburstValueMode
119
+ sort?: SunburstSort
120
+ level_lighten?: number
121
+ // Aggregate sibling arcs below this fraction of the total into one 'Other' leaf
122
+ // per parent (only when >= 2 qualify); 0 disables
123
+ min_fraction?: number
124
+ other_label?: string
125
+ max_depth?: number // rings shown below the current zoom root (0 = all)
126
+ inner_radius?: number // center hole as fraction of outer radius
127
+ pad_angle?: number // degrees between sibling arcs
128
+ show_labels?: boolean
129
+ label_rotation?: SunburstLabelRotation
130
+ label_text?: SunburstLabelText // what labels display (plotly textinfo equivalent)
131
+ zoom_on_click?: boolean
132
+ zoom_root_id?: string | number | null // id of the arc the view is rooted on
133
+ show_breadcrumbs?: boolean // clickable ancestor trail when zoomed
134
+ // Color arcs by a numeric metric (continuous colormap) instead of categorical
135
+ // inheritance; return null to keep an arc's categorical color
136
+ color_values?: (arc: PositionedArc<Metadata>) => number | null
137
+ color_scale?: D3InterpolateName
138
+ color_range?: Vec2 // defaults to the metric's [min, max]
139
+ colorbar?: ComponentProps<typeof ColorBar> | null // null hides it
140
+ export_buttons?: boolean // SVG/PNG download buttons in the controls pane
141
+ export_filename?: string
142
+ tween?: TweenOptions<{ x0: number; x1: number; y0: number; n_rings: number }>
143
+ value_format?: string
144
+ padding?: Sides
145
+ legend?: LegendConfig | null
146
+ show_legend?: boolean
147
+ tooltip?: Snippet<[SunburstNodeHandlerProps<Metadata>]>
148
+ // Fully replace the default arc path. NOTE: this also replaces the built-in
149
+ // hover/focus/click + tooltip wiring, so re-implement any interactivity you
150
+ // need inside the snippet.
151
+ arc_content?: Snippet<
152
+ [{ arc: PositionedArc<Metadata>; a0: number; a1: number; r0: number; r1: number }]
153
+ >
154
+ center_content?: Snippet<
155
+ [{ root: PositionedArc<Metadata> | null; radius: number; zoomed: boolean }]
156
+ >
157
+ change?: (data: SunburstNodeHandlerProps<Metadata> | null) => void
158
+ on_node_click?: (
159
+ data: SunburstNodeHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
160
+ ) => void
161
+ on_node_hover?: (
162
+ data: (SunburstNodeHandlerProps<Metadata> & { event: MouseEvent | FocusEvent }) | null,
163
+ ) => void
164
+ on_zoom?: (data: { root: SunburstNodeHandlerProps<Metadata> | null }) => void
165
+ header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
166
+ controls_extra?: Snippet<[{ zoom_root_id: string | number | null }]>
167
+ } = $props()
153
168
 
154
169
  let [width, height] = $state([0, 0])
155
170
  let wrapper: HTMLDivElement | undefined = $state()
156
171
  let svg_element: SVGSVGElement | null = $state(null)
157
172
  let center_el: SVGCircleElement | null = $state(null)
173
+ // Unique per instance so multiple sunbursts on one page don't collide on
174
+ // the hatch pattern's SVG id.
175
+ const uid = $props.id()
176
+ const hatch_pattern_id = `sunburst-hatch-${uid}`
158
177
 
159
178
  let hovered_idx = $state<number | null>(null)
160
179
  let hover_info = $state<SunburstNodeHandlerProps<Metadata> | null>(null)
@@ -175,28 +194,18 @@
175
194
  )
176
195
  let inner_height = $derived(avail_height - colorbar_reserve)
177
196
 
178
- // Degrade to an empty layout (instead of crashing the host page) on invalid data.
179
197
  // Layout depends only on data/value semantics - not on size or zoom.
180
- let layout = $derived.by(() => {
181
- try {
182
- return compute_sunburst_layout(data, {
183
- value_mode,
184
- sort,
185
- level_lighten,
186
- min_fraction,
187
- other_label,
188
- })
189
- } catch (err) {
190
- console.error(err)
191
- return { arcs: [], root: null, max_depth: 0 }
192
- }
193
- })
194
- let arc_by_id = $derived(new Map(layout.arcs.map((arc) => [arc.id, arc])))
195
-
196
- // Resolve the zoom root; stale ids (e.g. after a data swap) fall back to the root
197
- let zoom_root = $derived(
198
- (zoom_root_id != null ? arc_by_id.get(zoom_root_id) : null) ?? layout.root,
198
+ let layout = $derived(
199
+ safe_hierarchy_layout(data, {
200
+ value_mode,
201
+ sort,
202
+ level_lighten,
203
+ min_fraction,
204
+ other_label,
205
+ }),
199
206
  )
207
+ // Resolve the zoom root; stale ids (e.g. after a data swap) fall back to the root
208
+ let zoom_root = $derived(layout.arcs.find((arc) => arc.id === zoom_root_id) ?? layout.root)
200
209
  let zoomed = $derived((zoom_root?.depth ?? 0) > 0)
201
210
 
202
211
  // Drop muted ids that no longer exist when data changes (untrack avoids a
@@ -204,11 +213,9 @@
204
213
  // Hover/focus state is index-based, so a layout swap would otherwise leave a stale
205
214
  // tooltip and highlight whatever unrelated node now occupies the old index.
206
215
  $effect(() => {
207
- const valid = new Set(
208
- layout.arcs.filter((arc) => arc.depth === 1).map((arc) => arc.id),
209
- )
216
+ const { arcs } = layout
210
217
  untrack(() => {
211
- for (const id of muted_ids) if (!valid.has(id)) muted_ids.delete(id)
218
+ prune_muted_ids(arcs, muted_ids)
212
219
  set_arc_hover(null)
213
220
  focused_idx = null
214
221
  })
@@ -266,10 +273,10 @@
266
273
 
267
274
  let arc_gen = $derived(
268
275
  d3_arc<ScreenArc>()
269
- .startAngle((d) => d.a0)
270
- .endAngle((d) => d.a1)
271
- .innerRadius((d) => d.r0)
272
- .outerRadius((d) => d.r1)
276
+ .startAngle((screen) => screen.a0)
277
+ .endAngle((screen) => screen.a1)
278
+ .innerRadius((screen) => screen.r0)
279
+ .outerRadius((screen) => screen.r1)
273
280
  .padAngle(pad_angle * DEG_TO_RAD)
274
281
  .padRadius(radius || 1),
275
282
  )
@@ -278,7 +285,7 @@
278
285
  const screen_path = (screen: ScreenArc): string =>
279
286
  shape === `icicle`
280
287
  ? `M${screen.a0},${screen.r0}H${screen.a1}V${screen.r1}H${screen.a0}Z`
281
- : arc_gen(screen) ?? ``
288
+ : (arc_gen(screen) ?? ``)
282
289
 
283
290
  // The chart group's transform: sunburst draws around the center, icicle from the
284
291
  // top-left of the padded plot area
@@ -287,35 +294,20 @@
287
294
  )
288
295
 
289
296
  // Arc centroid in container (pad-offset) pixel space, for tooltip + legend placement
290
- const arc_center = (d: ScreenArc): { x: number; y: number } => {
297
+ const arc_center = (screen: ScreenArc): { x: number; y: number } => {
291
298
  if (shape === `icicle`) {
292
- return { x: pad.l + (d.a0 + d.a1) / 2, y: pad.t + (d.r0 + d.r1) / 2 }
299
+ return { x: pad.l + (screen.a0 + screen.a1) / 2, y: pad.t + (screen.r0 + screen.r1) / 2 }
293
300
  }
294
- const mid_a = (d.a0 + d.a1) / 2
295
- const mid_r = (d.r0 + d.r1) / 2
301
+ const mid_a = (screen.a0 + screen.a1) / 2
302
+ const mid_r = (screen.r0 + screen.r1) / 2
296
303
  return { x: cx + Math.sin(mid_a) * mid_r, y: cy - Math.cos(mid_a) * mid_r }
297
304
  }
298
305
 
299
306
  // Continuous metric coloring: when color_values is given, arcs are colored by their
300
- // metric on a d3 colormap (arcs returning null keep their categorical color).
301
- // The user accessor runs exactly once per arc.
302
- let metric = $derived.by<{ range: [number, number]; colors: string[] } | null>(() => {
303
- if (!color_values) return null
304
- const vals = layout.arcs.map((arc) => {
305
- const val = arc.depth === 0 ? null : color_values(arc)
306
- return val != null && Number.isFinite(val) ? val : null
307
- })
308
- const finite = vals.filter((val) => val != null)
309
- if (finite.length === 0) return null
310
- const range = color_range ?? [Math.min(...finite), Math.max(...finite)]
311
- const scale = create_color_scale({ scheme: color_scale, value_range: range }, range)
312
- return {
313
- range,
314
- colors: vals.map((val, idx) =>
315
- val == null ? layout.arcs[idx].color : `${scale(val)}`
316
- ),
317
- }
318
- })
307
+ // metric on a d3 colormap (arcs returning null keep their categorical color)
308
+ let metric = $derived(
309
+ compute_metric_colors(layout.arcs, color_values, color_scale, color_range),
310
+ )
319
311
  const arc_color = (arc: PositionedArc<Metadata>): string =>
320
312
  metric?.colors[arc.node_idx] ?? arc.color
321
313
  // release the colorbar's reserved chart space when it's not rendered
@@ -323,83 +315,30 @@
323
315
  if (!metric || colorbar == null) colorbar_height = 0
324
316
  })
325
317
 
326
- // Predicate keeping the hovered arc + its ancestors/descendants fully opaque.
327
- // Pre-order indexing makes both tests O(1): a subtree is the contiguous index
328
- // range [node_idx, subtree_end].
329
- let active = $derived.by(() => {
330
- if (hovered_idx == null) return null
331
- const hov = layout.arcs[hovered_idx]
332
- if (!hov) return null
333
- return (arc: PositionedArc<Metadata>): boolean =>
334
- (arc.node_idx >= hov.node_idx && arc.node_idx <= hov.subtree_end) ||
335
- (hov.node_idx >= arc.node_idx && hov.node_idx <= arc.subtree_end)
336
- })
337
-
338
- const is_muted = (arc: PositionedArc<Metadata>): boolean =>
339
- arc.path.length > 0 && muted_ids.has(arc.path[0])
340
-
341
- const MUTED_OPACITY = 0.12
342
- const arc_opacity = (arc: PositionedArc<Metadata>): number => {
343
- if (is_muted(arc)) return MUTED_OPACITY
344
- if (active && !active(arc)) return 0.3
345
- return 1
346
- }
318
+ // Hovered arc + its ancestors/descendants stay fully opaque, others dim
319
+ let arc_dim = $derived(compute_node_dim(layout.arcs, muted_ids, hovered_idx))
347
320
 
348
321
  // Black/white label text, whichever contrasts with the arc's fill (light arcs from
349
- // explicit colors or level_lighten would hide white labels, esp. when highlighted).
350
- // Memoized per color string - parsing/luminance would otherwise run per label per
351
- // animation frame, and distinct arc colors are few.
352
- const contrast_cache = new Map<string, string>()
353
- const label_color = (arc: PositionedArc<Metadata>): string => {
354
- const fill = arc_color(arc)
355
- let contrast = contrast_cache.get(fill)
356
- if (contrast === undefined) {
357
- contrast = pick_contrast_color({ bg_color: fill })
358
- contrast_cache.set(fill, contrast)
359
- }
360
- return contrast
361
- }
322
+ // explicit colors or level_lighten would hide white labels, esp. when highlighted)
323
+ const contrast_for = make_cached_contrast()
362
324
 
363
- // Parent arc of an arc (null for the root) and its display name
364
- const parent_of = (arc: PositionedArc<Metadata>): PositionedArc<Metadata> | null =>
365
- arc.parent_idx != null ? layout.arcs[arc.parent_idx] : null
366
- const arc_name = (arc: PositionedArc<Metadata>): string => arc.label ?? `${arc.id}`
325
+ const make_node_props = (arc: PositionedArc<Metadata>): SunburstNodeHandlerProps<Metadata> =>
326
+ node_handler_props(layout.arcs, arc, arc_color(arc))
367
327
 
368
- function make_node_props(
369
- arc: PositionedArc<Metadata>,
370
- ): SunburstNodeHandlerProps<Metadata> {
371
- // Handler props are the arc minus its screen geometry, plus the parent id
372
- const { x0, x1, y0, y1, subtree_end, parent_idx, ...node } = arc
373
- return {
374
- ...node,
375
- type: `node`,
376
- color: arc_color(arc),
377
- parent_id: parent_of(arc)?.id ?? null,
378
- }
379
- }
380
-
381
- // Anchor the tooltip at the cursor (mouse hover) so it follows the pointer across
382
- // wide arcs; fall back to the arc centroid on keyboard focus (no cursor).
383
- const event_pos = (event?: MouseEvent | FocusEvent): { x: number; y: number } | null =>
384
- event instanceof MouseEvent ? get_relative_coords(event, svg_element) : null
385
-
386
- function set_arc_hover(
387
- screen: ScreenArc | null,
388
- event?: MouseEvent | FocusEvent,
389
- ) {
328
+ function set_arc_hover(screen: ScreenArc | null, event?: MouseEvent | FocusEvent) {
390
329
  // Same arc as before: only the cursor anchor moves - skip rebuilding the handler
391
330
  // payload and re-firing change/on_node_hover on every mousemove within an arc.
392
331
  // Requires hover_info: legend item hover sets hovered_idx alone (for dimming), and
393
332
  // skipping then would leave the arc's own tooltip permanently suppressed.
394
333
  if (screen && screen.arc.node_idx === hovered_idx && hover_info) {
395
- hover_pos = event_pos(event) ?? hover_pos
334
+ hover_pos = pointer_pos(event, svg_element) ?? hover_pos
396
335
  return
397
336
  }
398
337
  if (screen) {
399
338
  hovered = true
400
339
  hovered_idx = screen.arc.node_idx
401
340
  hover_info = make_node_props(screen.arc)
402
- hover_pos = event_pos(event) ?? arc_center(screen)
341
+ hover_pos = pointer_pos(event, svg_element) ?? arc_center(screen)
403
342
  change(hover_info)
404
343
  if (event) on_node_hover?.({ ...hover_info, event })
405
344
  } else {
@@ -416,7 +355,7 @@
416
355
 
417
356
  const screen_arc_from_event = (event: Event): ScreenArc | null => {
418
357
  const idx = closest_data_idx(event, `data-sunburst-node-idx`, svg_element)
419
- return idx == null ? null : screen_arcs[idx] ?? null
358
+ return idx == null ? null : (screen_arcs[idx] ?? null)
420
359
  }
421
360
 
422
361
  function handle_arc_hover_event(event: MouseEvent | FocusEvent) {
@@ -428,25 +367,15 @@
428
367
 
429
368
  // Re-root the view on the given arc (or the data root when null) and notify
430
369
  function zoom_to(arc: PositionedArc<Metadata> | null) {
431
- zoom_root_id = arc && arc.depth > 0 ? arc.id : null
370
+ const root = arc && arc.depth > 0 ? arc : null
371
+ zoom_root_id = root?.id ?? null
432
372
  // The clicked arc collapses into the hole - drop the now-stale hover/tooltip
433
373
  set_arc_hover(null)
434
- on_zoom?.({ root: arc && arc.depth > 0 ? make_node_props(arc) : null })
435
- }
436
-
437
- // True while the user has an uncollapsed text selection inside this chart. Labels
438
- // are selectable text, and the mouseup that ends a selection drag also fires a
439
- // click - selecting a label must not zoom or fire on_node_click.
440
- function selection_in_chart(): boolean {
441
- const selection = globalThis.getSelection?.()
442
- return Boolean(
443
- selection && !selection.isCollapsed && selection.anchorNode &&
444
- wrapper?.contains(selection.anchorNode),
445
- )
374
+ on_zoom?.({ root: root && make_node_props(root) })
446
375
  }
447
376
 
448
377
  function handle_arc_click(event: MouseEvent | KeyboardEvent) {
449
- if (event instanceof MouseEvent && selection_in_chart()) return
378
+ if (event instanceof MouseEvent && selection_within(wrapper)) return
450
379
  const screen = screen_arc_from_event(event)
451
380
  if (!screen) return
452
381
  const { arc } = screen
@@ -455,7 +384,7 @@
455
384
  }
456
385
 
457
386
  function zoom_out(event?: Event) {
458
- if (event instanceof MouseEvent && selection_in_chart()) return
387
+ if (event instanceof MouseEvent && selection_within(wrapper)) return
459
388
  if (!zoomed) return
460
389
  zoom_to(breadcrumb_arcs.at(-2) ?? null)
461
390
  }
@@ -465,7 +394,7 @@
465
394
  // the center zoom-out button already fired its own click action twice - compounding
466
395
  // a third full reset would teleport step-by-step zoom-outs straight to the root)
467
396
  function handle_dblclick(event: MouseEvent) {
468
- if (screen_arc_from_event(event) || selection_in_chart()) return
397
+ if (screen_arc_from_event(event) || selection_within(wrapper)) return
469
398
  const target = event.target as Element | null
470
399
  if (target?.closest?.(`.center-circle, .center-label`)) return
471
400
  if (zoomed) zoom_to(null)
@@ -493,8 +422,6 @@
493
422
  )
494
423
  }
495
424
 
496
- const is_activation_key = (evt: KeyboardEvent) => [`Enter`, ` `].includes(evt.key)
497
-
498
425
  function handle_arc_keydown(event: KeyboardEvent) {
499
426
  const nav_target = nav_target_from_event(event)
500
427
  if (nav_target != null) {
@@ -528,57 +455,48 @@
528
455
  const arc_clickable = (arc: PositionedArc<Metadata>): boolean =>
529
456
  Boolean(on_node_click) || (zoom_on_click && !arc.is_leaf)
530
457
 
531
- // Measure label fit in the font labels actually render in (respects the
532
- // --sunburst-font-size CSS var instead of assuming 11px). Memoized because canvas
533
- // measureText is far too slow to run for every visible arc on every tween frame.
534
- let label_font = $derived.by(() => {
535
- if (!svg_element) return `11px sans-serif`
536
- const { fontSize, fontFamily } = getComputedStyle(svg_element)
537
- return `${fontSize} ${fontFamily}`
538
- })
539
- const text_width_cache = new Map<string, number>()
540
- function cached_text_width(text: string, font: string): number {
541
- const key = `${font}|${text}`
542
- let text_width = text_width_cache.get(key)
543
- if (text_width === undefined) {
544
- if (text_width_cache.size > 10_000) text_width_cache.clear() // growth guard
545
- text_width = measure_text_width(text, font)
546
- text_width_cache.set(key, text_width)
547
- }
548
- return text_width
549
- }
550
-
551
- // What an arc's label displays, per the label_text mode (plotly textinfo equivalent)
552
- const arc_label_str = (arc: PositionedArc<Metadata>): string => {
553
- const name = arc_name(arc)
554
- if (label_text === `label`) return name
555
- const val = format_value(arc.value, value_format)
556
- if (label_text === `value`) return val
557
- const pct = format_value(arc.fraction, `.1%`)
558
- if (label_text === `percent`) return pct
559
- return label_text === `label+value` ? `${name} ${val}` : `${name} ${pct}`
560
- }
458
+ let label_font = $derived(svg_label_font(svg_element))
459
+ const cached_text_width = make_cached_text_width()
561
460
 
562
- // Per-arc label text, measured width and aria string - all view-independent, so
563
- // computed once per layout/label-option change instead of per animation frame
564
- // (format_value + canvas measureText would otherwise run per visible arc per frame)
461
+ // Per-arc label text, measured width, fill/label colors, clickability and aria
462
+ // string - all view-independent, so computed once per layout/option change
463
+ // instead of per animation frame
565
464
  let arc_info = $derived(
566
- layout.arcs.map((arc) => {
567
- const text = arc_label_str(arc)
568
- return {
569
- text,
570
- width: cached_text_width(text, label_font),
571
- aria: `${arc_name(arc)}: ${arc.value}`,
572
- }
465
+ compute_node_infos(layout.arcs, {
466
+ label_text,
467
+ value_format,
468
+ label_font,
469
+ color_for: arc_color,
470
+ text_width: cached_text_width,
471
+ contrast: contrast_for,
472
+ clickable: arc_clickable,
573
473
  }),
574
474
  )
575
475
 
576
- // Label text + placement transform for an arc; null = doesn't fit, hide the label
577
- function label_attrs(d: ScreenArc): { transform: string; text: string } | null {
578
- const { text, width: text_w } = arc_info[d.arc.node_idx]
579
- if (!text) return null
580
- const transform = arc_label_transform(d, text_w, shape, label_rotation)
581
- return transform ? { transform, text } : null
476
+ // Downscale steps tried when no label variant fits at full size: narrow
477
+ // slices keep a (smaller) label instead of losing it entirely.
478
+ const LABEL_FONT_SCALES = [1, 0.85, 0.7]
479
+
480
+ // Label text + placement transform for an arc; null = no variant fits, hide
481
+ // the label. Full font size wins over richer text: within each scale the
482
+ // richest variant that fits is used (extended -> label -> label_short).
483
+ function label_attrs(
484
+ screen: ScreenArc,
485
+ ): { transform: string; text: string; font_scale: number } | null {
486
+ for (const font_scale of LABEL_FONT_SCALES) {
487
+ for (const { text, width: text_w } of arc_info[screen.arc.node_idx].variants) {
488
+ const transform = arc_label_transform(
489
+ screen,
490
+ text_w * font_scale,
491
+ shape,
492
+ label_rotation,
493
+ radius,
494
+ font_scale,
495
+ )
496
+ if (transform) return { transform, text, font_scale }
497
+ }
498
+ }
499
+ return null
582
500
  }
583
501
 
584
502
  // Legend: one item per depth-1 category, toggling mutes (dims) rather than removes.
@@ -600,21 +518,12 @@
600
518
  points: settled.map(arc_center),
601
519
  })
602
520
  })
603
- let legend_data = $derived.by<LegendItem[]>(() =>
604
- depth1_arcs.map((arc, idx) => ({
605
- series_idx: idx,
606
- label: arc_name(arc),
607
- visible: !muted_ids.has(arc.id),
608
- display_style: { symbol_type: `Square` as const, symbol_color: arc_color(arc) },
609
- }))
521
+ let legend_data: LegendItem[] = $derived(
522
+ hierarchy_legend_items(depth1_arcs, muted_ids, arc_color),
610
523
  )
611
524
 
612
- function toggle_category(series_idx: number) {
613
- const id = depth1_arcs[series_idx]?.id
614
- if (id === undefined) return
615
- if (muted_ids.has(id)) muted_ids.delete(id)
616
- else muted_ids.add(id)
617
- }
525
+ const toggle_category = (series_idx: number) =>
526
+ toggle_muted(muted_ids, depth1_arcs[series_idx]?.id)
618
527
 
619
528
  $effect(() => set_fullscreen_bg(wrapper, fullscreen, `--sunburst-fullscreen-bg`))
620
529
 
@@ -623,62 +532,24 @@
623
532
  let zoom_out_label = $derived.by(() => {
624
533
  const parent = breadcrumb_arcs.at(-2)
625
534
  if (!parent) return ``
626
- return parent.depth === 0 ? `full chart` : arc_name(parent)
627
- })
628
-
629
- // Ancestor chain from the root to the current zoom root (clickable breadcrumb trail)
630
- let breadcrumb_arcs = $derived.by(() => {
631
- if (!zoom_root || zoom_root.depth === 0) return []
632
- const chain: PositionedArc<Metadata>[] = []
633
- for (
634
- let cur: PositionedArc<Metadata> | null = zoom_root;
635
- cur;
636
- cur = parent_of(cur)
637
- ) chain.unshift(cur)
638
- return chain
535
+ return parent.depth === 0 ? `full chart` : node_display_name(parent)
639
536
  })
640
537
 
641
- // Styles the component applies via CSS that exported standalone SVGs must carry
642
- // as presentation attributes (inlined onto a clone by the io/export helpers)
643
- const export_inline_styles = [
644
- `fill`,
645
- `stroke`,
646
- `stroke-width`,
647
- `text-anchor`,
648
- `dominant-baseline`,
649
- `font-size`,
650
- `font-family`,
651
- `font-weight`,
652
- `opacity`,
653
- ]
538
+ let breadcrumb_arcs = $derived(ancestor_chain(layout.arcs, zoom_root))
654
539
 
655
- function export_chart(format: `svg` | `png`) {
656
- if (!svg_element) return
657
- if (format === `svg`) {
658
- export_svg_as_svg(svg_element, `${export_filename}.svg`, export_inline_styles)
659
- } else {
660
- export_svg_as_png(svg_element, `${export_filename}.png`, 150, export_inline_styles)
661
- }
662
- }
540
+ const export_chart = (format: `svg` | `png`) =>
541
+ export_hierarchy_chart(svg_element, export_filename, format)
663
542
  </script>
664
543
 
665
544
  <svelte:window
666
- onkeydown={(evt) => {
667
- if (evt.key !== `Escape`) return
668
- // only react when the user is interacting with this chart (pointer over it,
669
- // focus inside it, or fullscreen) - Escape zooms out one level, then exits
670
- // fullscreen once at the root
671
- const within = fullscreen || Boolean(wrapper?.matches(`:hover`)) ||
672
- Boolean(wrapper && document.activeElement && wrapper.contains(document.activeElement))
673
- if (!within) return
674
- if (zoomed) {
675
- evt.preventDefault()
676
- zoom_out()
677
- } else if (fullscreen) {
678
- evt.preventDefault()
679
- fullscreen = false
680
- }
681
- }}
545
+ onkeydown={(evt) =>
546
+ handle_hierarchy_escape(evt, {
547
+ wrapper,
548
+ fullscreen,
549
+ zoomed,
550
+ zoom_out: () => zoom_out(),
551
+ exit_fullscreen: () => (fullscreen = false),
552
+ })}
682
553
  />
683
554
 
684
555
  <div
@@ -686,7 +557,7 @@
686
557
  bind:clientWidth={width}
687
558
  bind:clientHeight={height}
688
559
  {...rest}
689
- class="sunburst {rest.class ?? ``}"
560
+ class={[`sunburst`, rest.class]}
690
561
  class:fullscreen
691
562
  class:icicle={shape === `icicle`}
692
563
  >
@@ -695,6 +566,7 @@
695
566
  {@render header_controls?.({ height, width, fullscreen })}
696
567
  {#if show_controls}
697
568
  <SunburstControls
569
+ chart="sunburst"
698
570
  toggle_props={{
699
571
  ...controls_toggle_props,
700
572
  // join the header flex row instead of absolute positioning (overrides
@@ -735,7 +607,7 @@
735
607
  disabled={crumb_idx === breadcrumb_arcs.length - 1}
736
608
  onclick={() => zoom_to(crumb)}
737
609
  >
738
- {crumb.depth === 0 ? `all` : crumb.label ?? crumb.id}
610
+ {crumb.depth === 0 ? `all` : (crumb.label ?? crumb.id)}
739
611
  </button>
740
612
  {/each}
741
613
  </nav>
@@ -748,6 +620,12 @@
748
620
  aria-label={rest[`aria-label`] ?? `${shape === `icicle` ? `Icicle` : `Sunburst`} chart`}
749
621
  onmouseleave={() => set_arc_hover(null)}
750
622
  >
623
+ <!-- inert unless some arc references it via fill -->
624
+ <defs>
625
+ <pattern id={hatch_pattern_id} patternUnits="userSpaceOnUse" width="8" height="8">
626
+ <path class="hatch-pattern-line" d="M-1,1 l2,-2 M0,8 l8,-8 M7,9 l2,-2" />
627
+ </pattern>
628
+ </defs>
751
629
  <!-- Hover/click delegation sits on the chart group (not the arcs group) so
752
630
  labels - which carry the same data-sunburst-node-idx and are selectable text -
753
631
  forward interactions to their arc instead of swallowing them -->
@@ -768,20 +646,37 @@
768
646
  {#if arc_content}
769
647
  {@render arc_content(screen)}
770
648
  {:else}
771
- {@const clickable = arc_clickable(screen.arc)}
649
+ <!-- @const so the path is generated (and info looked up) once per
650
+ arc per frame, shared by the base path and the hatch overlay -->
651
+ {@const info = arc_info[screen.arc.node_idx]}
652
+ {@const opacity = arc_dim[screen.arc.node_idx].opacity}
653
+ {@const path_d = screen_path(screen)}
772
654
  <!-- svelte-ignore a11y_no_static_element_interactions -->
773
655
  <path
774
- d={screen_path(screen)}
656
+ d={path_d}
775
657
  data-sunburst-node-idx={screen.arc.node_idx}
776
- fill={arc_color(screen.arc)}
777
- fill-opacity={arc_opacity(screen.arc)}
778
- role={clickable ? `button` : undefined}
779
- tabindex={clickable
780
- ? (screen.arc.node_idx === roving_idx ? 0 : -1)
658
+ fill={info.fill}
659
+ fill-opacity={opacity}
660
+ role={info.clickable ? `button` : undefined}
661
+ tabindex={info.clickable
662
+ ? screen.arc.node_idx === roving_idx
663
+ ? 0
664
+ : -1
781
665
  : undefined}
782
- aria-label={clickable ? arc_info[screen.arc.node_idx].aria : undefined}
783
- style:cursor={clickable ? `pointer` : `default`}
666
+ aria-label={info.clickable ? info.aria : undefined}
667
+ style:cursor={info.clickable ? `pointer` : `default`}
784
668
  />
669
+ {#if screen.arc.hatch}
670
+ <!-- Decorative texture overlay (e.g. preemptible jobs); rendered as
671
+ the base path's next sibling so the hover 'pull' can track it -->
672
+ <path
673
+ class="arc-hatch"
674
+ aria-hidden="true"
675
+ d={path_d}
676
+ fill="url(#{hatch_pattern_id})"
677
+ fill-opacity={opacity}
678
+ />
679
+ {/if}
785
680
  {/if}
786
681
  {/each}
787
682
  </g>
@@ -793,13 +688,15 @@
793
688
  {#each visible_arcs as screen (screen.arc.node_idx)}
794
689
  {@const lbl = label_attrs(screen)}
795
690
  {#if lbl}
691
+ {@const info = arc_info[screen.arc.node_idx]}
796
692
  <text
797
693
  class="arc-label"
798
694
  data-sunburst-node-idx={screen.arc.node_idx}
799
695
  transform={lbl.transform}
800
- fill={label_color(screen.arc)}
801
- fill-opacity={is_muted(screen.arc) ? MUTED_OPACITY : undefined}
802
- style:cursor={arc_clickable(screen.arc) ? `pointer` : `text`}
696
+ fill={info.label_fill}
697
+ fill-opacity={arc_dim[screen.arc.node_idx].label_opacity}
698
+ style:cursor={info.clickable ? `pointer` : `text`}
699
+ style:font-size={lbl.font_scale === 1 ? undefined : `${lbl.font_scale}em`}
803
700
  >
804
701
  {lbl.text}
805
702
  </text>
@@ -861,14 +758,13 @@
861
758
  {#if tooltip}
862
759
  {@render tooltip(hover_info)}
863
760
  {:else}
864
- <strong>{hover_info.label_path.join(` › `)}</strong>: {
865
- format_value(hover_info.value, value_format)
866
- }
867
- ({format_value(hover_info.fraction, `.1%`)} of total{
868
- hover_info.depth > 1
761
+ <strong>{hover_info.label_path.join(` › `)}</strong>: {format_value(
762
+ hover_info.value,
763
+ value_format,
764
+ )}
765
+ ({format_value(hover_info.fraction, `.1%`)} of total{hover_info.depth > 1
869
766
  ? `, ${format_value(hover_info.parent_fraction, `.1%`)} of parent`
870
- : ``
871
- })
767
+ : ``})
872
768
  {/if}
873
769
  </PlotTooltip>
874
770
  {/if}
@@ -882,9 +778,10 @@
882
778
  series_data={legend_data}
883
779
  on_toggle={legend?.on_toggle ?? toggle_category}
884
780
  on_item_hover={(item) =>
885
- (hovered_idx = item != null && item.series_idx >= 0
886
- ? depth1_arcs[item.series_idx]?.node_idx ?? null
887
- : null)}
781
+ (hovered_idx =
782
+ item != null && item.series_idx >= 0
783
+ ? (depth1_arcs[item.series_idx]?.node_idx ?? null)
784
+ : null)}
888
785
  style={`position: absolute; left: ${legend_left}px; top: ${legend_top}px; pointer-events: auto; ${
889
786
  legend?.style ?? ``
890
787
  }`}
@@ -894,10 +791,10 @@
894
791
  {#if metric && colorbar != null}
895
792
  <div
896
793
  bind:clientHeight={colorbar_height}
897
- style="position: absolute; bottom: var(--sunburst-colorbar-bottom, 8px); left: 50%; transform: translateX(-50%); width: var(--sunburst-colorbar-width, 40%); min-width: 120px; pointer-events: auto;"
794
+ style="position: absolute; bottom: var(--sunburst-colorbar-bottom, 8px); left: var(--sunburst-colorbar-left, 50%); transform: var(--sunburst-colorbar-transform, translateX(-50%)); width: var(--sunburst-colorbar-width, 40%); min-width: 120px; pointer-events: auto;"
898
795
  >
899
796
  <ColorBar
900
- color_scale={color_scale}
797
+ {color_scale}
901
798
  range={metric.range}
902
799
  {...colorbar}
903
800
  wrapper_style={`width: 100%; ${colorbar?.wrapper_style ?? ``}`}
@@ -990,7 +887,9 @@
990
887
  .sunburst :global(.pane-toggle),
991
888
  .sunburst .header-controls {
992
889
  opacity: 0;
993
- transition: opacity 0.2s, background-color 0.2s;
890
+ transition:
891
+ opacity 0.2s,
892
+ background-color 0.2s;
994
893
  }
995
894
  .sunburst:hover :global(.pane-toggle),
996
895
  .sunburst:hover .header-controls,
@@ -1013,13 +912,32 @@
1013
912
  presentation attributes is not reliably supported across browsers */
1014
913
  stroke: var(--sunburst-arc-stroke, var(--plot-bg, white));
1015
914
  stroke-width: var(--sunburst-arc-stroke-width, 0.25);
1016
- transition: fill-opacity 0.15s ease, transform 0.15s ease;
915
+ transition:
916
+ fill-opacity 0.15s ease,
917
+ transform 0.15s ease;
1017
918
  /* hover 'pull': scaling about the chart center offsets the arc radially */
1018
919
  transform-origin: 0 0;
1019
920
  }
1020
- .sunburst:not(.icicle) .arcs path:hover {
921
+ /* the hatch overlay (an arc's next sibling) rides along with its hover 'pull' */
922
+ .sunburst:not(.icicle) .arcs path:hover,
923
+ .sunburst:not(.icicle) .arcs path:hover + path.arc-hatch {
1021
924
  transform: scale(var(--sunburst-hover-scale, 1.02));
1022
925
  }
926
+ /* decorative overlay: never intercepts pointer events, no border of its own */
927
+ .arcs path.arc-hatch {
928
+ stroke: none;
929
+ pointer-events: none;
930
+ }
931
+ /* subtle by default: thin stripes inheriting the arc border color (itself
932
+ defaulting to the chart bg) at low opacity, so hatching matches the gaps
933
+ between slices instead of reading as solid white */
934
+ .hatch-pattern-line {
935
+ stroke: var(
936
+ --sunburst-hatch-stroke,
937
+ color-mix(in srgb, var(--sunburst-arc-stroke, var(--plot-bg, white)) 30%, transparent)
938
+ );
939
+ stroke-width: var(--sunburst-hatch-stroke-width, 0.35);
940
+ }
1023
941
  .center-circle {
1024
942
  fill: var(--sunburst-center-bg, transparent);
1025
943
  }