matterviz 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (499) hide show
  1. package/dist/EmptyState.svelte +10 -4
  2. package/dist/FilePicker.svelte +48 -60
  3. package/dist/Icon.svelte +7 -4
  4. package/dist/MillerIndexInput.svelte +2 -2
  5. package/dist/api/optimade.js +10 -7
  6. package/dist/app.css +68 -0
  7. package/dist/brillouin/BrillouinZone.svelte +90 -91
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
  9. package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
  11. package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
  12. package/dist/brillouin/BrillouinZoneScene.svelte +46 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  14. package/dist/brillouin/compute.js +10 -16
  15. package/dist/brillouin/geometry.js +2 -2
  16. package/dist/chempot-diagram/ChemPotDiagram.svelte +48 -50
  17. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +61 -169
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +559 -755
  20. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
  21. package/dist/chempot-diagram/async-compute.svelte.js +13 -14
  22. package/dist/chempot-diagram/color.d.ts +21 -1
  23. package/dist/chempot-diagram/color.js +70 -1
  24. package/dist/chempot-diagram/compute.d.ts +7 -0
  25. package/dist/chempot-diagram/compute.js +85 -26
  26. package/dist/chempot-diagram/controls-state.svelte.js +1 -1
  27. package/dist/chempot-diagram/export.js +1 -6
  28. package/dist/chempot-diagram/temperature.js +9 -10
  29. package/dist/colors/index.d.ts +0 -4
  30. package/dist/colors/index.js +19 -20
  31. package/dist/composition/BarChart.svelte +30 -26
  32. package/dist/composition/BubbleChart.svelte +17 -17
  33. package/dist/composition/Composition.svelte +10 -19
  34. package/dist/composition/Formula.svelte +20 -27
  35. package/dist/composition/FormulaFilter.svelte +115 -184
  36. package/dist/composition/PieChart.svelte +55 -41
  37. package/dist/composition/format.js +7 -9
  38. package/dist/composition/index.d.ts +2 -0
  39. package/dist/composition/index.js +14 -0
  40. package/dist/composition/parse.d.ts +2 -2
  41. package/dist/composition/parse.js +61 -115
  42. package/dist/constants.d.ts +0 -1
  43. package/dist/constants.js +17 -26
  44. package/dist/convex-hull/ConvexHull.svelte +12 -15
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +93 -94
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +127 -186
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +131 -180
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
  53. package/dist/convex-hull/ConvexHullControls.svelte +46 -48
  54. package/dist/convex-hull/ConvexHullStats.svelte +193 -195
  55. package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
  56. package/dist/convex-hull/GasPressureControls.svelte +5 -12
  57. package/dist/convex-hull/StructurePopup.svelte +18 -22
  58. package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
  59. package/dist/convex-hull/TemperatureSlider.svelte +4 -10
  60. package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
  61. package/dist/convex-hull/gas-thermodynamics.js +21 -53
  62. package/dist/convex-hull/helpers.d.ts +8 -10
  63. package/dist/convex-hull/helpers.js +37 -43
  64. package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
  65. package/dist/convex-hull/thermodynamics.js +37 -44
  66. package/dist/coordination/CoordinationBarPlot.svelte +49 -53
  67. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  68. package/dist/coordination/calc-coordination.d.ts +1 -1
  69. package/dist/coordination/calc-coordination.js +28 -25
  70. package/dist/element/BohrAtom.svelte +5 -9
  71. package/dist/element/ElementHeading.svelte +4 -1
  72. package/dist/element/ElementPhoto.svelte +11 -9
  73. package/dist/element/ElementStats.svelte +12 -10
  74. package/dist/element/ElementTile.svelte +44 -67
  75. package/dist/element/Nucleus.svelte +4 -2
  76. package/dist/element/data.d.ts +3 -2
  77. package/dist/element/data.js +5 -1
  78. package/dist/element/helpers.d.ts +4 -0
  79. package/dist/element/helpers.js +18 -0
  80. package/dist/element/index.d.ts +2 -5
  81. package/dist/element/index.js +4 -19
  82. package/dist/feedback/DragOverlay.svelte +5 -5
  83. package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
  84. package/dist/feedback/StatusMessage.svelte +7 -4
  85. package/dist/fermi-surface/FermiSlice.svelte +21 -18
  86. package/dist/fermi-surface/FermiSurface.svelte +35 -44
  87. package/dist/fermi-surface/FermiSurface.svelte.d.ts +7 -13
  88. package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
  89. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  90. package/dist/fermi-surface/FermiSurfaceScene.svelte +64 -136
  91. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  92. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
  93. package/dist/fermi-surface/compute.js +25 -58
  94. package/dist/fermi-surface/parse.js +54 -88
  95. package/dist/file-viewer/JsonBrowser.svelte +1043 -0
  96. package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
  97. package/dist/file-viewer/PlotPanel.svelte +350 -0
  98. package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
  99. package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
  100. package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
  101. package/dist/file-viewer/detect.d.ts +11 -0
  102. package/dist/file-viewer/detect.js +381 -0
  103. package/dist/file-viewer/eligibility.d.ts +4 -0
  104. package/dist/file-viewer/eligibility.js +53 -0
  105. package/dist/file-viewer/host-protocol.d.ts +50 -0
  106. package/dist/file-viewer/host-transfer.d.ts +25 -0
  107. package/dist/file-viewer/host-transfer.js +42 -0
  108. package/dist/file-viewer/index.d.ts +5 -0
  109. package/dist/file-viewer/index.js +5 -0
  110. package/dist/file-viewer/main.d.ts +44 -0
  111. package/dist/file-viewer/main.js +535 -0
  112. package/dist/file-viewer/parse.d.ts +9 -0
  113. package/dist/file-viewer/parse.js +170 -0
  114. package/dist/file-viewer/plot-utils.d.ts +24 -0
  115. package/dist/file-viewer/plot-utils.js +191 -0
  116. package/dist/file-viewer/types.d.ts +4 -0
  117. package/dist/file-viewer/types.js +5 -0
  118. package/dist/heatmap-matrix/HeatmapMatrix.svelte +153 -219
  119. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
  120. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
  121. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  122. package/dist/heatmap-matrix/index.d.ts +5 -0
  123. package/dist/icons.d.ts +4 -0
  124. package/dist/icons.js +4 -0
  125. package/dist/index.d.ts +2 -2
  126. package/dist/index.js +1 -1
  127. package/dist/io/ExportPane.svelte +11 -7
  128. package/dist/io/decompress.d.ts +4 -1
  129. package/dist/io/decompress.js +24 -36
  130. package/dist/io/export.d.ts +11 -4
  131. package/dist/io/export.js +75 -19
  132. package/dist/io/fetch.d.ts +0 -3
  133. package/dist/io/fetch.js +0 -11
  134. package/dist/io/file-drop.js +60 -35
  135. package/dist/io/index.d.ts +0 -1
  136. package/dist/io/index.js +2 -9
  137. package/dist/io/is-binary.d.ts +9 -0
  138. package/dist/io/is-binary.js +45 -1
  139. package/dist/io/url-drop.d.ts +2 -0
  140. package/dist/io/url-drop.js +40 -40
  141. package/dist/isosurface/Isosurface.svelte +700 -215
  142. package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
  143. package/dist/isosurface/IsosurfaceControls.svelte +590 -116
  144. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
  145. package/dist/isosurface/VolumeSlice.svelte +224 -0
  146. package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
  147. package/dist/isosurface/coloring.d.ts +20 -0
  148. package/dist/isosurface/coloring.js +118 -0
  149. package/dist/isosurface/geometry-worker-types.d.ts +38 -0
  150. package/dist/isosurface/geometry.worker.d.ts +1 -0
  151. package/dist/isosurface/geometry.worker.js +59 -0
  152. package/dist/isosurface/grid.d.ts +10 -0
  153. package/dist/isosurface/grid.js +31 -0
  154. package/dist/isosurface/index.d.ts +4 -0
  155. package/dist/isosurface/index.js +4 -0
  156. package/dist/isosurface/parse-vaspwave.d.ts +3 -0
  157. package/dist/isosurface/parse-vaspwave.js +138 -0
  158. package/dist/isosurface/parse.js +6 -6
  159. package/dist/isosurface/profile.d.ts +12 -0
  160. package/dist/isosurface/profile.js +19 -0
  161. package/dist/isosurface/sampling.d.ts +34 -0
  162. package/dist/isosurface/sampling.js +408 -0
  163. package/dist/isosurface/slice-rendering.d.ts +13 -0
  164. package/dist/isosurface/slice-rendering.js +79 -0
  165. package/dist/isosurface/slice.d.ts +22 -2
  166. package/dist/isosurface/slice.js +200 -116
  167. package/dist/isosurface/types.d.ts +24 -1
  168. package/dist/isosurface/types.js +120 -8
  169. package/dist/json-path.d.ts +4 -0
  170. package/dist/json-path.js +89 -0
  171. package/dist/labels.d.ts +6 -2
  172. package/dist/labels.js +44 -14
  173. package/dist/layout/FullscreenButton.svelte +2 -1
  174. package/dist/layout/FullscreenToggle.svelte +10 -4
  175. package/dist/layout/InfoCard.svelte +8 -21
  176. package/dist/layout/InfoTag.svelte +1 -3
  177. package/dist/layout/NumberRangeInput.svelte +46 -0
  178. package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
  179. package/dist/layout/PropertyFilter.svelte +2 -6
  180. package/dist/layout/SettingsSection.svelte +57 -62
  181. package/dist/layout/SettingsSection.svelte.d.ts +6 -7
  182. package/dist/layout/SubpageGrid.svelte +3 -1
  183. package/dist/layout/ViewerChrome.svelte +16 -11
  184. package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
  185. package/dist/layout/fullscreen.js +3 -5
  186. package/dist/layout/fullscreen.svelte.js +1 -2
  187. package/dist/layout/index.d.ts +1 -0
  188. package/dist/layout/index.js +1 -0
  189. package/dist/layout/json-tree/JsonNode.svelte +22 -59
  190. package/dist/layout/json-tree/JsonTree.svelte +101 -172
  191. package/dist/layout/json-tree/JsonValue.svelte +9 -32
  192. package/dist/layout/json-tree/utils.d.ts +0 -3
  193. package/dist/layout/json-tree/utils.js +50 -188
  194. package/dist/marching-cubes.d.ts +8 -1
  195. package/dist/marching-cubes.js +115 -48
  196. package/dist/math.d.ts +5 -2
  197. package/dist/math.js +48 -38
  198. package/dist/overlays/ContextMenu.svelte +19 -34
  199. package/dist/overlays/CopyButton.svelte +11 -5
  200. package/dist/overlays/DragControlTab.svelte +6 -1
  201. package/dist/overlays/DraggablePane.svelte +110 -74
  202. package/dist/overlays/DraggablePane.svelte.d.ts +2 -1
  203. package/dist/overlays/GlassChip.svelte +32 -0
  204. package/dist/overlays/GlassChip.svelte.d.ts +8 -0
  205. package/dist/overlays/InfoPaneCards.svelte +11 -23
  206. package/dist/overlays/index.d.ts +7 -0
  207. package/dist/overlays/index.js +26 -0
  208. package/dist/overlays/portal.d.ts +2 -0
  209. package/dist/overlays/portal.js +14 -0
  210. package/dist/periodic-table/PeriodicTable.svelte +113 -145
  211. package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
  212. package/dist/periodic-table/PeriodicTableControls.svelte +37 -115
  213. package/dist/periodic-table/TableInset.svelte +9 -4
  214. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +68 -105
  215. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
  216. package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
  217. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  218. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  219. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
  220. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
  221. package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
  222. package/dist/phase-diagram/build-diagram.js +4 -9
  223. package/dist/phase-diagram/diagram-input.d.ts +1 -2
  224. package/dist/phase-diagram/parse.js +15 -15
  225. package/dist/phase-diagram/svg-to-diagram.js +28 -27
  226. package/dist/phase-diagram/utils.js +2 -3
  227. package/dist/plot/bar/BarPlot.svelte +461 -426
  228. package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
  229. package/dist/plot/bar/BarPlotControls.svelte +1 -3
  230. package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
  231. package/dist/plot/bar/SpacegroupBarPlot.svelte +46 -77
  232. package/dist/plot/bar/data.d.ts +5 -4
  233. package/dist/plot/bar/data.js +2 -1
  234. package/dist/plot/box/BoxPlot.svelte +322 -226
  235. package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
  236. package/dist/plot/box/Violin.svelte.d.ts +1 -0
  237. package/dist/plot/box/box-plot.js +1 -1
  238. package/dist/plot/core/auto-place.d.ts +1 -1
  239. package/dist/plot/core/auto-place.js +2 -2
  240. package/dist/plot/core/axis-utils.d.ts +12 -7
  241. package/dist/plot/core/axis-utils.js +48 -48
  242. package/dist/plot/core/components/AxisLabel.svelte +82 -20
  243. package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
  244. package/dist/plot/core/components/ColorBar.svelte +101 -106
  245. package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
  246. package/dist/plot/core/components/FillArea.svelte +10 -17
  247. package/dist/plot/core/components/HierarchyControls.svelte +249 -0
  248. package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
  249. package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
  250. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
  251. package/dist/plot/core/components/Line.svelte +18 -9
  252. package/dist/plot/core/components/Line.svelte.d.ts +2 -0
  253. package/dist/plot/core/components/PlotAxis.svelte +40 -17
  254. package/dist/plot/core/components/PlotControls.svelte +65 -41
  255. package/dist/plot/core/components/PlotLegend.svelte +31 -35
  256. package/dist/plot/core/components/PlotMarginals.svelte +616 -0
  257. package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
  258. package/dist/plot/core/components/PlotTooltip.svelte +1 -1
  259. package/dist/plot/core/components/PortalSelect.svelte +4 -8
  260. package/dist/plot/core/components/ReferenceLine.svelte +27 -24
  261. package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
  262. package/dist/plot/core/components/ReferencePlane.svelte +7 -8
  263. package/dist/plot/core/components/ZeroLines.svelte +4 -8
  264. package/dist/plot/core/components/ZoomRect.svelte +1 -2
  265. package/dist/plot/core/components/index.d.ts +1 -0
  266. package/dist/plot/core/components/index.js +1 -0
  267. package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
  268. package/dist/plot/core/data-cleaning-signal.js +467 -0
  269. package/dist/plot/core/data-cleaning.d.ts +2 -42
  270. package/dist/plot/core/data-cleaning.js +27 -474
  271. package/dist/plot/core/data-transform.d.ts +1 -2
  272. package/dist/plot/core/data-transform.js +9 -15
  273. package/dist/plot/core/fill-utils.d.ts +3 -1
  274. package/dist/plot/core/fill-utils.js +28 -6
  275. package/dist/plot/core/index.d.ts +1 -0
  276. package/dist/plot/core/index.js +1 -0
  277. package/dist/plot/core/interactions.js +0 -2
  278. package/dist/plot/core/layout.d.ts +15 -6
  279. package/dist/plot/core/layout.js +57 -38
  280. package/dist/plot/core/marginals.d.ts +170 -0
  281. package/dist/plot/core/marginals.js +436 -0
  282. package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
  283. package/dist/plot/core/pan-zoom.svelte.js +34 -17
  284. package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
  285. package/dist/plot/core/placed-tween.svelte.js +68 -21
  286. package/dist/plot/core/reference-line.js +1 -1
  287. package/dist/plot/core/scales.d.ts +23 -0
  288. package/dist/plot/core/scales.js +39 -13
  289. package/dist/plot/core/svg.js +0 -1
  290. package/dist/plot/core/types/fills.d.ts +101 -0
  291. package/dist/plot/core/types/fills.js +15 -0
  292. package/dist/plot/core/types/plot-3d.d.ts +87 -0
  293. package/dist/plot/core/types/plot-3d.js +4 -0
  294. package/dist/plot/core/types/reference-lines.d.ts +140 -0
  295. package/dist/plot/core/types/reference-lines.js +9 -0
  296. package/dist/plot/core/types.d.ts +29 -324
  297. package/dist/plot/core/types.js +23 -20
  298. package/dist/plot/core/utils/hierarchy-chart.d.ts +52 -0
  299. package/dist/plot/core/utils/hierarchy-chart.js +191 -0
  300. package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
  301. package/dist/plot/core/utils/hierarchy-labels.js +100 -0
  302. package/dist/plot/core/utils/label-placement.js +2 -2
  303. package/dist/plot/histogram/Histogram.svelte +314 -340
  304. package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
  305. package/dist/plot/histogram/HistogramControls.svelte +30 -28
  306. package/dist/plot/histogram/histogram.d.ts +31 -0
  307. package/dist/plot/histogram/histogram.js +70 -0
  308. package/dist/plot/histogram/index.d.ts +1 -0
  309. package/dist/plot/histogram/index.js +1 -0
  310. package/dist/plot/index.d.ts +1 -0
  311. package/dist/plot/index.js +1 -0
  312. package/dist/plot/sankey/Sankey.svelte +78 -69
  313. package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
  314. package/dist/plot/sankey/SankeyControls.svelte +22 -16
  315. package/dist/plot/sankey/sankey.js +13 -9
  316. package/dist/plot/scatter/BinnedScatterPlot.svelte +311 -141
  317. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
  318. package/dist/plot/scatter/ElementScatter.svelte +4 -1
  319. package/dist/plot/scatter/ScatterPlot.svelte +517 -410
  320. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
  321. package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
  322. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  323. package/dist/plot/scatter/ScatterPoint.svelte +16 -9
  324. package/dist/plot/scatter/adaptive-density.d.ts +1 -1
  325. package/dist/plot/scatter/adaptive-density.js +36 -20
  326. package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
  327. package/dist/plot/scatter/binned-scatter-types.js +6 -1
  328. package/dist/plot/scatter/scatter-data.d.ts +1 -1
  329. package/dist/plot/scatter/scatter-data.js +8 -7
  330. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
  331. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
  332. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
  333. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
  334. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
  335. package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
  336. package/dist/plot/sunburst/Sunburst.svelte +269 -347
  337. package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
  338. package/dist/plot/sunburst/index.d.ts +1 -1
  339. package/dist/plot/sunburst/index.js +2 -1
  340. package/dist/plot/sunburst/render.d.ts +2 -2
  341. package/dist/plot/sunburst/render.js +47 -22
  342. package/dist/plot/sunburst/sunburst.d.ts +5 -1
  343. package/dist/plot/sunburst/sunburst.js +10 -3
  344. package/dist/plot/treemap/Treemap.svelte +1022 -0
  345. package/dist/plot/treemap/Treemap.svelte.d.ts +90 -0
  346. package/dist/plot/treemap/index.d.ts +4 -0
  347. package/dist/plot/treemap/index.js +4 -0
  348. package/dist/plot/treemap/labels.d.ts +38 -0
  349. package/dist/plot/treemap/labels.js +80 -0
  350. package/dist/plot/treemap/treemap.d.ts +28 -0
  351. package/dist/plot/treemap/treemap.js +102 -0
  352. package/dist/rdf/RdfPlot.svelte +37 -59
  353. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  354. package/dist/rdf/calc-rdf.js +63 -52
  355. package/dist/rdf/index.d.ts +0 -1
  356. package/dist/sanitize.js +85 -27
  357. package/dist/scene/SceneCamera.svelte +1 -1
  358. package/dist/scene/SceneCamera.svelte.d.ts +1 -1
  359. package/dist/scene/bind-renderer.svelte.js +14 -6
  360. package/dist/scene/index.d.ts +1 -2
  361. package/dist/scene/index.js +1 -1
  362. package/dist/scene/{props.js → props.svelte.js} +15 -1
  363. package/dist/settings.d.ts +19 -9
  364. package/dist/settings.js +229 -525
  365. package/dist/spectral/Bands.svelte +207 -221
  366. package/dist/spectral/BandsAndDos.svelte +11 -9
  367. package/dist/spectral/BrillouinBandsDos.svelte +19 -42
  368. package/dist/spectral/Dos.svelte +92 -93
  369. package/dist/spectral/helpers.d.ts +3 -3
  370. package/dist/spectral/helpers.js +65 -140
  371. package/dist/state.svelte.js +5 -18
  372. package/dist/structure/Arrow.svelte +5 -18
  373. package/dist/structure/ArrowInstances.svelte +160 -0
  374. package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
  375. package/dist/structure/AtomLegend.svelte +120 -85
  376. package/dist/structure/AtomLegend.svelte.d.ts +2 -3
  377. package/dist/structure/Bond.svelte +31 -23
  378. package/dist/structure/CanvasTooltip.svelte +15 -3
  379. package/dist/structure/CellSelect.svelte +45 -35
  380. package/dist/structure/Cylinder.svelte +24 -40
  381. package/dist/structure/InstancedAtoms.svelte +114 -0
  382. package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
  383. package/dist/structure/Lattice.svelte +85 -149
  384. package/dist/structure/SiteLabels.svelte +122 -0
  385. package/dist/structure/SiteLabels.svelte.d.ts +18 -0
  386. package/dist/structure/Structure.svelte +636 -504
  387. package/dist/structure/Structure.svelte.d.ts +4 -2
  388. package/dist/structure/StructureCarousel.svelte +637 -0
  389. package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
  390. package/dist/structure/StructureControls.svelte +165 -425
  391. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  392. package/dist/structure/StructureExportPane.svelte +17 -21
  393. package/dist/structure/StructureInfoPane.svelte +128 -133
  394. package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
  395. package/dist/structure/StructureScene.svelte +535 -578
  396. package/dist/structure/StructureScene.svelte.d.ts +6 -2
  397. package/dist/structure/StructureViewport.svelte +451 -0
  398. package/dist/structure/StructureViewport.svelte.d.ts +65 -0
  399. package/dist/structure/atom-label-placement.d.ts +16 -12
  400. package/dist/structure/atom-label-placement.js +70 -37
  401. package/dist/structure/atom-properties.d.ts +2 -0
  402. package/dist/structure/atom-properties.js +105 -38
  403. package/dist/structure/bond-order-perception.js +3 -8
  404. package/dist/structure/bonding.d.ts +9 -3
  405. package/dist/structure/bonding.js +95 -46
  406. package/dist/structure/edit-history.d.ts +6 -0
  407. package/dist/structure/edit-history.js +27 -0
  408. package/dist/structure/export.d.ts +1 -0
  409. package/dist/structure/export.js +89 -105
  410. package/dist/structure/format-detect.d.ts +1 -0
  411. package/dist/structure/format-detect.js +20 -0
  412. package/dist/structure/geometry.d.ts +9 -0
  413. package/dist/structure/geometry.js +27 -0
  414. package/dist/structure/index.d.ts +16 -1
  415. package/dist/structure/index.js +15 -10
  416. package/dist/structure/measure.d.ts +1 -1
  417. package/dist/structure/measure.js +6 -12
  418. package/dist/structure/parse.d.ts +2 -1
  419. package/dist/structure/parse.js +272 -313
  420. package/dist/structure/partial-occupancy.js +10 -5
  421. package/dist/structure/pbc.js +85 -107
  422. package/dist/structure/polyhedra.d.ts +0 -1
  423. package/dist/structure/polyhedra.js +7 -21
  424. package/dist/structure/prop-groups.d.ts +85 -0
  425. package/dist/structure/prop-groups.js +18 -0
  426. package/dist/structure/supercell.d.ts +1 -2
  427. package/dist/structure/supercell.js +7 -13
  428. package/dist/symmetry/SymmetryElements.svelte +11 -15
  429. package/dist/symmetry/SymmetryStats.svelte +55 -71
  430. package/dist/symmetry/WyckoffTable.svelte +10 -28
  431. package/dist/symmetry/cell-transform.js +6 -14
  432. package/dist/symmetry/index.d.ts +1 -0
  433. package/dist/symmetry/index.js +13 -10
  434. package/dist/symmetry/spacegroups.js +3 -2
  435. package/dist/table/HeatmapTable.svelte +1142 -447
  436. package/dist/table/HeatmapTable.svelte.d.ts +12 -2
  437. package/dist/table/ToggleMenu.svelte +168 -53
  438. package/dist/table/ToggleMenu.svelte.d.ts +1 -1
  439. package/dist/table/index.d.ts +21 -6
  440. package/dist/table/index.js +34 -21
  441. package/dist/theme/ThemeControl.svelte +16 -8
  442. package/dist/theme/embedded.d.ts +18 -0
  443. package/dist/theme/embedded.js +206 -0
  444. package/dist/theme/index.d.ts +5 -5
  445. package/dist/theme/index.js +5 -12
  446. package/dist/theme/themes.mjs +44 -66
  447. package/dist/tooltip/KCoords.svelte +4 -1
  448. package/dist/tooltip/TooltipContent.svelte +3 -6
  449. package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
  450. package/dist/tooltip/index.d.ts +8 -1
  451. package/dist/trajectory/Trajectory.svelte +613 -386
  452. package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
  453. package/dist/trajectory/TrajectoryError.svelte +12 -74
  454. package/dist/trajectory/TrajectoryExportPane.svelte +14 -32
  455. package/dist/trajectory/TrajectoryInfoPane.svelte +127 -164
  456. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
  457. package/dist/trajectory/extract.js +61 -49
  458. package/dist/trajectory/format-detect.d.ts +2 -0
  459. package/dist/trajectory/format-detect.js +17 -13
  460. package/dist/trajectory/frame-reader.d.ts +2 -0
  461. package/dist/trajectory/frame-reader.js +35 -32
  462. package/dist/trajectory/helpers.d.ts +2 -0
  463. package/dist/trajectory/helpers.js +34 -56
  464. package/dist/trajectory/index.d.ts +1 -0
  465. package/dist/trajectory/index.js +5 -8
  466. package/dist/trajectory/parse/ase.js +1 -1
  467. package/dist/trajectory/parse/h5-utils.d.ts +13 -0
  468. package/dist/trajectory/parse/h5-utils.js +109 -0
  469. package/dist/trajectory/parse/hdf5.d.ts +1 -1
  470. package/dist/trajectory/parse/hdf5.js +116 -106
  471. package/dist/trajectory/parse/index.d.ts +6 -6
  472. package/dist/trajectory/parse/index.js +25 -33
  473. package/dist/trajectory/parse/lammps.js +8 -12
  474. package/dist/trajectory/parse/vasp.js +10 -7
  475. package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
  476. package/dist/trajectory/parse/vaspout-electronic.js +207 -0
  477. package/dist/trajectory/parse/vaspout-h5.d.ts +4 -0
  478. package/dist/trajectory/parse/vaspout-h5.js +281 -0
  479. package/dist/trajectory/parse/xyz.js +59 -9
  480. package/dist/trajectory/plotting.d.ts +8 -9
  481. package/dist/trajectory/plotting.js +113 -64
  482. package/dist/utils.d.ts +3 -0
  483. package/dist/utils.js +7 -0
  484. package/dist/xrd/XrdPlot.svelte +79 -118
  485. package/dist/xrd/broadening.js +2 -1
  486. package/dist/xrd/calc-xrd.js +24 -62
  487. package/dist/xrd/parse.d.ts +0 -1
  488. package/dist/xrd/parse.js +69 -95
  489. package/package.json +94 -34
  490. package/readme.md +6 -5
  491. package/dist/api/mp.d.ts +0 -6
  492. package/dist/api/mp.js +0 -22
  493. package/dist/plot/sunburst/SunburstControls.svelte +0 -200
  494. package/dist/scene/types.d.ts +0 -26
  495. package/dist/tooltip/types.d.ts +0 -8
  496. package/dist/trajectory/constants.d.ts +0 -6
  497. package/dist/trajectory/constants.js +0 -11
  498. /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
  499. /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
@@ -0,0 +1,1022 @@
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
+ observe_height,
30
+ pointer_pos,
31
+ prune_muted_ids,
32
+ safe_hierarchy_layout,
33
+ selection_within,
34
+ svg_label_font,
35
+ toggle_muted,
36
+ } from '../core/utils/hierarchy-chart'
37
+ import {
38
+ export_hierarchy_chart,
39
+ make_cached_contrast,
40
+ make_cached_text_width,
41
+ } from '../core/utils/hierarchy-labels'
42
+ import { SCALE_DEFAULTS } from '../core/types'
43
+ import type { Rect, Sides } from '../core/layout'
44
+ import { arrow_nav_target } from '../sunburst/render'
45
+ import type { PositionedArc } from '../sunburst/sunburst'
46
+ import {
47
+ normalize_treemap_label_lines,
48
+ place_treemap_label,
49
+ safe_font_size,
50
+ type TreemapLabelFit,
51
+ type TreemapLabelFormatter,
52
+ type TreemapLabelLine,
53
+ type TreemapLabelPlacement,
54
+ } from './labels'
55
+ import type { TreemapNode, TreemapNodeHandlerProps } from './treemap'
56
+ import { lerp_rects, tile_rects } from './treemap'
57
+ import { DEFAULTS } from '../../settings'
58
+ import type { ComponentProps, Snippet } from 'svelte'
59
+ import { tick, untrack } from 'svelte'
60
+ import { cubicInOut } from 'svelte/easing'
61
+ import type { HTMLAttributes } from 'svelte/elements'
62
+ import { Tween, type TweenOptions } from 'svelte/motion'
63
+ import { SvelteSet } from 'svelte/reactivity'
64
+
65
+ // no outer inset by default: cells tile flush with the container (pass
66
+ // `padding` to reserve chart-edge space, e.g. for host-drawn annotations)
67
+ const DEFAULT_PADDING: Required<Sides> = { t: 0, b: 0, l: 0, r: 0 }
68
+
69
+ let {
70
+ data = $bindable([]),
71
+ value_mode = $bindable(DEFAULTS.treemap.value_mode),
72
+ // descending (unlike Sunburst's input-order default): squarified tiling
73
+ // reads best with the largest cell top-left and smallest bottom-right
74
+ sort = `descending`,
75
+ level_lighten = 0,
76
+ min_fraction = $bindable(DEFAULTS.treemap.min_fraction),
77
+ other_label = `Other`,
78
+ max_depth = $bindable(DEFAULTS.treemap.max_depth),
79
+ padding_inner = $bindable(DEFAULTS.treemap.padding_inner),
80
+ padding_top = $bindable(DEFAULTS.treemap.padding_top),
81
+ padding_outer = $bindable(DEFAULTS.treemap.padding_outer),
82
+ show_labels = $bindable(DEFAULTS.treemap.show_labels),
83
+ label_text = $bindable(DEFAULTS.treemap.label_text),
84
+ label_formatter,
85
+ label_fit = `shrink`,
86
+ label_min_font_size = 6,
87
+ label_max_font_size,
88
+ parent_label_font_size = 14,
89
+ zoom_on_click = $bindable(DEFAULTS.treemap.zoom_on_click),
90
+ zoom_root_id = $bindable(null),
91
+ show_breadcrumbs = $bindable(DEFAULTS.treemap.show_breadcrumbs),
92
+ color_values,
93
+ color_scale = SCALE_DEFAULTS.scheme,
94
+ color_range,
95
+ colorbar = {},
96
+ export_buttons = true,
97
+ export_filename = `treemap`,
98
+ tween,
99
+ value_format = `,`,
100
+ padding = DEFAULT_PADDING,
101
+ legend = {},
102
+ show_legend = false,
103
+ tooltip,
104
+ cell_content,
105
+ hovered = $bindable(false),
106
+ change = () => {},
107
+ on_node_click,
108
+ on_node_hover,
109
+ on_zoom,
110
+ show_controls = $bindable(true),
111
+ controls_open = $bindable(false),
112
+ controls_toggle_props,
113
+ controls_pane_props,
114
+ fullscreen = $bindable(false),
115
+ fullscreen_toggle = true,
116
+ children,
117
+ header_controls,
118
+ controls_extra,
119
+ ...rest
120
+ }: HTMLAttributes<HTMLDivElement> &
121
+ Omit<BasePlotProps, `change`> & {
122
+ data?: TreemapNode<Metadata> | TreemapNode<Metadata>[]
123
+ value_mode?: SunburstValueMode
124
+ sort?: SunburstSort // default 'descending' (largest top-left); 'none' keeps input order
125
+ level_lighten?: number
126
+ // Aggregate sibling cells below this fraction of the total into one 'Other'
127
+ // cell per parent (only when >= 2 qualify); 0 disables
128
+ min_fraction?: number
129
+ other_label?: string
130
+ max_depth?: number // levels shown below the current zoom root (0 = all)
131
+ padding_inner?: number // px gap between sibling cells
132
+ padding_top?: number // px header strip on branch cells (0 = no headers)
133
+ padding_outer?: number // px inset of children within their parent (plotly marker.pad)
134
+ show_labels?: boolean
135
+ label_text?: SunburstLabelText // what labels display (plotly textinfo equivalent)
136
+ // Structured multiline labels. Unlike cell_content, this keeps built-in
137
+ // hover/focus/click and tooltip behavior on the underlying cell.
138
+ label_formatter?: TreemapLabelFormatter<Metadata>
139
+ label_fit?: TreemapLabelFit // shrink-to-fit (default), hide, or clip at max size
140
+ label_min_font_size?: number // px floor used by shrink mode
141
+ label_max_font_size?: number // px ceiling for leaf/cutoff labels
142
+ parent_label_font_size?: number // px size/ceiling for branch header labels
143
+ zoom_on_click?: boolean
144
+ zoom_root_id?: string | number | null // id of the cell the view is rooted on
145
+ show_breadcrumbs?: boolean // clickable ancestor trail when zoomed
146
+ // Color cells by a numeric metric (continuous colormap) instead of categorical
147
+ // inheritance; return null to keep a cell's categorical color
148
+ color_values?: (rect: PositionedArc<Metadata>) => number | null
149
+ color_scale?: D3InterpolateName
150
+ color_range?: Vec2 // defaults to the metric's [min, max]
151
+ colorbar?: ComponentProps<typeof ColorBar> | null // null hides it
152
+ export_buttons?: boolean // SVG/PNG download buttons in the controls pane
153
+ export_filename?: string
154
+ // Zoom transition timing (resizes/data swaps snap instantly, plotly-style).
155
+ // interpolate is not overridable: the component's rect interpolator also
156
+ // handles rect-array length changes on data swaps (default would throw)
157
+ tween?: Omit<TweenOptions<Rect[]>, `interpolate`>
158
+ value_format?: string
159
+ padding?: Sides
160
+ legend?: LegendConfig | null
161
+ show_legend?: boolean
162
+ tooltip?: Snippet<[TreemapNodeHandlerProps<Metadata>]>
163
+ // Fully replace the default cell rect + labels. NOTE: this also replaces the
164
+ // built-in hover/focus/click + tooltip wiring, so re-implement any
165
+ // interactivity you need inside the snippet.
166
+ cell_content?: Snippet<[{ arc: PositionedArc<Metadata>; rect: Rect }]>
167
+ change?: (data: TreemapNodeHandlerProps<Metadata> | null) => void
168
+ on_node_click?: (
169
+ data: TreemapNodeHandlerProps<Metadata> & { event: MouseEvent | KeyboardEvent },
170
+ ) => void
171
+ on_node_hover?: (
172
+ data: (TreemapNodeHandlerProps<Metadata> & { event: MouseEvent | FocusEvent }) | null,
173
+ ) => void
174
+ on_zoom?: (data: { root: TreemapNodeHandlerProps<Metadata> | null }) => void
175
+ header_controls?: Snippet<[{ height: number; width: number; fullscreen: boolean }]>
176
+ controls_extra?: Snippet<[{ zoom_root_id: string | number | null }]>
177
+ } = $props()
178
+
179
+ let [width, height] = $state([0, 0])
180
+ let wrapper: HTMLDivElement | undefined = $state()
181
+ let svg_element: SVGSVGElement | null = $state(null)
182
+ // Unique per instance so multiple treemaps on one page don't collide on the
183
+ // hatch pattern's SVG id.
184
+ const uid = $props.id()
185
+ const hatch_pattern_id = `treemap-hatch-${uid}`
186
+
187
+ let hovered_idx = $state<number | null>(null)
188
+ let hover_info = $state<TreemapNodeHandlerProps<Metadata> | null>(null)
189
+ let hover_pos = $state<{ x: number; y: number }>({ x: 0, y: 0 })
190
+ // Depth-1 category ids muted via legend toggle (dimmed, not removed - keeps layout stable)
191
+ let muted_ids = new SvelteSet<string | number>()
192
+
193
+ let pad = $derived(filter_padding(padding, DEFAULT_PADDING))
194
+ let inner_width = $derived(Math.max(0, width - pad.l - pad.r))
195
+ let avail_height = $derived(Math.max(0, height - pad.t - pad.b))
196
+ // measured height of the bottom colorbar (via observe_height, which resets it
197
+ // to 0 on unmount), reserved from the chart so it never overlaps the cells;
198
+ // capped at half the area so a bad measurement can't collapse the chart
199
+ let colorbar_height = $state(0)
200
+ let colorbar_reserve = $derived(
201
+ colorbar_height > 0 ? Math.min(colorbar_height + 16, avail_height / 2) : 0,
202
+ )
203
+ let inner_height = $derived(avail_height - colorbar_reserve)
204
+
205
+ // Tree semantics (values, colors, ids, pre-order indexing) are shared with
206
+ // Sunburst; only the pixel tiling below is treemap-specific.
207
+ let layout = $derived(
208
+ safe_hierarchy_layout(data, {
209
+ value_mode,
210
+ sort,
211
+ level_lighten,
212
+ min_fraction,
213
+ other_label,
214
+ }),
215
+ )
216
+
217
+ // Resolve the zoom root; stale ids (e.g. after a data swap) fall back to the root
218
+ let zoom_root = $derived(layout.arcs.find((arc) => arc.id === zoom_root_id) ?? layout.root)
219
+ let zoomed = $derived((zoom_root?.depth ?? 0) > 0)
220
+
221
+ // Drop muted ids that no longer exist when data changes (untrack avoids a
222
+ // self-trigger loop from reading/writing muted_ids in the same effect).
223
+ // Hover/focus state is index-based, so a layout swap would otherwise leave a stale
224
+ // tooltip and highlight whatever unrelated node now occupies the old index.
225
+ $effect(() => {
226
+ const { arcs } = layout
227
+ untrack(() => {
228
+ prune_muted_ids(arcs, muted_ids)
229
+ set_cell_hover(null)
230
+ focused_idx = null
231
+ })
232
+ })
233
+
234
+ // Re-tile the zoom root's subtree to the full plot area (plotly behavior:
235
+ // squarified aspect ratios stay correct at every zoom level, unlike projecting
236
+ // one fixed tiling through a window, which would distort cells and scale the
237
+ // fixed-px header strips). Zoom animation interpolates between tilings.
238
+ let target_rects = $derived(
239
+ tile_rects(
240
+ layout.arcs,
241
+ zoom_root?.node_idx ?? 0,
242
+ { width: inner_width, height: inner_height },
243
+ { padding_inner, padding_top, padding_outer },
244
+ ),
245
+ )
246
+ // Seeded at the current target (charts load fully drawn); untrack reads the
247
+ // tween options once at init (not meant to be reactive). The rect interpolator
248
+ // is applied after the user's options: Svelte's default interpolator throws
249
+ // when the rect-array length changes (data swaps), so it must not be overridable.
250
+ const rects_tween = new Tween<Rect[]>(
251
+ untrack(() => target_rects),
252
+ untrack(() => ({
253
+ duration: 400,
254
+ easing: cubicInOut,
255
+ ...tween,
256
+ interpolate: (from: Rect[], to: Rect[]) => (t: number) => lerp_rects(from, to, t),
257
+ })),
258
+ )
259
+ // Animate only zoom transitions; resizes, data swaps and padding tweaks snap
260
+ // instantly (animating a container drag-resize would chase the pointer with a
261
+ // 400ms lerp on every width change, and morphing between unrelated datasets
262
+ // is meaningless)
263
+ let prev_zoom_idx = untrack(() => zoom_root?.node_idx ?? 0)
264
+ let prev_arcs = untrack(() => layout.arcs)
265
+ $effect(() => {
266
+ const [target, zoom_idx, arcs] = [target_rects, zoom_root?.node_idx ?? 0, layout.arcs]
267
+ const zoom_changed = zoom_idx !== prev_zoom_idx && arcs === prev_arcs
268
+ ;[prev_zoom_idx, prev_arcs] = [zoom_idx, arcs]
269
+ rects_tween.set(target, zoom_changed ? undefined : { duration: 0 })
270
+ })
271
+ let rects = $derived(rects_tween.current)
272
+
273
+ // Deepest level rendered below the current zoom root (0 = unlimited)
274
+ let depth_cutoff = $derived(max_depth > 0 ? (zoom_root?.depth ?? 0) + max_depth : Infinity)
275
+ // Shared by rendering and legend placement. The zoom root fills the viewport
276
+ // and is represented by the breadcrumbs, not a cell — except when it's a leaf
277
+ // (e.g. programmatic zoom_root_id onto a compound), which renders as one full-
278
+ // viewport cell instead of a blank chart. Nodes outside the zoomed subtree
279
+ // hold zero rects from tile_rects.
280
+ const cell_visible = (arc: PositionedArc<Metadata>, rect: Rect): boolean =>
281
+ arc.depth > 0 &&
282
+ (arc.node_idx !== zoom_root?.node_idx || arc.is_leaf) &&
283
+ arc.depth <= depth_cutoff &&
284
+ rect.width > 0.5 &&
285
+ rect.height > 0.5
286
+ const idx_visible = (idx: number): boolean => {
287
+ const [arc, rect] = [layout.arcs[idx], rects[idx]]
288
+ return Boolean(arc && rect && cell_visible(arc, rect))
289
+ }
290
+ // Indices (= keys) of cells to render. A plain number array: arcs/rects/
291
+ // cell_info/cell_dim are all index-aligned, so rendering reads them by idx
292
+ // instead of rebuilding N wrapper objects per tween frame.
293
+ let visible_idxs = $derived.by(() => {
294
+ const idxs: number[] = []
295
+ for (let idx = 0; idx < layout.arcs.length; idx++) {
296
+ if (idx_visible(idx)) idxs.push(idx)
297
+ }
298
+ return idxs
299
+ })
300
+
301
+ // Roving tabindex: exactly one cell is in the tab order (the last-focused one,
302
+ // else the first visible cell); arrow keys move focus between cells. Every
303
+ // visible cell is focusable (not just clickable ones) so keyboard users can
304
+ // reach tooltips, and so zooming into a branch of plain leaves doesn't strand
305
+ // focus outside the chart. role="button" stays limited to clickable cells.
306
+ let focused_idx = $state<number | null>(null)
307
+ let roving_idx = $derived(
308
+ focused_idx != null && idx_visible(focused_idx) ? focused_idx : (visible_idxs[0] ?? null),
309
+ )
310
+
311
+ // Continuous metric coloring: when color_values is given, cells are colored by
312
+ // their metric on a d3 colormap (cells returning null keep their categorical color)
313
+ let metric = $derived(
314
+ compute_metric_colors(layout.arcs, color_values, color_scale, color_range),
315
+ )
316
+ const cell_color = (arc: PositionedArc<Metadata>): string =>
317
+ metric?.colors[arc.node_idx] ?? arc.color
318
+ // Hovered cell + its ancestors/descendants stay fully opaque, others dim
319
+ let cell_dim = $derived(compute_node_dim(layout.arcs, muted_ids, hovered_idx))
320
+
321
+ const contrast_for = make_cached_contrast()
322
+
323
+ const make_node_props = (arc: PositionedArc<Metadata>): TreemapNodeHandlerProps<Metadata> =>
324
+ node_handler_props(layout.arcs, arc, cell_color(arc))
325
+
326
+ // Rect center in container (pad-offset) pixel space, for tooltip + legend placement
327
+ const rect_center = (rect: Rect): { x: number; y: number } => ({
328
+ x: pad.l + rect.x + rect.width / 2,
329
+ y: pad.t + rect.y + rect.height / 2,
330
+ })
331
+
332
+ function set_cell_hover(idx: number | null, event?: MouseEvent | FocusEvent) {
333
+ // Same cell as before: only the cursor anchor moves - skip rebuilding the
334
+ // handler payload and re-firing change/on_node_hover on every mousemove.
335
+ // Requires hover_info: legend item hover sets hovered_idx alone (for dimming).
336
+ if (idx != null && idx === hovered_idx && hover_info) {
337
+ hover_pos = pointer_pos(event, svg_element) ?? hover_pos
338
+ return
339
+ }
340
+ if (idx != null) {
341
+ hovered = true
342
+ hovered_idx = idx
343
+ hover_info = make_node_props(layout.arcs[idx])
344
+ hover_pos =
345
+ pointer_pos(event, svg_element) ?? (rects[idx] ? rect_center(rects[idx]) : hover_pos)
346
+ change(hover_info)
347
+ if (event) on_node_hover?.({ ...hover_info, event })
348
+ } else {
349
+ if (hovered_idx == null && hover_info == null) return
350
+ hovered = false
351
+ hovered_idx = null
352
+ hover_info = null
353
+ change(null)
354
+ on_node_hover?.(null)
355
+ }
356
+ }
357
+
358
+ // Node idx carried by the event's nearest [data-treemap-node-idx] element
359
+ const idx_from_event = (event: Event): number | null => {
360
+ const idx = closest_data_idx(event, `data-treemap-node-idx`, svg_element)
361
+ return idx != null && layout.arcs[idx] ? idx : null
362
+ }
363
+
364
+ function handle_cell_hover_event(event: MouseEvent | FocusEvent) {
365
+ const idx = idx_from_event(event)
366
+ // roving tabindex follows keyboard focus
367
+ if (event.type === `focusin` && idx != null) focused_idx = idx
368
+ set_cell_hover(idx, event)
369
+ }
370
+
371
+ // Re-root the view on the given arc (or the data root when null) and notify
372
+ function zoom_to(arc: PositionedArc<Metadata> | null) {
373
+ const root = arc && arc.depth > 0 ? arc : null
374
+ zoom_root_id = root?.id ?? null
375
+ // The clicked cell expands to fill the viewport - drop the stale hover/tooltip
376
+ set_cell_hover(null)
377
+ on_zoom?.({ root: root && make_node_props(root) })
378
+ }
379
+
380
+ // Plotly semantics: clicking any cell (leaves included) zooms into it;
381
+ // clicking the current zoom root (the full-viewport cell a leaf zoom renders)
382
+ // zooms back out one level
383
+ function handle_cell_click(event: MouseEvent | KeyboardEvent) {
384
+ if (event instanceof MouseEvent && selection_within(wrapper)) return
385
+ const idx = idx_from_event(event)
386
+ if (idx == null) return
387
+ const arc = layout.arcs[idx]
388
+ on_node_click?.({ ...make_node_props(arc), event })
389
+ if (!zoom_on_click) return
390
+ if (arc.id === zoom_root?.id) zoom_out()
391
+ else zoom_to(arc)
392
+ }
393
+
394
+ function zoom_out() {
395
+ if (!zoomed) return
396
+ zoom_to(breadcrumb_arcs.at(-2) ?? null)
397
+ }
398
+
399
+ // Double-clicking empty chart background resets the zoom to the root (double-
400
+ // clicking a cell or label is click-to-zoom/text-selection territory, not a reset)
401
+ function handle_dblclick(event: MouseEvent) {
402
+ if (idx_from_event(event) != null || selection_within(wrapper)) return
403
+ if (zoomed) zoom_to(null)
404
+ }
405
+
406
+ const focus_cell = (idx: number | null) => {
407
+ if (idx == null) return
408
+ svg_element
409
+ ?.querySelector<SVGRectElement>(`.cells [data-treemap-node-idx="${idx}"]`)
410
+ ?.focus()
411
+ }
412
+
413
+ // Arrow-key navigation: left/right cycle through visible siblings (wrapping),
414
+ // down enters the first child, up returns to the parent (shared with Sunburst
415
+ // via the pre-order walk in render.ts)
416
+ const nav_target_from_event = (event: KeyboardEvent): number | null => {
417
+ const cur_idx = idx_from_event(event)
418
+ if (cur_idx == null) return null
419
+ return arrow_nav_target(layout.arcs, idx_visible, cur_idx, event.key)
420
+ }
421
+
422
+ function handle_cell_keydown(event: KeyboardEvent) {
423
+ const nav_target = nav_target_from_event(event)
424
+ if (nav_target != null) {
425
+ event.preventDefault()
426
+ focus_cell(nav_target)
427
+ return
428
+ }
429
+ if (!is_activation_key(event)) return
430
+ event.preventDefault()
431
+ const prev_root = zoom_root_id
432
+ handle_cell_click(event)
433
+ // Zooming via keyboard unmounts the focused cell - move focus to the new
434
+ // root's first child (pre-order: node_idx + 1), or the root cell itself for
435
+ // leaf zooms (rendered full-viewport), so keyboard users stay in the chart
436
+ if (zoom_root_id !== prev_root) {
437
+ tick().then(() => {
438
+ if (!zoom_root) return focus_cell(roving_idx)
439
+ focus_cell(zoom_root.is_leaf ? zoom_root.node_idx : zoom_root.node_idx + 1)
440
+ })
441
+ }
442
+ }
443
+
444
+ // Uniform now that any cell zooms (plotly semantics), not just branches
445
+ let cells_clickable = $derived(Boolean(on_node_click) || zoom_on_click)
446
+
447
+ let label_font = $derived(svg_label_font(svg_element))
448
+ // leading "<n>px" of the CSS font shorthand (e.g. "11px sans-serif")
449
+ let label_font_size = $derived.by(() => {
450
+ const leading_num = Number(label_font.match(/^[\d.]+/)?.[0])
451
+ return safe_font_size(leading_num, 11)
452
+ })
453
+ let resolved_parent_label_font_size = $derived(safe_font_size(parent_label_font_size, 14))
454
+ const cached_text_width = make_cached_text_width()
455
+
456
+ // Per-cell label text, measured width, fill/label colors and aria string -
457
+ // all zoom-independent, so computed once per layout/option change
458
+ let cell_info = $derived(
459
+ compute_node_infos(layout.arcs, {
460
+ label_text,
461
+ value_format,
462
+ label_font,
463
+ color_for: cell_color,
464
+ text_width: cached_text_width,
465
+ contrast: contrast_for,
466
+ }),
467
+ )
468
+
469
+ const LABEL_MARGIN = 6 // px clearance between label text and cell edges
470
+ const label_clip_id = (idx: number) => `treemap-label-clip-${uid}-${idx}`
471
+ const font_for_line = (line: TreemapLabelLine, font_size: number): string => {
472
+ const sized_font = label_font.replace(/(?:\d+(?:\.\d+)?|\.\d+)px/, `${font_size}px`)
473
+ return line.font_weight == null ? sized_font : `${line.font_weight} ${sized_font}`
474
+ }
475
+ const measure_label_line = (line: TreemapLabelLine, font_size: number): number => {
476
+ const measured_width = cached_text_width(line.text, font_for_line(line, font_size))
477
+ // Canvas text metrics can be unavailable during SSR and in lightweight DOM
478
+ // environments. A conservative fallback keeps fitting deterministic there.
479
+ return measured_width > 0 ? measured_width : line.text.length * font_size * 0.6
480
+ }
481
+
482
+ // Formatter output is layout-independent, so resolve it once per node instead
483
+ // of on every frame of a zoom tween. Without a formatter, fit modes use the
484
+ // richest built-in variant (compound text when configured).
485
+ // The depth-0 root never renders a cell, so the formatter is never invoked
486
+ // for it (for array data it's synthetic and e.g. carries no metadata).
487
+ let label_lines = $derived(
488
+ layout.arcs.map((arc, idx) =>
489
+ arc.depth === 0
490
+ ? []
491
+ : normalize_treemap_label_lines(
492
+ label_formatter ? label_formatter(arc) : cell_info[idx].variants[0]?.text,
493
+ ),
494
+ ),
495
+ )
496
+
497
+ // Label text + placement for a cell; null means hide the label.
498
+ // Branch cells label their header strip (top-left); leaves (and branches at the
499
+ // depth cutoff, which render as plain cells) center their label, rotating 90°
500
+ // in thin-but-tall cells like the icicle shape does.
501
+ function place_label(
502
+ arc: PositionedArc<Metadata>,
503
+ rect: Rect,
504
+ ): TreemapLabelPlacement | null {
505
+ // Branches with visible children only ever label their header strip: a
506
+ // centered label would paint over the descendant cells that cover the rest
507
+ // of the cell, so when the strip is missing/too thin the label is dropped
508
+ const has_visible_children = !arc.is_leaf && arc.depth < depth_cutoff
509
+
510
+ const place = (lines: TreemapLabelLine[]) =>
511
+ place_treemap_label({
512
+ rect,
513
+ lines,
514
+ header: has_visible_children,
515
+ fit: label_fit,
516
+ min_font_size: label_min_font_size,
517
+ max_font_size: has_visible_children
518
+ ? resolved_parent_label_font_size
519
+ : (label_max_font_size ?? label_font_size),
520
+ padding_top,
521
+ margin: LABEL_MARGIN,
522
+ measure_line: (line, font_size) =>
523
+ measure_label_line(
524
+ has_visible_children && line.font_weight == null
525
+ ? { ...line, font_weight: 600 }
526
+ : line,
527
+ font_size,
528
+ ),
529
+ })
530
+
531
+ if (!label_formatter && label_fit === `hide`) {
532
+ for (const { text } of cell_info[arc.node_idx].variants) {
533
+ const placement = place([{ text }])
534
+ if (placement) return placement
535
+ }
536
+ return null
537
+ }
538
+ return place(label_lines[arc.node_idx])
539
+ }
540
+
541
+ // hide mode never overflows (unfitting labels return null), so clipPaths are
542
+ // only rendered/applied for the shrink/clip modes (with or without formatter)
543
+ let clip_labels = $derived(label_fit !== `hide`)
544
+ const label_clip_rect = (rect: Rect, header: boolean): Rect => {
545
+ const inset = 1
546
+ const clip_height = header ? Math.min(rect.height, padding_top) : rect.height
547
+ return {
548
+ x: rect.x + inset,
549
+ y: rect.y + inset,
550
+ width: Math.max(0, rect.width - 2 * inset),
551
+ height: Math.max(0, clip_height - 2 * inset),
552
+ }
553
+ }
554
+ // Defs and visible text share these placements, avoiding duplicate text
555
+ // measurement and fitting work on every frame of a zoom tween.
556
+ let label_placements = $derived(
557
+ new Map(visible_idxs.map((idx) => [idx, place_label(layout.arcs[idx], rects[idx])])),
558
+ )
559
+
560
+ // Legend: one item per depth-1 category, toggling mutes (dims) rather than removes.
561
+ let depth1_arcs = $derived(layout.arcs.filter((arc) => arc.depth === 1))
562
+ let legend_visible = $derived(show_legend && legend != null && depth1_arcs.length > 1)
563
+ let legend_element = $state<HTMLDivElement | undefined>()
564
+ let legend_placement = $derived.by(() => {
565
+ if (!legend_visible || !width || !height) return null
566
+ // Place against the settled (target) tiling, not the animated one - placement
567
+ // is stable during zoom tweens. Same visibility rule as rendering so hidden
568
+ // cells (zoom root, beyond max_depth) don't repel the legend.
569
+ const settled = layout.arcs.flatMap((arc, idx) =>
570
+ cell_visible(arc, target_rects[idx]) ? [rect_center(target_rects[idx])] : [],
571
+ )
572
+ return compute_element_placement({
573
+ plot_bounds: { x: pad.l, y: pad.t, width: inner_width, height: inner_height },
574
+ element: legend_element,
575
+ element_size: { width: 120, height: 60 },
576
+ axis_clearance: legend?.axis_clearance,
577
+ exclude_rects: [],
578
+ points: settled,
579
+ })
580
+ })
581
+ let legend_data: LegendItem[] = $derived(
582
+ hierarchy_legend_items(depth1_arcs, muted_ids, cell_color),
583
+ )
584
+
585
+ const toggle_category = (series_idx: number) =>
586
+ toggle_muted(muted_ids, depth1_arcs[series_idx]?.id)
587
+
588
+ $effect(() => set_fullscreen_bg(wrapper, fullscreen, `--treemap-fullscreen-bg`))
589
+
590
+ let breadcrumb_arcs = $derived(ancestor_chain(layout.arcs, zoom_root))
591
+
592
+ const export_chart = (format: `svg` | `png`) =>
593
+ export_hierarchy_chart(svg_element, export_filename, format)
594
+ </script>
595
+
596
+ <svelte:window
597
+ onkeydown={(evt) =>
598
+ handle_hierarchy_escape(evt, {
599
+ wrapper,
600
+ fullscreen,
601
+ zoomed,
602
+ zoom_out,
603
+ exit_fullscreen: () => (fullscreen = false),
604
+ })}
605
+ />
606
+
607
+ <div
608
+ bind:this={wrapper}
609
+ bind:clientWidth={width}
610
+ bind:clientHeight={height}
611
+ {...rest}
612
+ class={[`treemap`, rest.class]}
613
+ class:fullscreen
614
+ >
615
+ {#if width && height}
616
+ <div class="header-controls">
617
+ {@render header_controls?.({ height, width, fullscreen })}
618
+ {#if show_controls}
619
+ <TreemapControls
620
+ chart="treemap"
621
+ toggle_props={{
622
+ ...controls_toggle_props,
623
+ // join the header flex row instead of absolute positioning
624
+ style: `position: static; ${controls_toggle_props?.style ?? ``}`,
625
+ }}
626
+ pane_props={controls_pane_props}
627
+ bind:show_controls
628
+ bind:controls_open
629
+ bind:value_mode
630
+ bind:max_depth
631
+ bind:padding_inner
632
+ bind:padding_top
633
+ bind:padding_outer
634
+ bind:min_fraction
635
+ bind:show_labels
636
+ bind:label_text
637
+ bind:zoom_on_click
638
+ bind:show_breadcrumbs
639
+ {export_buttons}
640
+ on_export={export_chart}
641
+ >
642
+ {@render controls_extra?.({ zoom_root_id })}
643
+ </TreemapControls>
644
+ {/if}
645
+ {#if fullscreen_toggle}
646
+ <FullscreenToggle bind:fullscreen />
647
+ {/if}
648
+ </div>
649
+ {#if show_breadcrumbs && breadcrumb_arcs.length > 0}
650
+ <!-- plotly-style pathbar: chevron segments, current root last (disabled) -->
651
+ <nav class="breadcrumbs" aria-label="zoom path">
652
+ {#each breadcrumb_arcs as crumb, crumb_idx (crumb.node_idx)}
653
+ <button
654
+ type="button"
655
+ class="breadcrumb"
656
+ disabled={crumb_idx === breadcrumb_arcs.length - 1}
657
+ onclick={() => zoom_to(crumb)}
658
+ >
659
+ {crumb.depth === 0 ? `all` : (crumb.label ?? crumb.id)}
660
+ </button>
661
+ {/each}
662
+ </nav>
663
+ {/if}
664
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
665
+ <svg
666
+ bind:this={svg_element}
667
+ viewBox="0 0 {width} {height}"
668
+ role="application"
669
+ aria-label={rest[`aria-label`] ?? `Treemap chart`}
670
+ onmouseleave={() => set_cell_hover(null)}
671
+ ondblclick={handle_dblclick}
672
+ >
673
+ <!-- inert unless some cell references it via fill -->
674
+ <defs>
675
+ <pattern id={hatch_pattern_id} patternUnits="userSpaceOnUse" width="8" height="8">
676
+ <path class="hatch-pattern-line" d="M-1,1 l2,-2 M0,8 l8,-8 M7,9 l2,-2" />
677
+ </pattern>
678
+ {#if show_labels && !cell_content && clip_labels}
679
+ {#each visible_idxs as idx (idx)}
680
+ {@const label = label_placements.get(idx)}
681
+ {#if label}
682
+ <clipPath id={label_clip_id(idx)}>
683
+ <rect {...label_clip_rect(rects[idx], label.header)} />
684
+ </clipPath>
685
+ {/if}
686
+ {/each}
687
+ {/if}
688
+ </defs>
689
+ <!-- Hover/click delegation sits on the chart group (not the cells group) so
690
+ labels - which carry the same data-treemap-node-idx and are selectable text -
691
+ forward interactions to their cell instead of swallowing them -->
692
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
693
+ <g
694
+ transform={`translate(${pad.l}, ${pad.t})`}
695
+ onmousemove={handle_cell_hover_event}
696
+ onmouseleave={() => set_cell_hover(null)}
697
+ onfocusin={handle_cell_hover_event}
698
+ onfocusout={() => set_cell_hover(null)}
699
+ onclick={handle_cell_click}
700
+ onkeydown={handle_cell_keydown}
701
+ >
702
+ <!-- Cells: pre-order document order paints parents first, children on top -->
703
+ <g class="cells">
704
+ {#each visible_idxs as idx (idx)}
705
+ {@const rect = rects[idx]}
706
+ {#if cell_content}
707
+ {@render cell_content({ arc: layout.arcs[idx], rect })}
708
+ {:else}
709
+ {@const info = cell_info[idx]}
710
+ {@const opacity = cell_dim[idx].opacity}
711
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
712
+ <rect
713
+ x={rect.x}
714
+ y={rect.y}
715
+ width={rect.width}
716
+ height={rect.height}
717
+ data-treemap-node-idx={idx}
718
+ fill={info.fill}
719
+ fill-opacity={opacity}
720
+ role={cells_clickable ? `button` : undefined}
721
+ tabindex={idx === roving_idx ? 0 : -1}
722
+ aria-label={info.aria}
723
+ style:cursor={cells_clickable ? `pointer` : `default`}
724
+ />
725
+ {#if layout.arcs[idx].hatch}
726
+ <!-- Decorative texture overlay; ignores pointer events -->
727
+ <rect
728
+ class="cell-hatch"
729
+ aria-hidden="true"
730
+ x={rect.x}
731
+ y={rect.y}
732
+ width={rect.width}
733
+ height={rect.height}
734
+ fill="url(#{hatch_pattern_id})"
735
+ fill-opacity={opacity}
736
+ />
737
+ {/if}
738
+ {/if}
739
+ {/each}
740
+ </g>
741
+
742
+ <!-- Cell labels: selectable text; data-treemap-node-idx forwards hover/click
743
+ to the underlying cell via the chart-group delegation above -->
744
+ {#if show_labels && !cell_content}
745
+ <g class="cell-labels">
746
+ {#each visible_idxs as idx (idx)}
747
+ {@const lbl = label_placements.get(idx)}
748
+ {#if lbl}
749
+ <!-- Keep the clip on this untransformed wrapper. Applying it to
750
+ rotated text rotates the clipping region and crops the wrong area. -->
751
+ <g clip-path={clip_labels ? `url(#${label_clip_id(idx)})` : undefined}>
752
+ <text
753
+ class="cell-label"
754
+ class:header={lbl.header}
755
+ data-treemap-node-idx={idx}
756
+ x={lbl.x}
757
+ y={lbl.lines[0].y}
758
+ dominant-baseline={lbl.dominant_baseline}
759
+ transform={lbl.transform}
760
+ fill={cell_info[idx].label_fill}
761
+ fill-opacity={cell_dim[idx].label_opacity}
762
+ font-size={lbl.font_size}
763
+ style:cursor={cells_clickable ? `pointer` : `text`}
764
+ >
765
+ {#each lbl.lines as line}
766
+ <tspan
767
+ class={line.class}
768
+ x={lbl.x}
769
+ y={line.y}
770
+ font-size={lbl.font_size * (line.font_scale ?? 1)}
771
+ font-weight={line.font_weight}
772
+ opacity={line.opacity}
773
+ fill={line.fill}
774
+ >
775
+ {line.text}
776
+ </tspan>
777
+ {/each}
778
+ </text>
779
+ </g>
780
+ {/if}
781
+ {/each}
782
+ </g>
783
+ {/if}
784
+ </g>
785
+ </svg>
786
+ {/if}
787
+
788
+ {#if hover_info}
789
+ <PlotTooltip
790
+ x={hover_pos.x}
791
+ y={hover_pos.y}
792
+ offset={{ x: 10, y: 5 }}
793
+ constrain_to={{ width, height }}
794
+ fallback_size={{ width: 140, height: 44 }}
795
+ bg_color={hover_info.color}
796
+ >
797
+ {#if tooltip}
798
+ {@render tooltip(hover_info)}
799
+ {:else}
800
+ <strong>{hover_info.label_path.join(` › `)}</strong>: {format_value(
801
+ hover_info.value,
802
+ value_format,
803
+ )}
804
+ ({format_value(hover_info.fraction, `.1%`)} of total{hover_info.depth > 1
805
+ ? `, ${format_value(hover_info.parent_fraction, `.1%`)} of parent`
806
+ : ``})
807
+ {/if}
808
+ </PlotTooltip>
809
+ {/if}
810
+
811
+ {#if legend_visible}
812
+ {@const legend_left = legend_placement?.x ?? pad.l + 10}
813
+ {@const legend_top = legend_placement?.y ?? pad.t + 10}
814
+ <PlotLegend
815
+ bind:root_element={legend_element}
816
+ {...legend}
817
+ series_data={legend_data}
818
+ on_toggle={legend?.on_toggle ?? toggle_category}
819
+ on_item_hover={(item) =>
820
+ (hovered_idx =
821
+ item != null && item.series_idx >= 0
822
+ ? (depth1_arcs[item.series_idx]?.node_idx ?? null)
823
+ : null)}
824
+ style={`position: absolute; left: ${legend_left}px; top: ${legend_top}px; pointer-events: auto; ${
825
+ legend?.style ?? ``
826
+ }`}
827
+ />
828
+ {/if}
829
+
830
+ {#if metric && colorbar != null}
831
+ <!-- positioning + height measurement live on ColorBar's own root (via rest
832
+ props + attachment) instead of an extra wrapper div -->
833
+ <ColorBar
834
+ {color_scale}
835
+ range={metric.range}
836
+ {...colorbar}
837
+ wrapper_style="{colorbar?.orientation === `vertical`
838
+ ? `--cbar-height: var(--treemap-colorbar-height, 150px);`
839
+ : ``} {colorbar?.wrapper_style ?? ``}"
840
+ 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; {colorbar?.style ??
841
+ ``}"
842
+ {@attach observe_height((px) => (colorbar_height = px))}
843
+ />
844
+ {/if}
845
+
846
+ {@render children?.({ height, width, fullscreen })}
847
+ </div>
848
+
849
+ <style>
850
+ .treemap {
851
+ position: relative;
852
+ width: var(--treemap-width, 100%);
853
+ height: var(--treemap-height, auto);
854
+ min-height: var(--treemap-min-height, 300px);
855
+ container-type: size;
856
+ z-index: var(--treemap-z-index, auto);
857
+ /* flex-basis auto (not 1 = 0%) so an authored height wins over flex sizing in
858
+ column-flex parents while the chart still grows/shrinks to fill fixed layouts */
859
+ flex: var(--treemap-flex, 1 1 auto);
860
+ display: var(--treemap-display, flex);
861
+ flex-direction: column;
862
+ /* no bg shading by default: the cells are the chart; set --treemap-bg to
863
+ add a panel background */
864
+ background: var(--treemap-bg, transparent);
865
+ border-radius: var(--treemap-border-radius, var(--border-radius, 3pt));
866
+ }
867
+ .treemap.fullscreen {
868
+ position: fixed;
869
+ top: 0;
870
+ left: 0;
871
+ width: 100vw !important;
872
+ height: 100vh !important;
873
+ z-index: var(--treemap-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
874
+ margin: 0;
875
+ border-radius: 0;
876
+ background: var(--treemap-fullscreen-bg, var(--treemap-bg, var(--plot-bg)));
877
+ max-height: none !important;
878
+ overflow: hidden;
879
+ /* border-top (not padding-top): bind:clientHeight includes padding but excludes
880
+ borders */
881
+ border-top: var(--plot-fullscreen-padding-top, 2em) solid
882
+ var(--treemap-fullscreen-bg, var(--treemap-bg, var(--plot-bg, transparent)));
883
+ box-sizing: border-box;
884
+ }
885
+ .header-controls {
886
+ position: absolute;
887
+ top: var(--ctrl-btn-top, 5pt);
888
+ right: var(--fullscreen-btn-right, 4px);
889
+ z-index: var(--fullscreen-btn-z-index, 10);
890
+ display: flex;
891
+ align-items: center;
892
+ gap: 8px;
893
+ }
894
+ .header-controls :global(.fullscreen-toggle) {
895
+ position: static;
896
+ opacity: 1;
897
+ }
898
+ /* plotly-pathbar look: right-pointing chevron segments with a matching left
899
+ notch on all but the first, slightly overlapped so they read as one bar.
900
+ Opaque background: the pathbar overlays arbitrarily-colored cells, and a
901
+ translucent one would be illegible over dark fills */
902
+ .breadcrumb {
903
+ background: var(--treemap-btn-bg, light-dark(#e3e6ea, #33383f));
904
+ color: inherit;
905
+ border: none;
906
+ padding: var(--treemap-breadcrumbs-padding, 0 14px 0 12px);
907
+ cursor: pointer;
908
+ font: inherit;
909
+ clip-path: polygon(
910
+ 0 0,
911
+ calc(100% - 7px) 0,
912
+ 100% 50%,
913
+ calc(100% - 7px) 100%,
914
+ 0 100%,
915
+ 7px 50%
916
+ );
917
+ }
918
+ .breadcrumb:first-child {
919
+ border-radius: 3pt 0 0 3pt;
920
+ padding-inline-start: 8px;
921
+ clip-path: polygon(0 0, calc(100% - 7px) 0, 100% 50%, calc(100% - 7px) 100%, 0 100%);
922
+ }
923
+ .breadcrumb:hover:not(:disabled) {
924
+ background: var(--treemap-btn-hover-bg, light-dark(#d0d5db, #454b54));
925
+ }
926
+ /* inset focus ring: native outlines get clipped by the chevron clip-path and
927
+ hidden under the next overlapped segment */
928
+ .breadcrumb:focus-visible {
929
+ position: relative;
930
+ z-index: 1;
931
+ outline: none;
932
+ box-shadow: inset 0 0 0 2px var(--accent-color, Highlight);
933
+ }
934
+ .breadcrumbs {
935
+ position: absolute;
936
+ top: var(--treemap-breadcrumbs-top, 5pt);
937
+ left: var(--treemap-breadcrumbs-left, 8px);
938
+ z-index: 9;
939
+ display: flex;
940
+ align-items: stretch;
941
+ flex-wrap: wrap;
942
+ max-width: 75%;
943
+ font-size: var(--treemap-breadcrumbs-font-size, 0.85em);
944
+ }
945
+ /* negative gap: each chevron tip tucks into the next segment's left notch */
946
+ .breadcrumb + .breadcrumb {
947
+ margin-left: -6px;
948
+ }
949
+ .breadcrumb:disabled {
950
+ cursor: default;
951
+ font-weight: bold;
952
+ }
953
+ .treemap :global(.pane-toggle),
954
+ .treemap .header-controls {
955
+ opacity: 0;
956
+ transition:
957
+ opacity 0.2s,
958
+ background-color 0.2s;
959
+ }
960
+ .treemap:hover :global(.pane-toggle),
961
+ .treemap:hover .header-controls,
962
+ .treemap :global(.pane-toggle:focus-visible),
963
+ .treemap :global(.pane-toggle[aria-expanded='true']),
964
+ .treemap .header-controls:has(:global([aria-expanded='true'])),
965
+ .treemap .header-controls:focus-within {
966
+ opacity: 1;
967
+ }
968
+ svg {
969
+ width: var(--treemap-svg-width, 100%);
970
+ height: var(--treemap-svg-height, 100%);
971
+ flex: var(--treemap-svg-flex, 1);
972
+ overflow: var(--treemap-svg-overflow, visible);
973
+ fill: var(--text-color);
974
+ font-size: var(--treemap-font-size, 11px);
975
+ }
976
+ .cells rect {
977
+ /* stroke via CSS (not presentation attributes): var() substitution in SVG
978
+ presentation attributes is not reliably supported across browsers.
979
+ Dividers matter more here than in the sunburst: sibling gaps show the
980
+ parent's fill (parents paint under their children), which is identical to
981
+ the children's inherited color - without a stroke, same-color siblings
982
+ blend into one shape. Not defaulted to --plot-bg because that is often
983
+ semi-transparent (near-invisible as a line); light-dark keeps dividers
984
+ paper-colored in both themes, like plotly */
985
+ stroke: var(--treemap-cell-stroke, light-dark(white, #16181d));
986
+ stroke-width: var(--treemap-cell-stroke-width, 1);
987
+ transition: fill-opacity 0.15s ease;
988
+ }
989
+ .cells rect:hover {
990
+ filter: brightness(var(--treemap-hover-brightness, 1.08));
991
+ }
992
+ /* decorative overlay: never intercepts pointer events, no hover effect */
993
+ .cells rect.cell-hatch {
994
+ stroke: none;
995
+ pointer-events: none;
996
+ }
997
+ /* subtle by default: thin stripes inheriting the cell border color (itself
998
+ defaulting to the chart bg) at low opacity, so hatching matches the gaps
999
+ between cells instead of reading as solid white */
1000
+ .hatch-pattern-line {
1001
+ stroke: var(
1002
+ --treemap-hatch-stroke,
1003
+ color-mix(
1004
+ in srgb,
1005
+ var(--treemap-cell-stroke, light-dark(white, #16181d)) 30%,
1006
+ transparent
1007
+ )
1008
+ );
1009
+ stroke-width: var(--treemap-hatch-stroke-width, 0.35);
1010
+ }
1011
+ .cell-label {
1012
+ text-anchor: middle;
1013
+ /* selectable so labels can be copied; clicks/hover still reach the underlying
1014
+ cell via data-treemap-node-idx + delegation on the chart group */
1015
+ -webkit-user-select: text;
1016
+ user-select: text;
1017
+ }
1018
+ .cell-label.header {
1019
+ text-anchor: start;
1020
+ font-weight: 600;
1021
+ }
1022
+ </style>