matterviz 0.3.5 → 0.3.6

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 (855) hide show
  1. package/.vscode/launch.json +13 -0
  2. package/.vscodeignore +7 -0
  3. package/dist/assets/STLExporter-BpTH3YHE.js +8 -0
  4. package/dist/assets/browser-DdDecX_W.js +1 -0
  5. package/dist/assets/export-qgn-H9y6.js +2 -0
  6. package/dist/assets/main-DiKYzti2.css +1 -0
  7. package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
  8. package/dist/extension.js +31293 -0
  9. package/dist/src/lib/FilePicker.svelte +360 -0
  10. package/dist/src/lib/MillerIndexInput.svelte +66 -0
  11. package/dist/src/lib/api/mp.ts +26 -0
  12. package/dist/src/lib/api/optimade.ts +204 -0
  13. package/dist/src/lib/brillouin/BrillouinZone.svelte +549 -0
  14. package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +144 -0
  15. package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +146 -0
  16. package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +146 -0
  17. package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +476 -0
  18. package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +92 -0
  19. package/dist/src/lib/brillouin/compute.ts +529 -0
  20. package/dist/src/lib/brillouin/index.ts +8 -0
  21. package/dist/src/lib/brillouin/types.ts +51 -0
  22. package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +327 -0
  23. package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +846 -0
  24. package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +3193 -0
  25. package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +94 -0
  26. package/dist/src/lib/chempot-diagram/chempot-worker.ts +11 -0
  27. package/dist/src/lib/chempot-diagram/color.ts +42 -0
  28. package/dist/src/lib/chempot-diagram/compute.ts +1014 -0
  29. package/dist/src/lib/chempot-diagram/index.ts +6 -0
  30. package/dist/src/lib/chempot-diagram/pointer.ts +56 -0
  31. package/dist/src/lib/chempot-diagram/temperature.ts +77 -0
  32. package/dist/src/lib/chempot-diagram/types.ts +130 -0
  33. package/dist/src/lib/colors/index.ts +249 -0
  34. package/dist/src/lib/composition/BarChart.svelte +297 -0
  35. package/dist/src/lib/composition/BubbleChart.svelte +218 -0
  36. package/dist/src/lib/composition/Composition.svelte +165 -0
  37. package/dist/src/lib/composition/Formula.svelte +268 -0
  38. package/dist/src/lib/composition/FormulaFilter.svelte +1257 -0
  39. package/dist/src/lib/composition/PieChart.svelte +323 -0
  40. package/dist/src/lib/composition/format.ts +155 -0
  41. package/dist/src/lib/composition/index.ts +37 -0
  42. package/dist/src/lib/composition/parse.ts +605 -0
  43. package/dist/src/lib/constants.ts +134 -0
  44. package/dist/src/lib/controls.ts +42 -0
  45. package/dist/src/lib/convex-hull/ConvexHull.svelte +157 -0
  46. package/dist/src/lib/convex-hull/ConvexHull2D.svelte +825 -0
  47. package/dist/src/lib/convex-hull/ConvexHull3D.svelte +1801 -0
  48. package/dist/src/lib/convex-hull/ConvexHull4D.svelte +1398 -0
  49. package/dist/src/lib/convex-hull/ConvexHullControls.svelte +535 -0
  50. package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +125 -0
  51. package/dist/src/lib/convex-hull/ConvexHullStats.svelte +929 -0
  52. package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +131 -0
  53. package/dist/src/lib/convex-hull/GasPressureControls.svelte +247 -0
  54. package/dist/src/lib/convex-hull/StructurePopup.svelte +151 -0
  55. package/dist/src/lib/convex-hull/TemperatureSlider.svelte +140 -0
  56. package/dist/src/lib/convex-hull/barycentric-coords.ts +246 -0
  57. package/dist/src/lib/convex-hull/demo-temperature.ts +63 -0
  58. package/dist/src/lib/convex-hull/gas-thermodynamics.ts +405 -0
  59. package/dist/src/lib/convex-hull/helpers.ts +932 -0
  60. package/dist/src/lib/convex-hull/index.ts +202 -0
  61. package/dist/src/lib/convex-hull/thermodynamics.ts +2192 -0
  62. package/dist/src/lib/convex-hull/types.ts +267 -0
  63. package/dist/src/lib/coordination/CoordinationBarPlot.svelte +311 -0
  64. package/dist/src/lib/coordination/calc-coordination.ts +93 -0
  65. package/dist/src/lib/coordination/index.ts +9 -0
  66. package/dist/src/lib/effects.svelte.ts +48 -0
  67. package/dist/src/lib/element/BohrAtom.svelte +147 -0
  68. package/dist/src/lib/element/ElementHeading.svelte +26 -0
  69. package/dist/src/lib/element/ElementPhoto.svelte +57 -0
  70. package/dist/src/lib/element/ElementStats.svelte +80 -0
  71. package/dist/src/lib/element/ElementTile.svelte +484 -0
  72. package/dist/src/lib/element/data.json.gz.d.ts +4 -0
  73. package/dist/src/lib/element/data.ts +14 -0
  74. package/dist/src/lib/element/index.ts +8 -0
  75. package/dist/src/lib/element/types.ts +62 -0
  76. package/dist/src/lib/feedback/ClickFeedback.svelte +58 -0
  77. package/dist/src/lib/feedback/DragOverlay.svelte +42 -0
  78. package/dist/src/lib/feedback/index.ts +4 -0
  79. package/dist/src/lib/fermi-surface/FermiSlice.svelte +189 -0
  80. package/dist/src/lib/fermi-surface/FermiSurface.svelte +600 -0
  81. package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +448 -0
  82. package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +794 -0
  83. package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +111 -0
  84. package/dist/src/lib/fermi-surface/compute.ts +728 -0
  85. package/dist/src/lib/fermi-surface/constants.ts +32 -0
  86. package/dist/src/lib/fermi-surface/export.ts +64 -0
  87. package/dist/src/lib/fermi-surface/index.ts +14 -0
  88. package/dist/src/lib/fermi-surface/marching-cubes.ts +3 -0
  89. package/dist/src/lib/fermi-surface/parse.ts +574 -0
  90. package/dist/src/lib/fermi-surface/symmetry.ts +56 -0
  91. package/dist/src/lib/fermi-surface/types.ts +159 -0
  92. package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +1545 -0
  93. package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  94. package/dist/src/lib/heatmap-matrix/index.ts +167 -0
  95. package/dist/src/lib/heatmap-matrix/shared.ts +7 -0
  96. package/dist/src/lib/icons.ts +650 -0
  97. package/dist/src/lib/index.ts +61 -0
  98. package/dist/src/lib/io/decompress.ts +92 -0
  99. package/dist/src/lib/io/export.ts +385 -0
  100. package/dist/src/lib/io/fetch.ts +46 -0
  101. package/dist/src/lib/io/file-drop.ts +51 -0
  102. package/dist/src/lib/io/index.ts +7 -0
  103. package/dist/src/lib/io/is-binary.ts +24 -0
  104. package/dist/src/lib/io/types.ts +8 -0
  105. package/dist/src/lib/io/url-drop.ts +141 -0
  106. package/dist/src/lib/isosurface/Isosurface.svelte +285 -0
  107. package/dist/src/lib/isosurface/IsosurfaceControls.svelte +277 -0
  108. package/dist/src/lib/isosurface/index.ts +7 -0
  109. package/dist/src/lib/isosurface/parse.ts +656 -0
  110. package/dist/src/lib/isosurface/slice.ts +175 -0
  111. package/dist/src/lib/isosurface/types.ts +309 -0
  112. package/dist/src/lib/labels.ts +320 -0
  113. package/dist/src/lib/layout/FullscreenToggle.svelte +50 -0
  114. package/dist/src/lib/layout/InfoCard.svelte +120 -0
  115. package/dist/src/lib/layout/InfoTag.svelte +185 -0
  116. package/dist/src/lib/layout/PropertyFilter.svelte +246 -0
  117. package/dist/src/lib/layout/SettingsSection.svelte +148 -0
  118. package/dist/src/lib/layout/SubpageGrid.svelte +82 -0
  119. package/dist/src/lib/layout/fullscreen.ts +65 -0
  120. package/dist/src/lib/layout/index.ts +11 -0
  121. package/dist/src/lib/layout/json-tree/JsonNode.svelte +548 -0
  122. package/dist/src/lib/layout/json-tree/JsonTree.svelte +1230 -0
  123. package/dist/src/lib/layout/json-tree/JsonValue.svelte +334 -0
  124. package/dist/src/lib/layout/json-tree/index.ts +3 -0
  125. package/dist/src/lib/layout/json-tree/types.ts +126 -0
  126. package/dist/src/lib/layout/json-tree/utils.ts +682 -0
  127. package/dist/src/lib/marching-cubes.ts +614 -0
  128. package/dist/src/lib/math.ts +1081 -0
  129. package/dist/src/lib/overlays/ContextMenu.svelte +162 -0
  130. package/dist/src/lib/overlays/CopyButton.svelte +45 -0
  131. package/dist/src/lib/overlays/DragControlTab.svelte +98 -0
  132. package/dist/src/lib/overlays/DraggablePane.svelte +487 -0
  133. package/dist/src/lib/overlays/InfoPaneCards.svelte +149 -0
  134. package/dist/src/lib/overlays/index.ts +3 -0
  135. package/dist/src/lib/periodic-table/PeriodicTable.svelte +469 -0
  136. package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +557 -0
  137. package/dist/src/lib/periodic-table/PropertySelect.svelte +37 -0
  138. package/dist/src/lib/periodic-table/index.ts +12 -0
  139. package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1086 -0
  140. package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +444 -0
  141. package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  142. package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
  143. package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
  144. package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +203 -0
  145. package/dist/src/lib/phase-diagram/build-diagram.ts +186 -0
  146. package/dist/src/lib/phase-diagram/colors.ts +58 -0
  147. package/dist/src/lib/phase-diagram/diagram-input.ts +40 -0
  148. package/dist/src/lib/phase-diagram/index.ts +13 -0
  149. package/dist/src/lib/phase-diagram/parse.ts +348 -0
  150. package/dist/src/lib/phase-diagram/svg-to-diagram.ts +1023 -0
  151. package/dist/src/lib/phase-diagram/types.ts +144 -0
  152. package/dist/src/lib/phase-diagram/utils.ts +775 -0
  153. package/dist/src/lib/plot/AxisLabel.svelte +51 -0
  154. package/dist/src/lib/plot/BarPlot.svelte +2113 -0
  155. package/dist/src/lib/plot/BarPlotControls.svelte +66 -0
  156. package/dist/src/lib/plot/BinnedScatterPlot.svelte +1114 -0
  157. package/dist/src/lib/plot/ColorBar.svelte +721 -0
  158. package/dist/src/lib/plot/ColorScaleSelect.svelte +54 -0
  159. package/dist/src/lib/plot/ElementScatter.svelte +63 -0
  160. package/dist/src/lib/plot/FillArea.svelte +223 -0
  161. package/dist/src/lib/plot/Histogram.svelte +1558 -0
  162. package/dist/src/lib/plot/HistogramControls.svelte +212 -0
  163. package/dist/src/lib/plot/InteractiveAxisLabel.svelte +96 -0
  164. package/dist/src/lib/plot/Line.svelte +84 -0
  165. package/dist/src/lib/plot/PlotAxis.svelte +169 -0
  166. package/dist/src/lib/plot/PlotControls.svelte +537 -0
  167. package/dist/src/lib/plot/PlotLegend.svelte +569 -0
  168. package/dist/src/lib/plot/PlotTooltip.svelte +67 -0
  169. package/dist/src/lib/plot/PortalSelect.svelte +253 -0
  170. package/dist/src/lib/plot/ReferenceLine3D.svelte +156 -0
  171. package/dist/src/lib/plot/ReferencePlane.svelte +175 -0
  172. package/dist/src/lib/plot/ScatterPlot.svelte +2778 -0
  173. package/dist/src/lib/plot/ScatterPlot3D.svelte +529 -0
  174. package/dist/src/lib/plot/ScatterPlot3DControls.svelte +437 -0
  175. package/dist/src/lib/plot/ScatterPlot3DScene.svelte +912 -0
  176. package/dist/src/lib/plot/ScatterPlotControls.svelte +306 -0
  177. package/dist/src/lib/plot/ScatterPoint.svelte +182 -0
  178. package/dist/src/lib/plot/SpacegroupBarPlot.svelte +293 -0
  179. package/dist/src/lib/plot/Surface3D.svelte +197 -0
  180. package/dist/src/lib/plot/ZeroLines.svelte +97 -0
  181. package/dist/src/lib/plot/ZoomRect.svelte +23 -0
  182. package/dist/src/lib/plot/adaptive-density.ts +316 -0
  183. package/dist/src/lib/plot/auto-place.ts +184 -0
  184. package/dist/src/lib/plot/axis-utils.ts +122 -0
  185. package/dist/src/lib/plot/binned-scatter-types.ts +83 -0
  186. package/dist/src/lib/plot/data-cleaning.ts +1069 -0
  187. package/dist/src/lib/plot/data-transform.ts +69 -0
  188. package/dist/src/lib/plot/defaults.ts +9 -0
  189. package/dist/src/lib/plot/fill-utils.ts +494 -0
  190. package/dist/src/lib/plot/hover-lock.svelte.ts +60 -0
  191. package/dist/src/lib/plot/index.ts +53 -0
  192. package/dist/src/lib/plot/interactions.ts +119 -0
  193. package/dist/src/lib/plot/layout.ts +425 -0
  194. package/dist/src/lib/plot/reference-line.ts +426 -0
  195. package/dist/src/lib/plot/scales.ts +654 -0
  196. package/dist/src/lib/plot/svg.ts +23 -0
  197. package/dist/src/lib/plot/types.ts +1144 -0
  198. package/dist/src/lib/plot/utils/label-placement.ts +541 -0
  199. package/dist/src/lib/plot/utils/series-visibility.ts +140 -0
  200. package/dist/src/lib/plot/utils.ts +11 -0
  201. package/dist/src/lib/rdf/RdfPlot.svelte +247 -0
  202. package/dist/src/lib/rdf/calc-rdf.ts +167 -0
  203. package/dist/src/lib/rdf/index.ts +27 -0
  204. package/dist/src/lib/sanitize.ts +126 -0
  205. package/dist/src/lib/settings.ts +1479 -0
  206. package/dist/src/lib/spectral/Bands.svelte +1040 -0
  207. package/dist/src/lib/spectral/BandsAndDos.svelte +134 -0
  208. package/dist/src/lib/spectral/BrillouinBandsDos.svelte +252 -0
  209. package/dist/src/lib/spectral/Dos.svelte +697 -0
  210. package/dist/src/lib/spectral/helpers.ts +1381 -0
  211. package/dist/src/lib/spectral/index.ts +8 -0
  212. package/dist/src/lib/spectral/types.ts +112 -0
  213. package/dist/src/lib/state.svelte.ts +64 -0
  214. package/dist/src/lib/structure/Arrow.svelte +72 -0
  215. package/dist/src/lib/structure/AtomLegend.svelte +815 -0
  216. package/dist/src/lib/structure/Bond.svelte +140 -0
  217. package/dist/src/lib/structure/CanvasTooltip.svelte +33 -0
  218. package/dist/src/lib/structure/CellSelect.svelte +349 -0
  219. package/dist/src/lib/structure/Cylinder.svelte +45 -0
  220. package/dist/src/lib/structure/Lattice.svelte +196 -0
  221. package/dist/src/lib/structure/Structure.svelte +2248 -0
  222. package/dist/src/lib/structure/StructureControls.svelte +1273 -0
  223. package/dist/src/lib/structure/StructureExportPane.svelte +252 -0
  224. package/dist/src/lib/structure/StructureInfoPane.svelte +737 -0
  225. package/dist/src/lib/structure/StructureScene.svelte +2255 -0
  226. package/dist/src/lib/structure/atom-properties.ts +316 -0
  227. package/dist/src/lib/structure/bond-order-perception.ts +447 -0
  228. package/dist/src/lib/structure/bonding.ts +944 -0
  229. package/dist/src/lib/structure/export.ts +861 -0
  230. package/dist/src/lib/structure/index.ts +291 -0
  231. package/dist/src/lib/structure/label-placement.ts +130 -0
  232. package/dist/src/lib/structure/measure.ts +45 -0
  233. package/dist/src/lib/structure/parse.ts +1705 -0
  234. package/dist/src/lib/structure/partial-occupancy.ts +183 -0
  235. package/dist/src/lib/structure/pbc.ts +164 -0
  236. package/dist/src/lib/structure/supercell.ts +226 -0
  237. package/dist/src/lib/structure/validation.ts +11 -0
  238. package/dist/src/lib/symmetry/SymmetryStats.svelte +226 -0
  239. package/dist/src/lib/symmetry/WyckoffTable.svelte +120 -0
  240. package/dist/src/lib/symmetry/cell-transform.ts +118 -0
  241. package/dist/src/lib/symmetry/index.ts +348 -0
  242. package/dist/src/lib/symmetry/spacegroups.ts +404 -0
  243. package/dist/src/lib/table/HeatmapTable.svelte +1833 -0
  244. package/dist/src/lib/table/ToggleMenu.svelte +385 -0
  245. package/dist/src/lib/table/index.ts +139 -0
  246. package/dist/src/lib/theme/ThemeControl.svelte +53 -0
  247. package/dist/src/lib/theme/index.ts +107 -0
  248. package/dist/src/lib/theme/themes.mjs +297 -0
  249. package/dist/src/lib/time.ts +71 -0
  250. package/dist/src/lib/tooltip/TooltipContent.svelte +58 -0
  251. package/dist/src/lib/tooltip/index.ts +2 -0
  252. package/dist/src/lib/tooltip/types.ts +13 -0
  253. package/dist/src/lib/trajectory/Trajectory.svelte +1545 -0
  254. package/dist/src/lib/trajectory/TrajectoryError.svelte +128 -0
  255. package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +357 -0
  256. package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +313 -0
  257. package/dist/src/lib/trajectory/constants.ts +7 -0
  258. package/dist/src/lib/trajectory/extract.ts +196 -0
  259. package/dist/src/lib/trajectory/format-detect.ts +96 -0
  260. package/dist/src/lib/trajectory/frame-reader.ts +456 -0
  261. package/dist/src/lib/trajectory/helpers.ts +217 -0
  262. package/dist/src/lib/trajectory/index.ts +218 -0
  263. package/dist/src/lib/trajectory/parse/ase.ts +109 -0
  264. package/dist/src/lib/trajectory/parse/hdf5.ts +173 -0
  265. package/dist/src/lib/trajectory/parse/index.ts +411 -0
  266. package/dist/src/lib/trajectory/parse/lammps.ts +215 -0
  267. package/dist/src/lib/trajectory/parse/vasp.ts +102 -0
  268. package/dist/src/lib/trajectory/parse/xyz.ts +143 -0
  269. package/dist/src/lib/trajectory/plotting.ts +599 -0
  270. package/dist/src/lib/trajectory/types.ts +13 -0
  271. package/dist/src/lib/utils.ts +56 -0
  272. package/dist/src/lib/xrd/XrdPlot.svelte +615 -0
  273. package/dist/src/lib/xrd/broadening.ts +130 -0
  274. package/dist/src/lib/xrd/calc-xrd.ts +397 -0
  275. package/dist/src/lib/xrd/index.ts +38 -0
  276. package/dist/src/lib/xrd/parse.ts +858 -0
  277. package/dist/webview.js +29421 -0
  278. package/icon.png +0 -0
  279. package/license +1 -1
  280. package/matterviz-0.3.2.vsix +0 -0
  281. package/matterviz-0.3.4.vsix +0 -0
  282. package/matterviz-0.3.5.vsix +0 -0
  283. package/package.json +1460 -215
  284. package/readme.md +171 -98
  285. package/scripts/sync-config.ts +101 -0
  286. package/src/declarations.d.ts +2 -0
  287. package/src/extension.ts +972 -0
  288. package/src/node-io.ts +65 -0
  289. package/src/types.ts +17 -0
  290. package/src/webview/JsonBrowser.svelte +1079 -0
  291. package/src/webview/PlotPanel.svelte +346 -0
  292. package/src/webview/detect.ts +444 -0
  293. package/src/webview/main.ts +764 -0
  294. package/src/webview/plot-utils.ts +250 -0
  295. package/test-fixtures/all-viz-types.json.gz +0 -0
  296. package/test-fixtures/plot-demo-data.json.gz +0 -0
  297. package/tests/detect.test.ts +604 -0
  298. package/tests/extension.test.ts +2041 -0
  299. package/tests/node-io.test.ts +39 -0
  300. package/tests/plot-utils.test.ts +302 -0
  301. package/tests/vite-plugin-json-gz.test.ts +114 -0
  302. package/tests/vscode-mock.ts +18 -0
  303. package/tests/webview.test.ts +231 -0
  304. package/tsconfig.json +20 -0
  305. package/vite-plugin-json-gz.ts +29 -0
  306. package/vite.config.ts +34 -0
  307. package/vite.extension.config.ts +34 -0
  308. package/dist/EmptyState.svelte.d.ts +0 -9
  309. package/dist/FilePicker.svelte +0 -360
  310. package/dist/FilePicker.svelte.d.ts +0 -17
  311. package/dist/Icon.svelte.d.ts +0 -13
  312. package/dist/MillerIndexInput.svelte +0 -66
  313. package/dist/MillerIndexInput.svelte.d.ts +0 -7
  314. package/dist/api/mp.d.ts +0 -6
  315. package/dist/api/mp.js +0 -22
  316. package/dist/api/optimade.d.ts +0 -45
  317. package/dist/api/optimade.js +0 -135
  318. package/dist/brillouin/BrillouinZone.svelte +0 -546
  319. package/dist/brillouin/BrillouinZone.svelte.d.ts +0 -83
  320. package/dist/brillouin/BrillouinZoneControls.svelte +0 -144
  321. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +0 -17
  322. package/dist/brillouin/BrillouinZoneExportPane.svelte +0 -148
  323. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +0 -15
  324. package/dist/brillouin/BrillouinZoneInfoPane.svelte +0 -146
  325. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +0 -13
  326. package/dist/brillouin/BrillouinZoneScene.svelte +0 -476
  327. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +0 -48
  328. package/dist/brillouin/BrillouinZoneTooltip.svelte +0 -92
  329. package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +0 -8
  330. package/dist/brillouin/compute.d.ts +0 -17
  331. package/dist/brillouin/compute.js +0 -426
  332. package/dist/brillouin/index.d.ts +0 -8
  333. package/dist/brillouin/index.js +0 -8
  334. package/dist/brillouin/types.d.ts +0 -48
  335. package/dist/brillouin/types.js +0 -1
  336. package/dist/chempot-diagram/ChemPotDiagram.svelte +0 -327
  337. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +0 -13
  338. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +0 -847
  339. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +0 -16
  340. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +0 -3194
  341. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +0 -16
  342. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +0 -7
  343. package/dist/chempot-diagram/async-compute.svelte.d.ts +0 -3
  344. package/dist/chempot-diagram/async-compute.svelte.js +0 -78
  345. package/dist/chempot-diagram/chempot-worker.d.ts +0 -1
  346. package/dist/chempot-diagram/chempot-worker.js +0 -11
  347. package/dist/chempot-diagram/color.d.ts +0 -10
  348. package/dist/chempot-diagram/color.js +0 -32
  349. package/dist/chempot-diagram/compute.d.ts +0 -48
  350. package/dist/chempot-diagram/compute.js +0 -812
  351. package/dist/chempot-diagram/index.d.ts +0 -6
  352. package/dist/chempot-diagram/index.js +0 -6
  353. package/dist/chempot-diagram/pointer.d.ts +0 -16
  354. package/dist/chempot-diagram/pointer.js +0 -40
  355. package/dist/chempot-diagram/temperature.d.ts +0 -15
  356. package/dist/chempot-diagram/temperature.js +0 -36
  357. package/dist/chempot-diagram/types.d.ts +0 -86
  358. package/dist/chempot-diagram/types.js +0 -28
  359. package/dist/colors/index.d.ts +0 -47
  360. package/dist/colors/index.js +0 -203
  361. package/dist/composition/BarChart.svelte +0 -297
  362. package/dist/composition/BarChart.svelte.d.ts +0 -39
  363. package/dist/composition/BubbleChart.svelte +0 -218
  364. package/dist/composition/BubbleChart.svelte.d.ts +0 -28
  365. package/dist/composition/Composition.svelte +0 -164
  366. package/dist/composition/Composition.svelte.d.ts +0 -15
  367. package/dist/composition/Formula.svelte +0 -265
  368. package/dist/composition/Formula.svelte.d.ts +0 -19
  369. package/dist/composition/FormulaFilter.svelte +0 -1259
  370. package/dist/composition/FormulaFilter.svelte.d.ts +0 -51
  371. package/dist/composition/PieChart.svelte +0 -323
  372. package/dist/composition/PieChart.svelte.d.ts +0 -37
  373. package/dist/composition/format.d.ts +0 -15
  374. package/dist/composition/format.js +0 -109
  375. package/dist/composition/index.d.ts +0 -20
  376. package/dist/composition/index.js +0 -14
  377. package/dist/composition/parse.d.ts +0 -55
  378. package/dist/composition/parse.js +0 -459
  379. package/dist/constants.d.ts +0 -29
  380. package/dist/constants.js +0 -99
  381. package/dist/controls.d.ts +0 -14
  382. package/dist/controls.js +0 -30
  383. package/dist/convex-hull/ConvexHull.svelte +0 -157
  384. package/dist/convex-hull/ConvexHull.svelte.d.ts +0 -13
  385. package/dist/convex-hull/ConvexHull2D.svelte +0 -814
  386. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +0 -11
  387. package/dist/convex-hull/ConvexHull3D.svelte +0 -1790
  388. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +0 -8
  389. package/dist/convex-hull/ConvexHull4D.svelte +0 -1386
  390. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +0 -8
  391. package/dist/convex-hull/ConvexHullControls.svelte +0 -546
  392. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +0 -48
  393. package/dist/convex-hull/ConvexHullInfoPane.svelte +0 -122
  394. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +0 -18
  395. package/dist/convex-hull/ConvexHullStats.svelte +0 -922
  396. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +0 -15
  397. package/dist/convex-hull/ConvexHullTooltip.svelte +0 -131
  398. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +0 -33
  399. package/dist/convex-hull/GasPressureControls.svelte +0 -247
  400. package/dist/convex-hull/GasPressureControls.svelte.d.ts +0 -11
  401. package/dist/convex-hull/StructurePopup.svelte +0 -116
  402. package/dist/convex-hull/StructurePopup.svelte.d.ts +0 -18
  403. package/dist/convex-hull/TemperatureSlider.svelte +0 -137
  404. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +0 -8
  405. package/dist/convex-hull/barycentric-coords.d.ts +0 -18
  406. package/dist/convex-hull/barycentric-coords.js +0 -182
  407. package/dist/convex-hull/demo-temperature.d.ts +0 -6
  408. package/dist/convex-hull/demo-temperature.js +0 -40
  409. package/dist/convex-hull/gas-thermodynamics.d.ts +0 -16
  410. package/dist/convex-hull/gas-thermodynamics.js +0 -316
  411. package/dist/convex-hull/helpers.d.ts +0 -103
  412. package/dist/convex-hull/helpers.js +0 -689
  413. package/dist/convex-hull/index.d.ts +0 -118
  414. package/dist/convex-hull/index.js +0 -57
  415. package/dist/convex-hull/thermodynamics.d.ts +0 -66
  416. package/dist/convex-hull/thermodynamics.js +0 -1752
  417. package/dist/convex-hull/types.d.ts +0 -162
  418. package/dist/convex-hull/types.js +0 -36
  419. package/dist/coordination/CoordinationBarPlot.svelte +0 -311
  420. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +0 -30
  421. package/dist/coordination/calc-coordination.d.ts +0 -15
  422. package/dist/coordination/calc-coordination.js +0 -63
  423. package/dist/coordination/index.d.ts +0 -8
  424. package/dist/coordination/index.js +0 -7
  425. package/dist/element/BohrAtom.svelte +0 -147
  426. package/dist/element/BohrAtom.svelte.d.ts +0 -20
  427. package/dist/element/ElementHeading.svelte +0 -26
  428. package/dist/element/ElementHeading.svelte.d.ts +0 -8
  429. package/dist/element/ElementPhoto.svelte +0 -57
  430. package/dist/element/ElementPhoto.svelte.d.ts +0 -9
  431. package/dist/element/ElementStats.svelte +0 -80
  432. package/dist/element/ElementStats.svelte.d.ts +0 -8
  433. package/dist/element/ElementTile.svelte +0 -484
  434. package/dist/element/ElementTile.svelte.d.ts +0 -29
  435. package/dist/element/Nucleus.svelte.d.ts +0 -17
  436. package/dist/element/data.d.ts +0 -2
  437. package/dist/element/data.js +0 -2
  438. package/dist/element/data.json.gz.d.ts +0 -2
  439. package/dist/element/index.d.ts +0 -8
  440. package/dist/element/index.js +0 -8
  441. package/dist/element/types.d.ts +0 -57
  442. package/dist/element/types.js +0 -1
  443. package/dist/feedback/ClickFeedback.svelte +0 -58
  444. package/dist/feedback/ClickFeedback.svelte.d.ts +0 -12
  445. package/dist/feedback/DragOverlay.svelte +0 -42
  446. package/dist/feedback/DragOverlay.svelte.d.ts +0 -7
  447. package/dist/feedback/Spinner.svelte.d.ts +0 -7
  448. package/dist/feedback/StatusMessage.svelte.d.ts +0 -9
  449. package/dist/feedback/index.d.ts +0 -4
  450. package/dist/feedback/index.js +0 -4
  451. package/dist/fermi-surface/FermiSlice.svelte +0 -189
  452. package/dist/fermi-surface/FermiSlice.svelte.d.ts +0 -24
  453. package/dist/fermi-surface/FermiSurface.svelte +0 -600
  454. package/dist/fermi-surface/FermiSurface.svelte.d.ts +0 -83
  455. package/dist/fermi-surface/FermiSurfaceControls.svelte +0 -452
  456. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +0 -35
  457. package/dist/fermi-surface/FermiSurfaceScene.svelte +0 -792
  458. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +0 -50
  459. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
  460. package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +0 -8
  461. package/dist/fermi-surface/compute.d.ts +0 -5
  462. package/dist/fermi-surface/compute.js +0 -538
  463. package/dist/fermi-surface/constants.d.ts +0 -9
  464. package/dist/fermi-surface/constants.js +0 -27
  465. package/dist/fermi-surface/export.d.ts +0 -5
  466. package/dist/fermi-surface/export.js +0 -63
  467. package/dist/fermi-surface/index.d.ts +0 -12
  468. package/dist/fermi-surface/index.js +0 -13
  469. package/dist/fermi-surface/marching-cubes.d.ts +0 -2
  470. package/dist/fermi-surface/marching-cubes.js +0 -2
  471. package/dist/fermi-surface/parse.d.ts +0 -2
  472. package/dist/fermi-surface/parse.js +0 -495
  473. package/dist/fermi-surface/symmetry.d.ts +0 -3
  474. package/dist/fermi-surface/symmetry.js +0 -46
  475. package/dist/fermi-surface/types.d.ts +0 -113
  476. package/dist/fermi-surface/types.js +0 -4
  477. package/dist/heatmap-matrix/HeatmapMatrix.svelte +0 -1527
  478. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +0 -110
  479. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
  480. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +0 -30
  481. package/dist/heatmap-matrix/index.d.ts +0 -53
  482. package/dist/heatmap-matrix/index.js +0 -100
  483. package/dist/heatmap-matrix/shared.d.ts +0 -2
  484. package/dist/heatmap-matrix/shared.js +0 -4
  485. package/dist/icons.d.ts +0 -569
  486. package/dist/icons.js +0 -648
  487. package/dist/index.d.ts +0 -39
  488. package/dist/index.js +0 -39
  489. package/dist/io/decompress.d.ts +0 -10
  490. package/dist/io/decompress.js +0 -74
  491. package/dist/io/export.d.ts +0 -16
  492. package/dist/io/export.js +0 -316
  493. package/dist/io/fetch.d.ts +0 -5
  494. package/dist/io/fetch.js +0 -39
  495. package/dist/io/file-drop.d.ts +0 -7
  496. package/dist/io/file-drop.js +0 -43
  497. package/dist/io/index.d.ts +0 -7
  498. package/dist/io/index.js +0 -7
  499. package/dist/io/is-binary.d.ts +0 -1
  500. package/dist/io/is-binary.js +0 -20
  501. package/dist/io/types.d.ts +0 -8
  502. package/dist/io/types.js +0 -1
  503. package/dist/io/url-drop.d.ts +0 -2
  504. package/dist/io/url-drop.js +0 -117
  505. package/dist/isosurface/Isosurface.svelte +0 -285
  506. package/dist/isosurface/Isosurface.svelte.d.ts +0 -8
  507. package/dist/isosurface/IsosurfaceControls.svelte +0 -291
  508. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +0 -9
  509. package/dist/isosurface/index.d.ts +0 -5
  510. package/dist/isosurface/index.js +0 -6
  511. package/dist/isosurface/parse.d.ts +0 -6
  512. package/dist/isosurface/parse.js +0 -553
  513. package/dist/isosurface/slice.d.ts +0 -11
  514. package/dist/isosurface/slice.js +0 -140
  515. package/dist/isosurface/types.d.ts +0 -56
  516. package/dist/isosurface/types.js +0 -227
  517. package/dist/labels.d.ts +0 -53
  518. package/dist/labels.js +0 -274
  519. package/dist/layout/FullscreenToggle.svelte +0 -50
  520. package/dist/layout/FullscreenToggle.svelte.d.ts +0 -7
  521. package/dist/layout/InfoCard.svelte +0 -120
  522. package/dist/layout/InfoCard.svelte.d.ts +0 -21
  523. package/dist/layout/InfoTag.svelte +0 -183
  524. package/dist/layout/InfoTag.svelte.d.ts +0 -19
  525. package/dist/layout/PropertyFilter.svelte +0 -244
  526. package/dist/layout/PropertyFilter.svelte.d.ts +0 -24
  527. package/dist/layout/SettingsSection.svelte +0 -148
  528. package/dist/layout/SettingsSection.svelte.d.ts +0 -17
  529. package/dist/layout/SubpageGrid.svelte +0 -82
  530. package/dist/layout/SubpageGrid.svelte.d.ts +0 -14
  531. package/dist/layout/fullscreen.d.ts +0 -9
  532. package/dist/layout/fullscreen.js +0 -53
  533. package/dist/layout/index.d.ts +0 -10
  534. package/dist/layout/index.js +0 -8
  535. package/dist/layout/json-tree/JsonNode.svelte +0 -548
  536. package/dist/layout/json-tree/JsonNode.svelte.d.ts +0 -11
  537. package/dist/layout/json-tree/JsonTree.svelte +0 -1222
  538. package/dist/layout/json-tree/JsonTree.svelte.d.ts +0 -6
  539. package/dist/layout/json-tree/JsonValue.svelte +0 -334
  540. package/dist/layout/json-tree/JsonValue.svelte.d.ts +0 -9
  541. package/dist/layout/json-tree/index.d.ts +0 -3
  542. package/dist/layout/json-tree/index.js +0 -3
  543. package/dist/layout/json-tree/types.d.ts +0 -73
  544. package/dist/layout/json-tree/types.js +0 -3
  545. package/dist/layout/json-tree/utils.d.ts +0 -29
  546. package/dist/layout/json-tree/utils.js +0 -649
  547. package/dist/marching-cubes.d.ts +0 -14
  548. package/dist/marching-cubes.js +0 -542
  549. package/dist/math.d.ts +0 -91
  550. package/dist/math.js +0 -896
  551. package/dist/overlays/ContextMenu.svelte +0 -162
  552. package/dist/overlays/ContextMenu.svelte.d.ts +0 -25
  553. package/dist/overlays/CopyButton.svelte +0 -45
  554. package/dist/overlays/CopyButton.svelte.d.ts +0 -8
  555. package/dist/overlays/DraggablePane.svelte +0 -564
  556. package/dist/overlays/DraggablePane.svelte.d.ts +0 -36
  557. package/dist/overlays/InfoPaneCards.svelte +0 -149
  558. package/dist/overlays/InfoPaneCards.svelte.d.ts +0 -22
  559. package/dist/overlays/index.d.ts +0 -2
  560. package/dist/overlays/index.js +0 -2
  561. package/dist/periodic-table/PeriodicTable.svelte +0 -469
  562. package/dist/periodic-table/PeriodicTable.svelte.d.ts +0 -55
  563. package/dist/periodic-table/PeriodicTableControls.svelte +0 -557
  564. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +0 -24
  565. package/dist/periodic-table/PropertySelect.svelte +0 -37
  566. package/dist/periodic-table/PropertySelect.svelte.d.ts +0 -13
  567. package/dist/periodic-table/TableInset.svelte.d.ts +0 -9
  568. package/dist/periodic-table/index.d.ts +0 -10
  569. package/dist/periodic-table/index.js +0 -4
  570. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1084
  571. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +0 -44
  572. package/dist/phase-diagram/PhaseDiagramControls.svelte +0 -449
  573. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +0 -30
  574. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
  575. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +0 -15
  576. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +0 -192
  577. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +0 -19
  578. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +0 -392
  579. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +0 -16
  580. package/dist/phase-diagram/TdbInfoPanel.svelte +0 -203
  581. package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +0 -12
  582. package/dist/phase-diagram/build-diagram.d.ts +0 -11
  583. package/dist/phase-diagram/build-diagram.js +0 -167
  584. package/dist/phase-diagram/colors.d.ts +0 -35
  585. package/dist/phase-diagram/colors.js +0 -51
  586. package/dist/phase-diagram/diagram-input.d.ts +0 -33
  587. package/dist/phase-diagram/diagram-input.js +0 -3
  588. package/dist/phase-diagram/index.d.ts +0 -13
  589. package/dist/phase-diagram/index.js +0 -13
  590. package/dist/phase-diagram/parse.d.ts +0 -55
  591. package/dist/phase-diagram/parse.js +0 -276
  592. package/dist/phase-diagram/svg-to-diagram.d.ts +0 -2
  593. package/dist/phase-diagram/svg-to-diagram.js +0 -867
  594. package/dist/phase-diagram/types.d.ts +0 -99
  595. package/dist/phase-diagram/types.js +0 -1
  596. package/dist/phase-diagram/utils.d.ts +0 -118
  597. package/dist/phase-diagram/utils.js +0 -606
  598. package/dist/plot/AxisLabel.svelte +0 -51
  599. package/dist/plot/AxisLabel.svelte.d.ts +0 -16
  600. package/dist/plot/BarPlot.svelte +0 -2265
  601. package/dist/plot/BarPlot.svelte.d.ts +0 -83
  602. package/dist/plot/BarPlotControls.svelte +0 -66
  603. package/dist/plot/BarPlotControls.svelte.d.ts +0 -18
  604. package/dist/plot/ColorBar.svelte +0 -719
  605. package/dist/plot/ColorBar.svelte.d.ts +0 -31
  606. package/dist/plot/ColorScaleSelect.svelte +0 -54
  607. package/dist/plot/ColorScaleSelect.svelte.d.ts +0 -15
  608. package/dist/plot/ElementScatter.svelte +0 -63
  609. package/dist/plot/ElementScatter.svelte.d.ts +0 -14
  610. package/dist/plot/FillArea.svelte +0 -225
  611. package/dist/plot/FillArea.svelte.d.ts +0 -21
  612. package/dist/plot/Histogram.svelte +0 -1672
  613. package/dist/plot/Histogram.svelte.d.ts +0 -50
  614. package/dist/plot/HistogramControls.svelte +0 -212
  615. package/dist/plot/HistogramControls.svelte.d.ts +0 -22
  616. package/dist/plot/InteractiveAxisLabel.svelte +0 -94
  617. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +0 -14
  618. package/dist/plot/Line.svelte +0 -84
  619. package/dist/plot/Line.svelte.d.ts +0 -15
  620. package/dist/plot/PlotControls.svelte +0 -537
  621. package/dist/plot/PlotControls.svelte.d.ts +0 -4
  622. package/dist/plot/PlotLegend.svelte +0 -569
  623. package/dist/plot/PlotLegend.svelte.d.ts +0 -29
  624. package/dist/plot/PlotTooltip.svelte +0 -67
  625. package/dist/plot/PlotTooltip.svelte.d.ts +0 -17
  626. package/dist/plot/PortalSelect.svelte +0 -253
  627. package/dist/plot/PortalSelect.svelte.d.ts +0 -16
  628. package/dist/plot/ReferenceLine.svelte.d.ts +0 -20
  629. package/dist/plot/ReferenceLine3D.svelte +0 -154
  630. package/dist/plot/ReferenceLine3D.svelte.d.ts +0 -14
  631. package/dist/plot/ReferencePlane.svelte +0 -178
  632. package/dist/plot/ReferencePlane.svelte.d.ts +0 -14
  633. package/dist/plot/ScatterPlot.svelte +0 -2845
  634. package/dist/plot/ScatterPlot.svelte.d.ts +0 -93
  635. package/dist/plot/ScatterPlot3D.svelte +0 -502
  636. package/dist/plot/ScatterPlot3D.svelte.d.ts +0 -94
  637. package/dist/plot/ScatterPlot3DControls.svelte +0 -437
  638. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +0 -20
  639. package/dist/plot/ScatterPlot3DScene.svelte +0 -912
  640. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +0 -74
  641. package/dist/plot/ScatterPlotControls.svelte +0 -307
  642. package/dist/plot/ScatterPlotControls.svelte.d.ts +0 -17
  643. package/dist/plot/ScatterPoint.svelte +0 -191
  644. package/dist/plot/ScatterPoint.svelte.d.ts +0 -21
  645. package/dist/plot/SpacegroupBarPlot.svelte +0 -293
  646. package/dist/plot/SpacegroupBarPlot.svelte.d.ts +0 -9
  647. package/dist/plot/Surface3D.svelte +0 -200
  648. package/dist/plot/Surface3D.svelte.d.ts +0 -13
  649. package/dist/plot/ZeroLines.svelte +0 -96
  650. package/dist/plot/ZeroLines.svelte.d.ts +0 -32
  651. package/dist/plot/ZoomRect.svelte +0 -23
  652. package/dist/plot/ZoomRect.svelte.d.ts +0 -8
  653. package/dist/plot/axis-utils.d.ts +0 -19
  654. package/dist/plot/axis-utils.js +0 -80
  655. package/dist/plot/data-cleaning.d.ts +0 -37
  656. package/dist/plot/data-cleaning.js +0 -855
  657. package/dist/plot/data-transform.d.ts +0 -16
  658. package/dist/plot/data-transform.js +0 -45
  659. package/dist/plot/defaults.d.ts +0 -19
  660. package/dist/plot/defaults.js +0 -9
  661. package/dist/plot/fill-utils.d.ts +0 -51
  662. package/dist/plot/fill-utils.js +0 -337
  663. package/dist/plot/hover-lock.svelte.d.ts +0 -14
  664. package/dist/plot/hover-lock.svelte.js +0 -46
  665. package/dist/plot/index.d.ts +0 -37
  666. package/dist/plot/index.js +0 -37
  667. package/dist/plot/interactions.d.ts +0 -12
  668. package/dist/plot/interactions.js +0 -100
  669. package/dist/plot/layout.d.ts +0 -60
  670. package/dist/plot/layout.js +0 -230
  671. package/dist/plot/reference-line.d.ts +0 -60
  672. package/dist/plot/reference-line.js +0 -316
  673. package/dist/plot/scales.d.ts +0 -48
  674. package/dist/plot/scales.js +0 -484
  675. package/dist/plot/svg.d.ts +0 -1
  676. package/dist/plot/svg.js +0 -11
  677. package/dist/plot/types.d.ts +0 -859
  678. package/dist/plot/types.js +0 -103
  679. package/dist/plot/utils/label-placement.d.ts +0 -47
  680. package/dist/plot/utils/label-placement.js +0 -256
  681. package/dist/plot/utils/series-visibility.d.ts +0 -9
  682. package/dist/plot/utils/series-visibility.js +0 -67
  683. package/dist/plot/utils.d.ts +0 -1
  684. package/dist/plot/utils.js +0 -14
  685. package/dist/rdf/RdfPlot.svelte +0 -247
  686. package/dist/rdf/RdfPlot.svelte.d.ts +0 -27
  687. package/dist/rdf/calc-rdf.d.ts +0 -4
  688. package/dist/rdf/calc-rdf.js +0 -111
  689. package/dist/rdf/index.d.ts +0 -23
  690. package/dist/rdf/index.js +0 -2
  691. package/dist/sanitize.d.ts +0 -4
  692. package/dist/sanitize.js +0 -114
  693. package/dist/settings.d.ts +0 -255
  694. package/dist/settings.js +0 -1132
  695. package/dist/spectral/Bands.svelte +0 -1040
  696. package/dist/spectral/Bands.svelte.d.ts +0 -40
  697. package/dist/spectral/BandsAndDos.svelte +0 -128
  698. package/dist/spectral/BandsAndDos.svelte.d.ts +0 -18
  699. package/dist/spectral/BrillouinBandsDos.svelte +0 -248
  700. package/dist/spectral/BrillouinBandsDos.svelte.d.ts +0 -20
  701. package/dist/spectral/Dos.svelte +0 -697
  702. package/dist/spectral/Dos.svelte.d.ts +0 -29
  703. package/dist/spectral/helpers.d.ts +0 -117
  704. package/dist/spectral/helpers.js +0 -1023
  705. package/dist/spectral/index.d.ts +0 -6
  706. package/dist/spectral/index.js +0 -7
  707. package/dist/spectral/types.d.ts +0 -84
  708. package/dist/spectral/types.js +0 -2
  709. package/dist/state.svelte.d.ts +0 -25
  710. package/dist/state.svelte.js +0 -45
  711. package/dist/structure/Arrow.svelte +0 -72
  712. package/dist/structure/Arrow.svelte.d.ts +0 -15
  713. package/dist/structure/AtomLegend.svelte +0 -798
  714. package/dist/structure/AtomLegend.svelte.d.ts +0 -34
  715. package/dist/structure/Bond.svelte +0 -140
  716. package/dist/structure/Bond.svelte.d.ts +0 -9
  717. package/dist/structure/CanvasTooltip.svelte +0 -33
  718. package/dist/structure/CanvasTooltip.svelte.d.ts +0 -12
  719. package/dist/structure/CellSelect.svelte +0 -351
  720. package/dist/structure/CellSelect.svelte.d.ts +0 -13
  721. package/dist/structure/Cylinder.svelte +0 -45
  722. package/dist/structure/Cylinder.svelte.d.ts +0 -10
  723. package/dist/structure/Lattice.svelte +0 -196
  724. package/dist/structure/Lattice.svelte.d.ts +0 -17
  725. package/dist/structure/Structure.svelte +0 -1999
  726. package/dist/structure/Structure.svelte.d.ts +0 -87
  727. package/dist/structure/StructureControls.svelte +0 -1298
  728. package/dist/structure/StructureControls.svelte.d.ts +0 -31
  729. package/dist/structure/StructureExportPane.svelte +0 -251
  730. package/dist/structure/StructureExportPane.svelte.d.ts +0 -17
  731. package/dist/structure/StructureInfoPane.svelte +0 -735
  732. package/dist/structure/StructureInfoPane.svelte.d.ts +0 -19
  733. package/dist/structure/StructureScene.svelte +0 -1905
  734. package/dist/structure/StructureScene.svelte.d.ts +0 -108
  735. package/dist/structure/atom-properties.d.ts +0 -37
  736. package/dist/structure/atom-properties.js +0 -200
  737. package/dist/structure/bond-order-perception.d.ts +0 -13
  738. package/dist/structure/bond-order-perception.js +0 -367
  739. package/dist/structure/bonding.d.ts +0 -42
  740. package/dist/structure/bonding.js +0 -525
  741. package/dist/structure/export.d.ts +0 -20
  742. package/dist/structure/export.js +0 -727
  743. package/dist/structure/index.d.ts +0 -125
  744. package/dist/structure/index.js +0 -171
  745. package/dist/structure/label-placement.d.ts +0 -14
  746. package/dist/structure/label-placement.js +0 -72
  747. package/dist/structure/measure.d.ts +0 -6
  748. package/dist/structure/measure.js +0 -29
  749. package/dist/structure/parse.d.ts +0 -66
  750. package/dist/structure/parse.js +0 -1363
  751. package/dist/structure/partial-occupancy.d.ts +0 -25
  752. package/dist/structure/partial-occupancy.js +0 -99
  753. package/dist/structure/pbc.d.ts +0 -9
  754. package/dist/structure/pbc.js +0 -123
  755. package/dist/structure/supercell.d.ts +0 -8
  756. package/dist/structure/supercell.js +0 -170
  757. package/dist/structure/validation.d.ts +0 -2
  758. package/dist/structure/validation.js +0 -10
  759. package/dist/symmetry/SymmetryStats.svelte +0 -226
  760. package/dist/symmetry/SymmetryStats.svelte.d.ts +0 -21
  761. package/dist/symmetry/WyckoffTable.svelte +0 -113
  762. package/dist/symmetry/WyckoffTable.svelte.d.ts +0 -11
  763. package/dist/symmetry/cell-transform.d.ts +0 -12
  764. package/dist/symmetry/cell-transform.js +0 -91
  765. package/dist/symmetry/index.d.ts +0 -43
  766. package/dist/symmetry/index.js +0 -229
  767. package/dist/symmetry/spacegroups.d.ts +0 -9
  768. package/dist/symmetry/spacegroups.js +0 -394
  769. package/dist/table/HeatmapTable.svelte +0 -1854
  770. package/dist/table/HeatmapTable.svelte.d.ts +0 -49
  771. package/dist/table/ToggleMenu.svelte +0 -376
  772. package/dist/table/ToggleMenu.svelte.d.ts +0 -11
  773. package/dist/table/index.d.ts +0 -74
  774. package/dist/table/index.js +0 -38
  775. package/dist/theme/ThemeControl.svelte +0 -53
  776. package/dist/theme/ThemeControl.svelte.d.ts +0 -9
  777. package/dist/theme/index.d.ts +0 -29
  778. package/dist/theme/index.js +0 -79
  779. package/dist/theme/themes.mjs +0 -285
  780. package/dist/time.d.ts +0 -4
  781. package/dist/time.js +0 -70
  782. package/dist/tooltip/TooltipContent.svelte +0 -58
  783. package/dist/tooltip/TooltipContent.svelte.d.ts +0 -31
  784. package/dist/tooltip/index.d.ts +0 -2
  785. package/dist/tooltip/index.js +0 -2
  786. package/dist/tooltip/types.d.ts +0 -8
  787. package/dist/tooltip/types.js +0 -1
  788. package/dist/trajectory/Trajectory.svelte +0 -1517
  789. package/dist/trajectory/Trajectory.svelte.d.ts +0 -77
  790. package/dist/trajectory/TrajectoryError.svelte +0 -128
  791. package/dist/trajectory/TrajectoryError.svelte.d.ts +0 -13
  792. package/dist/trajectory/TrajectoryExportPane.svelte +0 -357
  793. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +0 -17
  794. package/dist/trajectory/TrajectoryInfoPane.svelte +0 -313
  795. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +0 -17
  796. package/dist/trajectory/constants.d.ts +0 -6
  797. package/dist/trajectory/constants.js +0 -7
  798. package/dist/trajectory/extract.d.ts +0 -5
  799. package/dist/trajectory/extract.js +0 -162
  800. package/dist/trajectory/format-detect.d.ts +0 -9
  801. package/dist/trajectory/format-detect.js +0 -76
  802. package/dist/trajectory/frame-reader.d.ts +0 -17
  803. package/dist/trajectory/frame-reader.js +0 -332
  804. package/dist/trajectory/helpers.d.ts +0 -14
  805. package/dist/trajectory/helpers.js +0 -172
  806. package/dist/trajectory/index.d.ts +0 -63
  807. package/dist/trajectory/index.js +0 -126
  808. package/dist/trajectory/parse/ase.d.ts +0 -2
  809. package/dist/trajectory/parse/ase.js +0 -73
  810. package/dist/trajectory/parse/hdf5.d.ts +0 -2
  811. package/dist/trajectory/parse/hdf5.js +0 -127
  812. package/dist/trajectory/parse/index.d.ts +0 -12
  813. package/dist/trajectory/parse/index.js +0 -299
  814. package/dist/trajectory/parse/lammps.d.ts +0 -5
  815. package/dist/trajectory/parse/lammps.js +0 -179
  816. package/dist/trajectory/parse/vasp.d.ts +0 -2
  817. package/dist/trajectory/parse/vasp.js +0 -68
  818. package/dist/trajectory/parse/xyz.d.ts +0 -2
  819. package/dist/trajectory/parse/xyz.js +0 -110
  820. package/dist/trajectory/plotting.d.ts +0 -28
  821. package/dist/trajectory/plotting.js +0 -423
  822. package/dist/trajectory/types.d.ts +0 -11
  823. package/dist/trajectory/types.js +0 -1
  824. package/dist/utils.d.ts +0 -5
  825. package/dist/utils.js +0 -36
  826. package/dist/xrd/XrdPlot.svelte +0 -615
  827. package/dist/xrd/XrdPlot.svelte.d.ts +0 -28
  828. package/dist/xrd/broadening.d.ts +0 -20
  829. package/dist/xrd/broadening.js +0 -97
  830. package/dist/xrd/calc-xrd.d.ts +0 -37
  831. package/dist/xrd/calc-xrd.js +0 -337
  832. package/dist/xrd/index.d.ts +0 -37
  833. package/dist/xrd/index.js +0 -4
  834. package/dist/xrd/parse.d.ts +0 -13
  835. package/dist/xrd/parse.js +0 -749
  836. /package/dist/{EmptyState.svelte → src/lib/EmptyState.svelte} +0 -0
  837. /package/dist/{Icon.svelte → src/lib/Icon.svelte} +0 -0
  838. /package/dist/{app.css → src/lib/app.css} +0 -0
  839. /package/dist/{chempot-diagram → src/lib/chempot-diagram}/ChemPotScene3D.svelte +0 -0
  840. /package/dist/{colors → src/lib/colors}/alloy-colors.json +0 -0
  841. /package/dist/{colors → src/lib/colors}/dark-mode-colors.json +0 -0
  842. /package/dist/{colors → src/lib/colors}/jmol-colors.json +0 -0
  843. /package/dist/{colors → src/lib/colors}/muted-colors.json +0 -0
  844. /package/dist/{colors → src/lib/colors}/pastel-colors.json +0 -0
  845. /package/dist/{colors → src/lib/colors}/vesta-colors.json +0 -0
  846. /package/dist/{element → src/lib/element}/Nucleus.svelte +0 -0
  847. /package/dist/{element → src/lib/element}/data.json +0 -0
  848. /package/dist/{element → src/lib/element}/data.json.gz +0 -0
  849. /package/dist/{element → src/lib/element}/data.schema.json +0 -0
  850. /package/dist/{element-image-urls.json → src/lib/element-image-urls.json} +0 -0
  851. /package/dist/{feedback → src/lib/feedback}/Spinner.svelte +0 -0
  852. /package/dist/{feedback → src/lib/feedback}/StatusMessage.svelte +0 -0
  853. /package/dist/{periodic-table → src/lib/periodic-table}/TableInset.svelte +0 -0
  854. /package/dist/{plot → src/lib/plot}/ReferenceLine.svelte +0 -0
  855. /package/dist/{xrd → src/lib/xrd}/atomic_scattering_params.json +0 -0
@@ -1,2845 +0,0 @@
1
- <script
2
- lang="ts"
3
- generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
4
- >
5
- import type { D3ColorSchemeName, D3InterpolateName } from '../colors'
6
- import type { D3SymbolName } from '../labels'
7
- import { format_value, symbol_names } from '../labels'
8
- import { sanitize_html } from '../sanitize'
9
- import { FullscreenToggle, set_fullscreen_bg } from '../layout'
10
- import type { Vec2 } from '../math'
11
- import type {
12
- AxisLoadError,
13
- BasePlotProps,
14
- ControlsConfig,
15
- DataLoaderFn,
16
- DataSeries,
17
- ErrorBand,
18
- FillHandlerEvent,
19
- FillRegion,
20
- HoverConfig,
21
- InitialRanges,
22
- InternalPoint,
23
- LabelPlacementConfig,
24
- LegendConfig,
25
- PanConfig,
26
- PlotConfig,
27
- Point,
28
- RefLine,
29
- RefLineEvent,
30
- ScaleType,
31
- ScatterHandlerEvent,
32
- ScatterHandlerProps,
33
- Sides,
34
- StyleOverrides,
35
- UserContentProps,
36
- XyObj,
37
- } from './'
38
- import {
39
- AxisLabel,
40
- ColorBar,
41
- compute_element_placement,
42
- FillArea,
43
- get_tick_label,
44
- Line,
45
- PlotLegend,
46
- PlotTooltip,
47
- ReferenceLine,
48
- ScatterPlotControls,
49
- ScatterPoint,
50
- ZeroLines,
51
- ZoomRect,
52
- } from './'
53
- import type { AxisChangeState } from './axis-utils'
54
- import { create_axis_change_handler } from './axis-utils'
55
- import {
56
- get_series_color,
57
- get_series_symbol,
58
- process_prop,
59
- } from './data-transform'
60
- import { AXIS_DEFAULTS } from './defaults'
61
- import {
62
- create_dimension_tracker,
63
- create_hover_lock,
64
- } from './hover-lock.svelte'
65
- import {
66
- DEFAULT_GRID_STYLE,
67
- DEFAULT_MARKERS,
68
- get_scale_type_name,
69
- is_time_scale,
70
- } from './types'
71
- import { compute_label_positions } from './utils/label-placement'
72
- import {
73
- handle_legend_double_click,
74
- toggle_group_visibility,
75
- toggle_series_visibility,
76
- } from './utils/series-visibility'
77
- import { DEFAULTS } from '../settings'
78
- import { extent } from 'd3-array'
79
- import { scaleTime } from 'd3-scale'
80
- import type { ComponentProps, Snippet } from 'svelte'
81
- import { untrack } from 'svelte'
82
- import type { HTMLAttributes } from 'svelte/elements'
83
- import { Tween, type TweenOptions } from 'svelte/motion'
84
- import { SvelteSet } from 'svelte/reactivity'
85
- import type { FillPathPoint } from './fill-utils'
86
- import {
87
- apply_range_constraints,
88
- apply_where_condition,
89
- clamp_for_log_scale,
90
- convert_error_band_to_fill_region,
91
- generate_fill_path,
92
- is_fill_gradient,
93
- resolve_boundary,
94
- } from './fill-utils'
95
- import {
96
- expand_range_if_needed,
97
- get_relative_coords,
98
- normalize_y2_sync,
99
- pan_range,
100
- PINCH_ZOOM_THRESHOLD,
101
- pixels_to_data_delta,
102
- sync_y2_range,
103
- } from './interactions'
104
- import type { Rect } from './layout'
105
- import {
106
- calc_auto_padding,
107
- constrain_tooltip_position,
108
- filter_padding,
109
- LABEL_GAP_DEFAULT,
110
- measure_max_tick_width,
111
- } from './layout'
112
- import type { IndexedRefLine } from './reference-line'
113
- import { group_ref_lines_by_z, index_ref_lines } from './reference-line'
114
- import {
115
- create_color_scale,
116
- create_scale,
117
- create_size_scale,
118
- generate_ticks,
119
- get_nice_data_range,
120
- } from './scales'
121
-
122
- let {
123
- series = $bindable([]),
124
- x_axis = $bindable({}),
125
- x2_axis = $bindable({}),
126
- y_axis = $bindable({}),
127
- y2_axis = $bindable({}),
128
- display = $bindable(DEFAULTS.scatter.display),
129
- styles: styles_init = {},
130
- controls: controls_init = {},
131
- padding = {},
132
- range_padding = 0.05,
133
- current_x_value = null,
134
- tooltip_point = $bindable(null),
135
- selected_point = null,
136
- hovered = $bindable(false),
137
- tooltip,
138
- user_content,
139
- change = () => {},
140
- color_scale = {
141
- type: `linear`,
142
- scheme: `interpolateViridis`,
143
- value_range: undefined,
144
- },
145
- color_bar = {},
146
- size_scale = { type: `linear`, radius_range: [2, 10], value_range: undefined },
147
- label_placement_config = {},
148
- hover_config = {},
149
- legend = {},
150
- point_tween,
151
- line_tween,
152
- point_events,
153
- on_point_click,
154
- on_point_hover,
155
- fill_regions = $bindable([]),
156
- error_bands = [],
157
- on_fill_click,
158
- on_fill_hover,
159
- ref_lines = $bindable([]),
160
- on_ref_line_click,
161
- on_ref_line_hover,
162
- selected_series_idx = $bindable(0),
163
- wrapper = $bindable(),
164
- fullscreen = $bindable(false),
165
- fullscreen_toggle = true,
166
- children,
167
- header_controls,
168
- controls_extra,
169
- data_loader,
170
- on_axis_change,
171
- on_error,
172
- pan = {},
173
- ...rest
174
- }: HTMLAttributes<HTMLDivElement> & Omit<BasePlotProps, `change`> & PlotConfig & {
175
- series?: DataSeries<Metadata>[]
176
- styles?: StyleOverrides
177
- controls?: ControlsConfig
178
- current_x_value?: number | null
179
- tooltip_point?: InternalPoint<Metadata> | null
180
- selected_point?: { series_idx: number; point_idx: number } | null
181
- tooltip?: Snippet<[ScatterHandlerProps<Metadata>]>
182
- user_content?: Snippet<[UserContentProps]>
183
- header_controls?: Snippet<
184
- [{ height: number; width: number; fullscreen: boolean }]
185
- >
186
- controls_extra?: Snippet<
187
- [
188
- & { styles: StyleOverrides; selected_series_idx: number }
189
- & Required<PlotConfig>,
190
- ]
191
- >
192
- change?: (
193
- data: (Point<Metadata> & { series: DataSeries<Metadata> }) | null,
194
- ) => void
195
- color_scale?: {
196
- type?: ScaleType
197
- scheme?: D3ColorSchemeName | D3InterpolateName
198
- value_range?: [number, number]
199
- } | D3InterpolateName
200
- size_scale?: {
201
- type?: ScaleType
202
- radius_range?: [number, number]
203
- value_range?: [number, number]
204
- }
205
- color_bar?:
206
- | (ComponentProps<typeof ColorBar> & {
207
- margin?: number | Sides
208
- tween?: TweenOptions<XyObj>
209
- responsive?: boolean // Allow colorbar to reposition if density changes (default: false)
210
- })
211
- | null
212
- label_placement_config?: Partial<LabelPlacementConfig>
213
- hover_config?: Partial<HoverConfig>
214
- legend?: LegendConfig | null
215
- point_tween?: TweenOptions<XyObj>
216
- line_tween?: TweenOptions<string>
217
- point_events?: Record<
218
- string,
219
- (payload: { point: InternalPoint<Metadata>; event: Event }) => void
220
- >
221
- on_point_click?: (data: ScatterHandlerEvent<Metadata>) => void
222
- on_point_hover?: (data: ScatterHandlerEvent<Metadata> | null) => void
223
- fill_regions?: FillRegion[] // Bindable for legend toggle support
224
- error_bands?: ErrorBand[]
225
- on_fill_click?: (event: FillHandlerEvent) => void
226
- on_fill_hover?: (event: FillHandlerEvent | null) => void
227
- ref_lines?: RefLine[] // Bindable for legend toggle support
228
- on_ref_line_click?: (event: RefLineEvent) => void
229
- on_ref_line_hover?: (event: RefLineEvent | null) => void
230
- selected_series_idx?: number
231
- wrapper?: HTMLDivElement
232
- // Interactive axis props
233
- data_loader?: DataLoaderFn<Metadata>
234
- on_axis_change?: (
235
- axis: `x` | `x2` | `y` | `y2`,
236
- key: string,
237
- new_series: DataSeries<Metadata>[],
238
- ) => void
239
- on_error?: (error: AxisLoadError) => void
240
- pan?: PanConfig
241
- } = $props()
242
-
243
- // Merged axis/display values with defaults (use $derived to avoid breaking $bindable)
244
- const final_x_axis = $derived({
245
- ...AXIS_DEFAULTS,
246
- label_shift: { x: 0, y: -40 }, // x-axis needs different label position
247
- ...x_axis,
248
- })
249
- const final_y_axis = $derived({ ...AXIS_DEFAULTS, ...y_axis })
250
- const final_x2_axis = $derived({
251
- ...AXIS_DEFAULTS,
252
- label_shift: { x: 0, y: 40 }, // x2-axis label above top edge
253
- ...x2_axis,
254
- })
255
- const final_y2_axis = $derived({ ...AXIS_DEFAULTS, ...y2_axis })
256
- // Cache time-axis check — used in ~10 places for scale/tick/tooltip logic
257
- let is_time_x = $derived(
258
- is_time_scale(final_x_axis.scale_type, final_x_axis.format),
259
- )
260
- let is_time_x2 = $derived(
261
- is_time_scale(final_x2_axis.scale_type, final_x2_axis.format),
262
- )
263
- const final_display = $derived({ ...DEFAULTS.scatter.display, ...display })
264
- // Local state for styles (initialized from prop, owned by this component for controls)
265
- // Using $state because styles has bindings in ScatterPlotControls
266
- // untrack() explicitly captures initial prop value (intentional - props provide initial config)
267
- let styles = $state(untrack(() => ({
268
- show_points: DEFAULTS.scatter.show_points,
269
- show_lines: DEFAULTS.scatter.show_lines,
270
- point: { ...DEFAULTS.scatter.point, ...styles_init?.point },
271
- line: { ...DEFAULTS.scatter.line, ...styles_init?.line },
272
- ...styles_init,
273
- })))
274
- let controls = $derived({ show: true, open: false, ...controls_init })
275
-
276
- let [width, height] = $state([0, 0])
277
- let svg_element: SVGElement | null = $state(null) // Bind the SVG element
278
- let svg_bounding_box: DOMRect | null = $state(null) // Store SVG bounds during drag
279
-
280
- // Track which specific control properties user has modified
281
- let touched = new SvelteSet<string>()
282
-
283
- // Unique component ID to avoid clipPath conflicts between multiple instances
284
- let component_id = $state(`scatter-${crypto.randomUUID()}`)
285
- let clip_path_id = $derived(`plot-area-clip-${component_id}`)
286
-
287
- // Assign stable IDs to series for keying
288
- let series_with_ids = $derived(
289
- series.map((srs: DataSeries<Metadata>, idx: number) => {
290
- if (!srs || typeof srs !== `object`) return srs
291
- // Use series.id if provided, otherwise fall back to index
292
- // prevents re-mounts when series are reordered if stable IDs are provided
293
- return { ...srs, _id: srs.id ?? idx }
294
- }),
295
- )
296
-
297
- // State for rectangle zoom selection
298
- let drag_start_coords = $state<XyObj | null>(null)
299
- let drag_current_coords = $state<XyObj | null>(null)
300
-
301
- // Zoom/pan state - track both initial (data-driven) and current (after pan/zoom) ranges
302
- let initial_x_range = $state<[number, number]>([0, 1])
303
- let initial_x2_range = $state<[number, number]>([0, 1])
304
- let initial_y_range = $state<[number, number]>([0, 1])
305
- let initial_y2_range = $state<[number, number]>([0, 1])
306
- let zoom_x_range = $state<[number, number]>([0, 1])
307
- let zoom_x2_range = $state<[number, number]>([0, 1])
308
- let zoom_y_range = $state<[number, number]>([0, 1])
309
- let zoom_y2_range = $state<[number, number]>([0, 1])
310
- let previous_series_visibility: boolean[] | null = $state(null)
311
-
312
- // Y2 axis sync configuration
313
- let y2_sync_config = $derived(normalize_y2_sync(y2_axis?.sync))
314
- // Track previous sync mode to detect changes (updated in $effect.pre to avoid race conditions)
315
- let prev_sync_mode = $state<string>(`none`)
316
-
317
- // Helper to compute synced y2 range or return fallback when sync disabled
318
- const get_synced_y2 = (y1_range: Vec2, fallback: Vec2): Vec2 =>
319
- y2_sync_config.mode !== `none`
320
- ? sync_y2_range(y1_range, initial_y2_range, y2_sync_config)
321
- : fallback
322
-
323
- // Effect to update y2 range when sync mode changes - use $effect.pre to capture
324
- // mode change before the main range-update effect runs, ensuring sync is applied
325
- // immediately when toggled (not delayed until next data change)
326
- $effect.pre(() => {
327
- const mode = y2_sync_config.mode
328
- if (mode !== prev_sync_mode) {
329
- // When sync mode becomes enabled (or changes), apply sync immediately
330
- if (mode !== `none`) {
331
- zoom_y2_range = sync_y2_range(zoom_y_range, initial_y2_range, y2_sync_config)
332
- } else {
333
- // When switching to independent mode, reset Y2 to its data range
334
- zoom_y2_range = [...initial_y2_range] as [number, number]
335
- }
336
- prev_sync_mode = mode
337
- }
338
- })
339
-
340
- // Pan state
341
- let is_focused = $state(false)
342
- let shift_held = $state(false)
343
- let pan_drag_state = $state<
344
- InitialRanges & { start: { x: number; y: number } } | null
345
- >(null)
346
- let touch_state = $state<
347
- InitialRanges & { start_touches: { x: number; y: number }[] } | null
348
- >(null)
349
-
350
- // Fill region hover state
351
- let hovered_fill_idx = $state<number | null>(null)
352
-
353
- // Reference line hover state
354
- let hovered_ref_line_idx = $state<number | null>(null)
355
-
356
- // Interactive axis loading state
357
- let axis_loading = $state<`x` | `x2` | `y` | `y2` | null>(null)
358
-
359
- // State to hold the calculated label positions after simulation
360
- let label_positions = $state<Record<string, XyObj>>({})
361
-
362
- // State for legend dragging
363
- let legend_is_dragging = $state(false)
364
- let legend_drag_offset = $state<{ x: number; y: number }>({ x: 0, y: 0 })
365
- let legend_manual_position = $state<{ x: number; y: number } | null>(null)
366
- let hovered_legend_series_idx = $state<number | null>(null)
367
-
368
- // State for legend/colorbar placement stability
369
- let legend_element = $state<HTMLDivElement | undefined>()
370
- let colorbar_element = $state<HTMLDivElement | undefined>()
371
- const legend_hover = create_hover_lock()
372
- const colorbar_hover = create_hover_lock()
373
- const dim_tracker = create_dimension_tracker()
374
- let has_initial_legend_placement = $state(false)
375
- let has_initial_colorbar_placement = $state(false)
376
-
377
- // Clear pending hover lock timeouts on unmount
378
- $effect(() => () => {
379
- legend_hover.cleanup()
380
- colorbar_hover.cleanup()
381
- })
382
-
383
- // Tooltip element reference for dynamic sizing
384
- let tooltip_el = $state<HTMLDivElement | undefined>()
385
-
386
- // Module-level constants to avoid repeated allocations
387
- // Create and categorize points in a single pass (instead of 3 separate iterations)
388
- type SimplePoint = { x: number; y: number }
389
- let points_by_axis = $derived.by(() => {
390
- const all: SimplePoint[] = []
391
- const y1: SimplePoint[] = []
392
- const y2: SimplePoint[] = []
393
- const x2: SimplePoint[] = []
394
-
395
- for (const srs of series_with_ids) {
396
- if (!srs) continue
397
- const { x: xs, y: ys, visible = true, y_axis: series_y_axis = `y1`, x_axis: x_ax = `x1` } =
398
- srs as DataSeries
399
- for (let idx = 0; idx < xs.length; idx++) {
400
- const point = { x: xs[idx], y: ys[idx] }
401
- all.push(point)
402
- if (visible) {
403
- if (series_y_axis === `y2`) y2.push(point)
404
- else y1.push(point)
405
- if (x_ax === `x2`) x2.push(point)
406
- }
407
- }
408
- }
409
- return { all, y1, y2, x2 }
410
- })
411
-
412
- let all_points = $derived(points_by_axis.all)
413
- let y1_points = $derived(points_by_axis.y1)
414
- let y2_points = $derived(points_by_axis.y2)
415
- let x2_points = $derived(points_by_axis.x2)
416
-
417
- // Layout: dynamic padding based on tick label widths
418
- const default_padding = { t: 5, b: 50, l: 50, r: 20 }
419
- let pad = $state(untrack(() => filter_padding(padding, default_padding)))
420
-
421
- // Update padding when format or ticks change
422
- $effect(() => {
423
- const new_pad = width && height &&
424
- (y_tick_values.length || y2_tick_values.length || x2_tick_values.length)
425
- ? calc_auto_padding({
426
- padding,
427
- default_padding,
428
- x2_axis: { ...final_x2_axis, tick_values: x2_tick_values },
429
- y_axis: { ...final_y_axis, tick_values: y_tick_values },
430
- y2_axis: { ...final_y2_axis, tick_values: y2_tick_values },
431
- })
432
- : filter_padding(padding, default_padding)
433
-
434
- if (
435
- pad.t !== new_pad.t ||
436
- pad.b !== new_pad.b ||
437
- pad.l !== new_pad.l ||
438
- pad.r !== new_pad.r
439
- ) pad = new_pad
440
- })
441
-
442
- // Reactive clip area dimensions to ensure proper responsiveness
443
- let clip_area = $derived({
444
- x: pad.l || 0,
445
- y: pad.t || 0,
446
- width: isFinite(width - pad.l - pad.r) ? Math.max(1, width - pad.l - pad.r) : 1,
447
- height: isFinite(height - pad.t - pad.b)
448
- ? Math.max(1, height - pad.t - pad.b)
449
- : 1,
450
- })
451
-
452
- // Calculate plot area center coordinates
453
- let plot_center_x = $derived(pad.l + (width - pad.r - pad.l) / 2)
454
- let plot_center_y = $derived(pad.t + (height - pad.b - pad.t) / 2)
455
-
456
- // Extract color and size values in single pass (used for scale computations)
457
- let series_value_arrays = $derived.by(() => {
458
- const color_values: number[] = []
459
- const size_values: number[] = []
460
- for (const srs of series_with_ids) {
461
- if (!srs) continue
462
- const { color_values: cvs, size_values: svs } = srs as DataSeries
463
- if (cvs) { for (const val of cvs) if (val != null) color_values.push(val) }
464
- if (svs) { for (const val of svs) if (val != null) size_values.push(val) }
465
- }
466
- return { color_values, size_values }
467
- })
468
- let all_color_values = $derived(series_value_arrays.color_values)
469
-
470
- // Compute auto ranges based on data and limits
471
- let auto_x_range = $derived(
472
- get_nice_data_range(
473
- all_points,
474
- ({ x }) => x,
475
- final_x_axis.range ?? [null, null],
476
- final_x_axis.scale_type ?? `linear`,
477
- range_padding,
478
- is_time_x,
479
- ),
480
- )
481
-
482
- let auto_y_range = $derived(
483
- get_nice_data_range(
484
- y1_points,
485
- ({ y }) => y,
486
- final_y_axis.range ?? [null, null],
487
- final_y_axis.scale_type ?? `linear`,
488
- range_padding,
489
- false,
490
- ),
491
- )
492
-
493
- let auto_x2_range = $derived(
494
- get_nice_data_range(
495
- x2_points,
496
- ({ x }) => x,
497
- final_x2_axis.range ?? [null, null],
498
- final_x2_axis.scale_type ?? `linear`,
499
- range_padding,
500
- is_time_x2,
501
- ),
502
- )
503
-
504
- let auto_y2_range = $derived(
505
- get_nice_data_range(
506
- y2_points,
507
- ({ y }) => y,
508
- final_y2_axis.range ?? [null, null],
509
- final_y2_axis.scale_type ?? `linear`,
510
- range_padding,
511
- false,
512
- ),
513
- )
514
-
515
- // Update zoom ranges when auto ranges or explicit ranges change
516
- // - Explicit ranges (from zoom/pan): apply directly
517
- // - Auto ranges (from data changes): use lazy expansion to preserve view context
518
- $effect(() => {
519
- // Helper to get effective range (explicit ?? auto) and check if explicit
520
- const get_range = (
521
- axis: { range?: [number | null, number | null] },
522
- auto: Vec2,
523
- ): { explicit: boolean; range: Vec2 } => {
524
- const explicit = axis.range?.[0] != null && axis.range?.[1] != null
525
- const range = [axis.range?.[0] ?? auto[0], axis.range?.[1] ?? auto[1]] as Vec2
526
- return { explicit, range }
527
- }
528
-
529
- const x = get_range(final_x_axis, auto_x_range)
530
- const x2 = get_range(final_x2_axis, auto_x2_range)
531
- const y = get_range(final_y_axis, auto_y_range)
532
- const y2 = get_range(final_y2_axis, auto_y2_range)
533
-
534
- // X axis: explicit → direct, auto → lazy expand
535
- if (x.explicit) {
536
- zoom_x_range = x.range
537
- } else {
538
- const result = expand_range_if_needed(initial_x_range, x.range)
539
- if (result.changed) {
540
- ;[initial_x_range, zoom_x_range] = [result.range, result.range]
541
- }
542
- }
543
-
544
- // X2 axis: explicit → direct, auto → lazy expand
545
- if (x2.explicit) {
546
- zoom_x2_range = x2.range
547
- } else {
548
- const result = expand_range_if_needed(initial_x2_range, x2.range)
549
- if (result.changed) {
550
- ;[initial_x2_range, zoom_x2_range] = [result.range, result.range]
551
- }
552
- }
553
-
554
- // Y axis: explicit → direct, auto → lazy expand
555
- if (y.explicit) {
556
- zoom_y_range = y.range
557
- } else {
558
- const result = expand_range_if_needed(initial_y_range, y.range)
559
- if (result.changed) {
560
- ;[initial_y_range, zoom_y_range] = [result.range, result.range]
561
- }
562
- }
563
-
564
- // Y2 axis: explicit → direct, else expand initial range then optionally sync
565
- if (y2.explicit) {
566
- zoom_y2_range = y2.range
567
- } else {
568
- const result = expand_range_if_needed(initial_y2_range, y2.range)
569
- if (result.changed) initial_y2_range = result.range
570
- // Apply sync if enabled, otherwise use expanded range (or keep current if unchanged)
571
- if (y2_sync_config.mode !== `none`) {
572
- zoom_y2_range = sync_y2_range(zoom_y_range, initial_y2_range, y2_sync_config)
573
- } else if (result.changed) {
574
- zoom_y2_range = result.range
575
- }
576
- }
577
- })
578
-
579
- let [x_min, x_max] = $derived(zoom_x_range)
580
- let [x2_min, x2_max] = $derived(zoom_x2_range)
581
- let [y_min, y_max] = $derived(zoom_y_range)
582
- let [y2_min, y2_max] = $derived(zoom_y2_range)
583
-
584
- // Create auto color range
585
- let auto_color_range = $derived(
586
- // Ensure we only calculate extent on actual numbers, filtering out nulls/undefined
587
- all_color_values.length > 0
588
- ? extent(
589
- all_color_values.filter((color_val: number | null): color_val is number =>
590
- typeof color_val === `number`
591
- ),
592
- )
593
- : [0, 1],
594
- ) as Vec2
595
-
596
- // Create scale functions
597
- // For time scales, use scaleTime directly; otherwise use create_scale (supports linear/log/arcsinh)
598
- let x_scale_fn = $derived(
599
- is_time_x
600
- ? scaleTime()
601
- .domain([new Date(x_min), new Date(x_max)])
602
- .range([pad.l, width - pad.r])
603
- : create_scale(final_x_axis.scale_type ?? `linear`, [x_min, x_max], [
604
- pad.l,
605
- width - pad.r,
606
- ]),
607
- )
608
-
609
- let x2_scale_fn = $derived(
610
- is_time_x2
611
- ? scaleTime()
612
- .domain([new Date(x2_min), new Date(x2_max)])
613
- .range([pad.l, width - pad.r])
614
- : create_scale(final_x2_axis.scale_type ?? `linear`, [x2_min, x2_max], [
615
- pad.l,
616
- width - pad.r,
617
- ]),
618
- )
619
-
620
- let y_scale_fn = $derived(
621
- create_scale(final_y_axis.scale_type ?? `linear`, [y_min, y_max], [
622
- height - pad.b,
623
- pad.t,
624
- ]),
625
- )
626
-
627
- let y2_scale_fn = $derived(
628
- create_scale(final_y2_axis.scale_type ?? `linear`, [y2_min, y2_max], [
629
- height - pad.b,
630
- pad.t,
631
- ]),
632
- )
633
-
634
- // All size values from series (for size scale) - extracted in series_value_arrays
635
- let all_size_values = $derived(series_value_arrays.size_values)
636
-
637
- // Size scale function (using shared utility)
638
- let size_scale_fn = $derived(create_size_scale(size_scale, all_size_values))
639
-
640
- // Color scale function (using shared utility)
641
- let color_scale_fn = $derived(create_color_scale(color_scale, auto_color_range))
642
-
643
- // Filter series data to only include points within bounds and augment with internal data
644
- let filtered_series = $derived(
645
- series_with_ids
646
- .map((data_series: DataSeries<Metadata>, series_idx): DataSeries<Metadata> => {
647
- // Handle null/undefined series first
648
- if (!data_series) {
649
- return {
650
- x: [],
651
- y: [],
652
- visible: true,
653
- filtered_data: [],
654
- _id: series_idx,
655
- orig_series_idx: series_idx,
656
- }
657
- }
658
-
659
- // Handle explicitly hidden series
660
- if (!(data_series.visible ?? true)) {
661
- return {
662
- ...data_series,
663
- visible: false,
664
- filtered_data: [],
665
- orig_series_idx: series_idx,
666
- }
667
- }
668
-
669
- const { x: xs, y: ys, color_values, size_values, ...series_rest } = data_series
670
-
671
- // Process points internally, adding properties beyond the base Point type
672
- const processed_points: InternalPoint<Metadata>[] = xs.map(
673
- (x_val: number, point_idx: number) => ({
674
- x: x_val,
675
- y: ys[point_idx],
676
- color_value: color_values?.[point_idx],
677
- metadata: process_prop(series_rest.metadata, point_idx) as Metadata | undefined,
678
- point_style: process_prop(series_rest.point_style, point_idx),
679
- point_hover: process_prop(series_rest.point_hover, point_idx),
680
- point_label: process_prop(series_rest.point_label, point_idx),
681
- point_offset: process_prop(series_rest.point_offset, point_idx),
682
- series_idx,
683
- point_idx,
684
- size_value: size_values?.[point_idx],
685
- }),
686
- )
687
-
688
- // Filter to points within the plot bounds (handles inverted ranges like [3.5, 1.4])
689
- const in_range = (val: number | null | undefined, lo: number, hi: number) =>
690
- val != null && !isNaN(val) && val >= Math.min(lo, hi) &&
691
- val <= Math.max(lo, hi)
692
-
693
- // Determine which ranges to use based on series axis properties
694
- const [series_x_min, series_x_max] = (data_series.x_axis ?? `x1`) === `x2`
695
- ? [x2_min, x2_max]
696
- : [x_min, x_max]
697
- const [series_y_min, series_y_max] = (data_series.y_axis ?? `y1`) === `y2`
698
- ? [y2_min, y2_max]
699
- : [y_min, y_max]
700
-
701
- const filtered_data_with_extras = processed_points.filter(
702
- ({ x, y }) =>
703
- in_range(x, series_x_min, series_x_max) &&
704
- in_range(y, series_y_min, series_y_max),
705
- )
706
-
707
- // Return structure consistent with DataSeries but acknowledge internal data structure (filtered_data)
708
- return {
709
- ...data_series,
710
- visible: true, // Mark series as visible here
711
- filtered_data: filtered_data_with_extras,
712
- orig_series_idx: series_idx, // Store original index for auto-cycling colors/symbols
713
- }
714
- })
715
- // Filter series end up completely empty after point filtering
716
- .filter((
717
- srs,
718
- ): srs is DataSeries<Metadata> & { filtered_data: InternalPoint<Metadata>[] } =>
719
- !!srs.filtered_data && srs.filtered_data.length > 0
720
- ),
721
- )
722
-
723
- // Collect all plot points for legend placement calculation
724
- let plot_points_for_placement = $derived.by(() => {
725
- if (!width || !height || !filtered_series) return []
726
-
727
- const points: { x: number; y: number }[] = []
728
-
729
- for (const series_data of filtered_series) {
730
- if (!series_data?.filtered_data) continue
731
- const use_x2_scale = series_data.x_axis === `x2`
732
- for (const point of series_data.filtered_data) {
733
- const active_x_scale = use_x2_scale ? x2_scale_fn : x_scale_fn
734
- const active_is_time_x = use_x2_scale ? is_time_x2 : is_time_x
735
- const point_x_coord = active_is_time_x
736
- ? active_x_scale(new Date(point.x))
737
- : active_x_scale(point.x)
738
- const point_y_coord =
739
- (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(
740
- point.y,
741
- )
742
-
743
- if (isFinite(point_x_coord) && isFinite(point_y_coord)) {
744
- points.push({ x: point_x_coord, y: point_y_coord })
745
- }
746
- }
747
- }
748
- return points
749
- })
750
-
751
- // Explicitly define the type for display_style matching PlotLegend expectations
752
- type LegendDisplayStyle = {
753
- symbol_type?: D3SymbolName
754
- symbol_color?: string
755
- line_color?: string
756
- line_dash?: string
757
- }
758
-
759
- // Computed fill regions: merge fill_regions and converted error_bands, resolve boundaries
760
- type ComputedFill = FillRegion & {
761
- idx: number
762
- source_type: `fill_region` | `error_band`
763
- source_idx: number
764
- path_segments: string[]
765
- }
766
- let computed_fills = $derived.by((): ComputedFill[] => {
767
- // Early exit: skip expensive computation if no fills to render
768
- const has_fill_regions = fill_regions && fill_regions.length > 0
769
- const has_error_bands = error_bands && error_bands.length > 0
770
- if (!has_fill_regions && !has_error_bands) return []
771
-
772
- // Merge fill_regions and converted error_bands, tracking source
773
- const all_regions: {
774
- region: FillRegion | null
775
- source_type: `fill_region` | `error_band`
776
- source_idx: number
777
- }[] = [
778
- ...(fill_regions ?? []).map((region, source_idx) => ({
779
- region,
780
- source_type: `fill_region` as const,
781
- source_idx,
782
- })),
783
- ...(error_bands ?? []).map((band, source_idx) => ({
784
- region: convert_error_band_to_fill_region(band, series_with_ids),
785
- source_type: `error_band` as const,
786
- source_idx,
787
- })),
788
- ]
789
-
790
- // Compute unique x-values once for all fills
791
- // Optimization: deduplicate first (O(n)), then sort only unique values (O(k log k))
792
- // This is faster for datasets with many duplicate x-values across series
793
- const x_set = new SvelteSet<number>()
794
- for (const data_series of series_with_ids) {
795
- if (!data_series) continue
796
- for (const val of data_series.x) {
797
- if (typeof val === `number` && isFinite(val)) x_set.add(val)
798
- }
799
- }
800
- const unique_x = [...x_set].sort((val_a, val_b) => val_a - val_b)
801
-
802
- if (unique_x.length === 0) return []
803
-
804
- return all_regions
805
- .filter((
806
- entry,
807
- ): entry is {
808
- region: FillRegion
809
- source_type: `fill_region` | `error_band`
810
- source_idx: number
811
- } => entry.region !== null)
812
- .map(({ region, source_type, source_idx }, idx) => {
813
- if (region.visible === false) return null
814
-
815
- // Domain context for boundary resolution
816
- const domains = {
817
- y_domain: [y_min, y_max] as Vec2,
818
- y2_domain: [y2_min, y2_max] as Vec2,
819
- }
820
-
821
- // Resolve upper and lower boundaries
822
- const upper_values = resolve_boundary(
823
- region.upper,
824
- series_with_ids,
825
- unique_x,
826
- domains,
827
- )
828
- const lower_values = resolve_boundary(
829
- region.lower,
830
- series_with_ids,
831
- unique_x,
832
- domains,
833
- )
834
-
835
- if (!upper_values || !lower_values) return null
836
-
837
- // Apply range constraints
838
- const range_filtered = apply_range_constraints(
839
- unique_x,
840
- lower_values,
841
- upper_values,
842
- region,
843
- )
844
-
845
- // Clamp for log scale if needed
846
- const y_scale_type = final_y_axis.scale_type ?? `linear`
847
- const x_scale_type = final_x_axis.scale_type ?? `linear`
848
- const clamped = clamp_for_log_scale(
849
- range_filtered.x,
850
- range_filtered.y1,
851
- range_filtered.y2,
852
- y_scale_type,
853
- x_scale_type,
854
- )
855
-
856
- // Apply where condition (splits into segments)
857
- const conditioned = apply_where_condition(
858
- clamped.x,
859
- clamped.y1,
860
- clamped.y2,
861
- region,
862
- )
863
-
864
- // Generate paths for each segment (convert to pixel coordinates)
865
- const path_segments = conditioned.segments
866
- .filter((segment) => segment.length > 1)
867
- .map((segment) => {
868
- const pixel_data: FillPathPoint[] = segment.map((point) => ({
869
- x: x_scale_fn(point.x),
870
- y1: y_scale_fn(point.y1),
871
- y2: y_scale_fn(point.y2),
872
- }))
873
- return generate_fill_path(pixel_data, region.curve ?? `monotoneX`)
874
- })
875
- .filter((path) => path.length > 0)
876
-
877
- if (path_segments.length === 0) return null
878
-
879
- return { ...region, idx, source_type, source_idx, path_segments }
880
- })
881
- .filter((fill): fill is ComputedFill => fill !== null)
882
- })
883
-
884
- // Prepare data needed for the legend component
885
- let legend_data = $derived.by(() => {
886
- const items = series_with_ids.map(
887
- (data_series: DataSeries & { _id?: string | number }, series_idx: number) => {
888
- const is_visible = data_series?.visible ?? true
889
- // Prefer top-level label, fallback to metadata label
890
- const explicit_label = data_series?.label ??
891
- (typeof data_series?.metadata === `object` &&
892
- data_series.metadata !== null &&
893
- `label` in data_series.metadata &&
894
- typeof data_series.metadata.label === `string`
895
- ? data_series.metadata.label
896
- : null)
897
- // Use explicit label or generate default
898
- const label = explicit_label ?? `Series ${series_idx + 1}`
899
- const has_explicit_label = explicit_label != null
900
-
901
- // Use series-specific defaults for auto-differentiation
902
- const series_default_color = get_series_color(series_idx)
903
- const series_default_symbol = get_series_symbol(series_idx)
904
-
905
- const display_style: LegendDisplayStyle = {
906
- symbol_type: series_default_symbol,
907
- symbol_color: series_default_color,
908
- line_color: series_default_color,
909
- }
910
- const series_markers = data_series?.markers ?? DEFAULT_MARKERS
911
-
912
- // Check point_style (could be object or array)
913
- const first_point_style = Array.isArray(data_series?.point_style)
914
- ? data_series.point_style[0]
915
- : data_series?.point_style
916
-
917
- if (series_markers?.includes(`points`)) {
918
- if (first_point_style) {
919
- // Use explicit symbol_type if provided and valid, otherwise keep series default
920
- if (
921
- typeof first_point_style.symbol_type === `string` &&
922
- symbol_names.includes(first_point_style.symbol_type as D3SymbolName)
923
- ) {
924
- display_style.symbol_type = first_point_style
925
- .symbol_type as D3SymbolName
926
- }
927
-
928
- // Use explicit fill color if provided
929
- if (first_point_style.fill) {
930
- display_style.symbol_color = first_point_style.fill
931
- }
932
- if (first_point_style.stroke) {
933
- // Use stroke color if fill is none or transparent
934
- if (
935
- !display_style.symbol_color ||
936
- display_style.symbol_color === `none` ||
937
- display_style.symbol_color.startsWith(`rgba(`, 0) // Check if transparent
938
- ) display_style.symbol_color = first_point_style.stroke
939
- }
940
- }
941
- // else: keep series-specific defaults for symbol_type and symbol_color
942
- } else {
943
- // If no points marker, explicitly remove marker style for legend
944
- display_style.symbol_type = undefined
945
- display_style.symbol_color = undefined
946
- }
947
-
948
- // Check line_style
949
- if (series_markers?.includes(`line`)) {
950
- // Prefer explicit line stroke, then other explicit colors, then series default
951
- let legend_line_color = data_series?.line_style?.stroke
952
- if (!legend_line_color) {
953
- // Try color scale if available
954
- const first_cv = Array.isArray(data_series?.color_values)
955
- ? data_series?.color_values?.find((color_val: number | null) =>
956
- color_val != null
957
- )
958
- : undefined
959
- legend_line_color =
960
- (first_cv != null ? color_scale_fn(first_cv) : undefined) ||
961
- first_point_style?.fill ||
962
- first_point_style?.stroke ||
963
- series_default_color
964
- }
965
- display_style.line_color = legend_line_color
966
- display_style.line_dash = data_series?.line_style?.line_dash
967
- } else {
968
- // If no line marker, explicitly remove line style for legend
969
- display_style.line_dash = undefined
970
- display_style.line_color = undefined
971
- }
972
-
973
- return {
974
- series_idx,
975
- label,
976
- visible: is_visible,
977
- display_style,
978
- has_explicit_label,
979
- legend_group: data_series?.legend_group,
980
- }
981
- },
982
- )
983
-
984
- // Deduplicate by label+legend_group - keep first occurrence of each unique combination
985
- const seen_labels = new SvelteSet<string>()
986
- const series_items = items.filter(
987
- (
988
- legend_item: {
989
- label: string
990
- series_idx: number
991
- visible: boolean
992
- display_style: LegendDisplayStyle
993
- has_explicit_label: boolean
994
- legend_group?: string
995
- },
996
- ) => {
997
- // Use label+group as unique key (group may be undefined)
998
- const unique_key = `${legend_item.legend_group ?? ``}::${legend_item.label}`
999
- if (seen_labels.has(unique_key)) return false
1000
- seen_labels.add(unique_key)
1001
- return true
1002
- },
1003
- )
1004
-
1005
- // Add fill region items to legend (deduplicated using same key format as series)
1006
- const fill_items = computed_fills
1007
- .filter((fill) => fill.show_in_legend !== false && fill.label)
1008
- .filter((fill) => {
1009
- // Use same composite key as series: legend_group::label
1010
- const unique_key = `${fill.legend_group ?? ``}::${fill.label ?? ``}`
1011
- if (seen_labels.has(unique_key)) return false
1012
- seen_labels.add(unique_key)
1013
- return true
1014
- })
1015
- .map((fill) => {
1016
- // Pass gradient for swatch rendering, or solid color as fallback
1017
- const fill_gradient = is_fill_gradient(fill.fill) ? fill.fill : undefined
1018
- const fill_color = typeof fill.fill === `string` ? fill.fill : undefined
1019
-
1020
- return {
1021
- series_idx: -1, // Not a series
1022
- fill_idx: fill.idx,
1023
- fill_source_type: fill.source_type,
1024
- fill_source_idx: fill.source_idx,
1025
- item_type: `fill` as const,
1026
- label: fill.label ?? ``,
1027
- visible: fill.visible !== false,
1028
- legend_group: fill.legend_group,
1029
- display_style: {
1030
- fill_color,
1031
- fill_opacity: fill.fill_opacity ?? 0.3,
1032
- edge_color: fill.edge_upper?.color,
1033
- fill_gradient,
1034
- },
1035
- }
1036
- })
1037
-
1038
- return [...series_items, ...fill_items]
1039
- })
1040
-
1041
- // Group fills by z-index for ordered rendering (single pass instead of 4 filters)
1042
- let fills_by_z = $derived.by(() => {
1043
- const groups: {
1044
- below_grid: typeof computed_fills
1045
- below_lines: typeof computed_fills
1046
- below_points: typeof computed_fills
1047
- above_all: typeof computed_fills
1048
- } = { below_grid: [], below_lines: [], below_points: [], above_all: [] }
1049
-
1050
- for (const fill of computed_fills) {
1051
- if (fill.z_index === `below-grid`) groups.below_grid.push(fill)
1052
- else if (fill.z_index === `below-points`) groups.below_points.push(fill)
1053
- else if (fill.z_index === `above-all`) groups.above_all.push(fill)
1054
- else groups.below_lines.push(fill) // default: no z_index or 'below-lines'
1055
- }
1056
- return groups
1057
- })
1058
-
1059
- // Compute ref_lines with index and group by z-index (using shared utilities)
1060
- let indexed_ref_lines = $derived(index_ref_lines(ref_lines))
1061
- let ref_lines_by_z = $derived(group_ref_lines_by_z(indexed_ref_lines))
1062
-
1063
- // Calculate best legend placement using continuous grid sampling
1064
- let legend_placement = $derived.by(() => {
1065
- const should_place = legend != null &&
1066
- (legend_data.length > 1 || Object.keys(legend).length > 0)
1067
-
1068
- if (!should_place || !width || !height) return null
1069
-
1070
- const plot_width = width - pad.l - pad.r
1071
- const plot_height = height - pad.t - pad.b
1072
-
1073
- // Use measured size if available, otherwise estimate
1074
- const legend_size = legend_element
1075
- ? { width: legend_element.offsetWidth, height: legend_element.offsetHeight }
1076
- : { width: 120, height: 80 }
1077
-
1078
- const placement_config = {
1079
- plot_bounds: { x: pad.l, y: pad.t, width: plot_width, height: plot_height },
1080
- element_size: legend_size,
1081
- axis_clearance: legend?.axis_clearance,
1082
- exclude_rects: [],
1083
- points: plot_points_for_placement,
1084
- }
1085
-
1086
- return compute_element_placement(placement_config)
1087
- })
1088
-
1089
- // Calculate color bar placement (coordinates with legend to avoid overlap)
1090
- let color_bar_placement = $derived.by(() => {
1091
- if (!color_bar || !all_color_values.length || !width || !height) return null
1092
-
1093
- const plot_width = width - pad.l - pad.r
1094
- const plot_height = height - pad.t - pad.b
1095
-
1096
- // Use measured size if available, otherwise estimate based on orientation
1097
- const is_horizontal = color_bar.orientation === `horizontal`
1098
- const colorbar_size = colorbar_element
1099
- ? { width: colorbar_element.offsetWidth, height: colorbar_element.offsetHeight }
1100
- : is_horizontal
1101
- ? { width: 220, height: 40 }
1102
- : { width: 40, height: 100 }
1103
-
1104
- // Build exclusion rects (avoid legend if it's placed)
1105
- const exclude_rects: Rect[] = []
1106
- if (legend_element && legend_placement) {
1107
- exclude_rects.push({
1108
- x: legend_placement.x,
1109
- y: legend_placement.y,
1110
- width: legend_element.offsetWidth || 120,
1111
- height: legend_element.offsetHeight || 80,
1112
- })
1113
- }
1114
-
1115
- return compute_element_placement({
1116
- plot_bounds: { x: pad.l, y: pad.t, width: plot_width, height: plot_height },
1117
- element_size: colorbar_size,
1118
- // Colorbar needs slightly more clearance than legend to avoid axis labels
1119
- axis_clearance: 15,
1120
- exclude_rects,
1121
- points: plot_points_for_placement,
1122
- })
1123
- })
1124
-
1125
- // Active legend placement (null if user set explicit position)
1126
- let active_legend_placement = $derived.by(() => {
1127
- if (!legend_placement) return null
1128
-
1129
- // Skip auto-placement if user set explicit position in style
1130
- const legend_style = legend?.style ?? ``
1131
- if (
1132
- /(^|[;{]\s*)(top|bottom|left|right)\s*:|position\s*:\s*absolute/.test(
1133
- legend_style,
1134
- )
1135
- ) return null
1136
-
1137
- return legend_placement
1138
- })
1139
-
1140
- // Initialize tweened values for color bar position - create once, update target via effect
1141
- // untrack() explicitly captures initial tween config (intentional - config set once at mount)
1142
- const tweened_colorbar_coords = new Tween(
1143
- { x: 0, y: 0 },
1144
- untrack(() => ({ duration: 400, ...color_bar?.tween })),
1145
- )
1146
- // Initialize tweened values for legend position - create once, update target via effect
1147
- const tweened_legend_coords = new Tween(
1148
- { x: 0, y: 0 },
1149
- untrack(() => ({ duration: 400, ...legend?.tween })),
1150
- )
1151
-
1152
- // Update placement positions (with animation and stability checks)
1153
- $effect(() => {
1154
- if (!width || !height) return
1155
-
1156
- // Track dimensions for resize detection
1157
- const dims_changed = dim_tracker.has_changed(width, height)
1158
- if (dims_changed) dim_tracker.update(width, height)
1159
-
1160
- // Update colorbar position (stable after initial placement unless responsive)
1161
- if (color_bar_placement) {
1162
- const is_responsive = color_bar?.responsive ?? false
1163
- const should_update = dims_changed || (!colorbar_hover.is_locked.current &&
1164
- (is_responsive || !has_initial_colorbar_placement))
1165
-
1166
- if (should_update) {
1167
- tweened_colorbar_coords.set(
1168
- { x: color_bar_placement.x, y: color_bar_placement.y },
1169
- has_initial_colorbar_placement ? undefined : { duration: 0 },
1170
- )
1171
- if (colorbar_element && !has_initial_colorbar_placement) {
1172
- has_initial_colorbar_placement = true
1173
- }
1174
- }
1175
- }
1176
-
1177
- // Update legend position (stable after initial placement unless responsive)
1178
- if (legend_manual_position && !legend_is_dragging) {
1179
- // Immediate update (no animation) for manually dragged positions
1180
- tweened_legend_coords.set(legend_manual_position, { duration: 0 })
1181
- } else if (active_legend_placement && !legend_is_dragging) {
1182
- const is_responsive = legend?.responsive ?? false
1183
- const should_update = dims_changed || (!legend_hover.is_locked.current &&
1184
- (is_responsive || !has_initial_legend_placement))
1185
-
1186
- if (should_update) {
1187
- tweened_legend_coords.set(
1188
- { x: active_legend_placement.x, y: active_legend_placement.y },
1189
- has_initial_legend_placement ? undefined : { duration: 0 },
1190
- )
1191
- if (legend_element) has_initial_legend_placement = true
1192
- }
1193
- }
1194
- })
1195
-
1196
- // Generate axis ticks - consolidated into single derived for efficiency
1197
- let axis_ticks = $derived.by(() => {
1198
- if (!width || !height) return { x: [], x2: [], y: [], y2: [] }
1199
-
1200
- // X-axis ticks: choose appropriate scale for tick generation
1201
- // Time scales (format starts with %) use scaleTime for better tick placement
1202
- const x_scale_for_ticks = is_time_x
1203
- ? scaleTime().domain([new Date(x_min), new Date(x_max)])
1204
- : create_scale(final_x_axis.scale_type ?? `linear`, [x_min, x_max], [0, 1])
1205
-
1206
- const x2_scale_for_ticks = is_time_x2
1207
- ? scaleTime().domain([new Date(x2_min), new Date(x2_max)])
1208
- : create_scale(final_x2_axis.scale_type ?? `linear`, [x2_min, x2_max], [0, 1])
1209
-
1210
- return {
1211
- x: generate_ticks(
1212
- [x_min, x_max],
1213
- final_x_axis.scale_type ?? `linear`,
1214
- final_x_axis.ticks,
1215
- x_scale_for_ticks,
1216
- { format: final_x_axis.format },
1217
- ),
1218
- x2: x2_points.length > 0
1219
- ? generate_ticks(
1220
- [x2_min, x2_max],
1221
- final_x2_axis.scale_type ?? `linear`,
1222
- final_x2_axis.ticks,
1223
- x2_scale_for_ticks,
1224
- { format: final_x2_axis.format },
1225
- )
1226
- : [],
1227
- y: generate_ticks(
1228
- [y_min, y_max],
1229
- final_y_axis.scale_type ?? `linear`,
1230
- final_y_axis.ticks,
1231
- y_scale_fn,
1232
- { default_count: 5 },
1233
- ),
1234
- y2: y2_points.length > 0
1235
- ? generate_ticks(
1236
- [y2_min, y2_max],
1237
- final_y2_axis.scale_type ?? `linear`,
1238
- final_y2_axis.ticks,
1239
- y2_scale_fn,
1240
- { default_count: 5 },
1241
- )
1242
- : [],
1243
- }
1244
- })
1245
-
1246
- let x_tick_values = $derived(axis_ticks.x)
1247
- let x2_tick_values = $derived(axis_ticks.x2)
1248
- let y_tick_values = $derived(axis_ticks.y)
1249
- let y2_tick_values = $derived(axis_ticks.y2)
1250
-
1251
- // Cache measured tick-label widths so expensive text measurement only runs
1252
- // when tick values/format change, not on every template rerender.
1253
- let tick_label_widths = $derived({
1254
- x2_max: measure_max_tick_width(x2_tick_values, final_x2_axis.format ?? ``),
1255
- y_max: measure_max_tick_width(y_tick_values, final_y_axis.format ?? ``),
1256
- y2_max: measure_max_tick_width(y2_tick_values, final_y2_axis.format ?? ``),
1257
- })
1258
-
1259
- // Define global handlers reference for adding/removing listeners
1260
- const on_window_mouse_move = (evt: MouseEvent) => {
1261
- if (!drag_start_coords || !svg_bounding_box) return // Exit if not dragging or no bounds
1262
-
1263
- // Calculate mouse position relative to the stored SVG bounding box
1264
- const current_x = evt.clientX - svg_bounding_box.left
1265
- const current_y = evt.clientY - svg_bounding_box.top
1266
- drag_current_coords = { x: current_x, y: current_y }
1267
-
1268
- // Optional: update tooltip only if inside SVG bounds
1269
- const is_inside_svg = current_x >= 0 &&
1270
- current_x <= svg_bounding_box.width &&
1271
- current_y >= 0 &&
1272
- current_y <= svg_bounding_box.height
1273
-
1274
- if (is_inside_svg) {
1275
- // Use the already calculated relative coordinates
1276
- update_tooltip_point(current_x, current_y)
1277
- } else tooltip_point = null // Clear tooltip if outside
1278
- }
1279
-
1280
- const on_window_mouse_up = (_evt: MouseEvent) => {
1281
- if (drag_start_coords && drag_current_coords) {
1282
- // Use current scales to invert screen coords to data coords
1283
- const start_data_x_val = x_scale_fn.invert(drag_start_coords.x)
1284
- const end_data_x_val = x_scale_fn.invert(drag_current_coords.x)
1285
- const start_data_y_val = y_scale_fn.invert(drag_start_coords.y)
1286
- const end_data_y_val = y_scale_fn.invert(drag_current_coords.y)
1287
-
1288
- // Ensure range is not zero and order is correct
1289
- let x1: number, x2: number
1290
- if (start_data_x_val instanceof Date && end_data_x_val instanceof Date) {
1291
- x1 = start_data_x_val.getTime()
1292
- x2 = end_data_x_val.getTime()
1293
- } else if (
1294
- typeof start_data_x_val === `number` &&
1295
- typeof end_data_x_val === `number`
1296
- ) {
1297
- x1 = start_data_x_val
1298
- x2 = end_data_x_val
1299
- } else {
1300
- console.error(`Mismatched types for x-axis zoom calculation`)
1301
- // Reset states without zooming if types are wrong
1302
- drag_start_coords = null
1303
- drag_current_coords = null
1304
- window.removeEventListener(`mousemove`, on_window_mouse_move)
1305
- window.removeEventListener(`mouseup`, on_window_mouse_up)
1306
- return
1307
- }
1308
-
1309
- const next_x_range: [number, number] = [Math.min(x1, x2), Math.max(x1, x2)]
1310
- // Y axis is always number
1311
- const next_y_range: [number, number] = [
1312
- Math.min(start_data_y_val, end_data_y_val),
1313
- Math.max(start_data_y_val, end_data_y_val),
1314
- ]
1315
-
1316
- // Check for minuscule zoom box (e.g. accidental click)
1317
- const min_zoom_size = 5 // Minimum pixels to trigger zoom
1318
- const dx = Math.abs(drag_start_coords.x - drag_current_coords.x)
1319
- const dy = Math.abs(drag_start_coords.y - drag_current_coords.y)
1320
-
1321
- if (
1322
- dx > min_zoom_size &&
1323
- dy > min_zoom_size &&
1324
- next_x_range[0] !== next_x_range[1] &&
1325
- next_y_range[0] !== next_y_range[1]
1326
- ) {
1327
- // Update axis ranges to trigger reactivity (like BarPlot/Histogram do)
1328
- // Y2 sync is handled by the effect that reacts to y_axis changes
1329
- x_axis = { ...x_axis, range: next_x_range }
1330
- y_axis = { ...y_axis, range: next_y_range }
1331
-
1332
- // X2 axis: invert screen coords using x2 scale
1333
- if (x2_points.length > 0) {
1334
- const start_x2_val = x2_scale_fn.invert(drag_start_coords.x)
1335
- const end_x2_val = x2_scale_fn.invert(drag_current_coords.x)
1336
- const x2_a = start_x2_val instanceof Date
1337
- ? start_x2_val.getTime()
1338
- : start_x2_val as number
1339
- const x2_b = end_x2_val instanceof Date
1340
- ? end_x2_val.getTime()
1341
- : end_x2_val as number
1342
- x2_axis = {
1343
- ...x2_axis,
1344
- range: [Math.min(x2_a, x2_b), Math.max(x2_a, x2_b)],
1345
- }
1346
- }
1347
- }
1348
- }
1349
-
1350
- // Reset states and remove listeners
1351
- drag_start_coords = null
1352
- drag_current_coords = null
1353
- svg_bounding_box = null
1354
- window.removeEventListener(`mousemove`, on_window_mouse_move)
1355
- window.removeEventListener(`mouseup`, on_window_mouse_up)
1356
- document.body.style.cursor = `default`
1357
- }
1358
-
1359
- // Pan drag handlers
1360
- const on_pan_move = (evt: MouseEvent) => {
1361
- if (!pan_drag_state) return
1362
- const dx = evt.clientX - pan_drag_state.start.x
1363
- const dy = evt.clientY - pan_drag_state.start.y
1364
-
1365
- // Convert pixel delta to data delta (note: drag direction is inverted for natural pan feel)
1366
- // Clamp to at least 1 to avoid Infinity deltas when padding equals container size
1367
- const plot_width = Math.max(1, width - pad.l - pad.r)
1368
- const plot_height = Math.max(1, height - pad.t - pad.b)
1369
- const sensitivity = pan?.drag_sensitivity ?? 1
1370
-
1371
- const x_delta = pixels_to_data_delta(
1372
- -dx * sensitivity,
1373
- pan_drag_state.initial_x_range,
1374
- plot_width,
1375
- )
1376
- const x2_delta = pixels_to_data_delta(
1377
- -dx * sensitivity,
1378
- pan_drag_state.initial_x2_range,
1379
- plot_width,
1380
- )
1381
- const y_delta = pixels_to_data_delta(
1382
- dy * sensitivity,
1383
- pan_drag_state.initial_y_range,
1384
- plot_height,
1385
- )
1386
- const y2_delta = pixels_to_data_delta(
1387
- dy * sensitivity,
1388
- pan_drag_state.initial_y2_range,
1389
- plot_height,
1390
- )
1391
-
1392
- zoom_x_range = pan_range(pan_drag_state.initial_x_range, x_delta)
1393
- zoom_x2_range = pan_range(pan_drag_state.initial_x2_range, x2_delta)
1394
- zoom_y_range = pan_range(pan_drag_state.initial_y_range, y_delta)
1395
- zoom_y2_range = get_synced_y2(
1396
- zoom_y_range,
1397
- pan_range(pan_drag_state.initial_y2_range, y2_delta),
1398
- )
1399
- }
1400
-
1401
- const on_pan_end = () => {
1402
- pan_drag_state = null
1403
- document.body.style.cursor = ``
1404
- window.removeEventListener(`mousemove`, on_pan_move)
1405
- window.removeEventListener(`mouseup`, on_pan_end)
1406
- }
1407
-
1408
- function handle_mouse_down(evt: MouseEvent) {
1409
- if (!svg_element) return
1410
-
1411
- // Check if pan is enabled and shift is held for pan mode
1412
- const pan_enabled = pan?.enabled !== false
1413
- if (pan_enabled && evt.shiftKey) {
1414
- evt.preventDefault()
1415
- pan_drag_state = {
1416
- start: { x: evt.clientX, y: evt.clientY },
1417
- initial_x_range: [...zoom_x_range] as [number, number],
1418
- initial_x2_range: [...zoom_x2_range] as [number, number],
1419
- initial_y_range: [...zoom_y_range] as [number, number],
1420
- initial_y2_range: [...zoom_y2_range] as [number, number],
1421
- }
1422
- document.body.style.cursor = `grabbing`
1423
- window.addEventListener(`mousemove`, on_pan_move)
1424
- window.addEventListener(`mouseup`, on_pan_end)
1425
- return
1426
- }
1427
-
1428
- // Store bounding box first, then calculate coords using it
1429
- svg_bounding_box = svg_element.getBoundingClientRect()
1430
-
1431
- // Calculate initial coords using the same bounding box that will be used during drag
1432
- const initial_x = evt.clientX - svg_bounding_box.left
1433
- const initial_y = evt.clientY - svg_bounding_box.top
1434
- const coords = { x: initial_x, y: initial_y }
1435
-
1436
- drag_start_coords = coords
1437
- drag_current_coords = coords
1438
-
1439
- window.addEventListener(`mousemove`, on_window_mouse_move)
1440
- window.addEventListener(`mouseup`, on_window_mouse_up)
1441
- document.body.style.cursor = `crosshair`
1442
- evt.preventDefault()
1443
- }
1444
-
1445
- // Wheel handler for pan (requires focus and shift)
1446
- function handle_wheel(evt: WheelEvent) {
1447
- const pan_enabled = pan?.enabled !== false
1448
- // Only capture wheel when focused AND Shift is held
1449
- // Use shift_held state (tracked via keydown/keyup) for compatibility with synthetic events
1450
- if (!pan_enabled || !is_focused || !shift_held) return
1451
-
1452
- evt.preventDefault()
1453
-
1454
- // Clamp to at least 1 to avoid Infinity deltas when padding equals container size
1455
- const plot_width = Math.max(1, width - pad.l - pad.r)
1456
- const plot_height = Math.max(1, height - pad.t - pad.b)
1457
- const sensitivity = pan?.wheel_sensitivity ?? 1
1458
-
1459
- // Determine pan direction based on wheel delta
1460
- // deltaX for horizontal scroll (trackpad), deltaY for vertical
1461
- const x_delta = pixels_to_data_delta(
1462
- evt.deltaX * sensitivity,
1463
- zoom_x_range,
1464
- plot_width,
1465
- )
1466
- const x2_delta = pixels_to_data_delta(
1467
- evt.deltaX * sensitivity,
1468
- zoom_x2_range,
1469
- plot_width,
1470
- )
1471
- const y_delta = pixels_to_data_delta(
1472
- evt.deltaY * sensitivity,
1473
- zoom_y_range,
1474
- plot_height,
1475
- )
1476
- const y2_delta = pixels_to_data_delta(
1477
- evt.deltaY * sensitivity,
1478
- zoom_y2_range,
1479
- plot_height,
1480
- )
1481
-
1482
- if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
1483
- zoom_x_range = pan_range(zoom_x_range, x_delta)
1484
- zoom_x2_range = pan_range(zoom_x2_range, x2_delta)
1485
- } else {
1486
- zoom_y_range = pan_range(zoom_y_range, y_delta)
1487
- zoom_y2_range = get_synced_y2(zoom_y_range, pan_range(zoom_y2_range, y2_delta))
1488
- }
1489
- }
1490
-
1491
- // Touch handlers for pinch-zoom and two-finger pan
1492
- function handle_touch_start(evt: TouchEvent) {
1493
- const touch_enabled = pan?.enabled !== false && pan?.touch_enabled !== false
1494
- if (!touch_enabled || evt.touches.length !== 2) return
1495
-
1496
- evt.preventDefault()
1497
- const touches = Array.from(evt.touches)
1498
- touch_state = {
1499
- start_touches: touches.map((touch) => ({ x: touch.clientX, y: touch.clientY })),
1500
- initial_x_range: [...zoom_x_range] as [number, number],
1501
- initial_x2_range: [...zoom_x2_range] as [number, number],
1502
- initial_y_range: [...zoom_y_range] as [number, number],
1503
- initial_y2_range: [...zoom_y2_range] as [number, number],
1504
- }
1505
- }
1506
-
1507
- function handle_touch_move(evt: TouchEvent) {
1508
- if (!touch_state || evt.touches.length !== 2) return
1509
- evt.preventDefault()
1510
-
1511
- const [t1, t2] = Array.from(evt.touches)
1512
- const [s1, s2] = touch_state.start_touches
1513
-
1514
- // Calculate center movement for pan
1515
- const start_center = { x: (s1.x + s2.x) / 2, y: (s1.y + s2.y) / 2 }
1516
- const curr_center = {
1517
- x: (t1.clientX + t2.clientX) / 2,
1518
- y: (t1.clientY + t2.clientY) / 2,
1519
- }
1520
- const dx = curr_center.x - start_center.x
1521
- const dy = curr_center.y - start_center.y
1522
-
1523
- // Calculate pinch scale (curr/start so spread = zoom out, pinch = zoom in)
1524
- const start_dist = Math.hypot(s2.x - s1.x, s2.y - s1.y)
1525
- // Guard against zero-distance pinch to avoid Infinity scale
1526
- if (start_dist < Number.EPSILON) return
1527
- const curr_dist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
1528
- const scale = curr_dist / start_dist
1529
-
1530
- // Clamp to at least 1 to avoid Infinity deltas when padding equals container size
1531
- const plot_width = Math.max(1, width - pad.l - pad.r)
1532
- const plot_height = Math.max(1, height - pad.t - pad.b)
1533
-
1534
- // If scale changed significantly, treat as pinch-zoom
1535
- // Also guard against scale being too small to avoid division by zero
1536
- if (Math.abs(scale - 1) > PINCH_ZOOM_THRESHOLD && scale > Number.EPSILON) {
1537
- // Pinch zoom centered on gesture center
1538
- // Divide by scale so spread (scale > 1) = smaller span (zoom in)
1539
- const x_span = touch_state.initial_x_range[1] - touch_state.initial_x_range[0]
1540
- const x2_span = touch_state.initial_x2_range[1] -
1541
- touch_state.initial_x2_range[0]
1542
- const y_span = touch_state.initial_y_range[1] - touch_state.initial_y_range[0]
1543
- const y2_span = touch_state.initial_y2_range[1] -
1544
- touch_state.initial_y2_range[0]
1545
- const x_center =
1546
- (touch_state.initial_x_range[0] + touch_state.initial_x_range[1]) / 2
1547
- const x2_center =
1548
- (touch_state.initial_x2_range[0] + touch_state.initial_x2_range[1]) / 2
1549
- const y_center =
1550
- (touch_state.initial_y_range[0] + touch_state.initial_y_range[1]) / 2
1551
- const y2_center =
1552
- (touch_state.initial_y2_range[0] + touch_state.initial_y2_range[1]) / 2
1553
-
1554
- zoom_x_range = [x_center - x_span / scale / 2, x_center + x_span / scale / 2]
1555
- zoom_x2_range = [
1556
- x2_center - x2_span / scale / 2,
1557
- x2_center + x2_span / scale / 2,
1558
- ]
1559
- zoom_y_range = [y_center - y_span / scale / 2, y_center + y_span / scale / 2]
1560
- zoom_y2_range = get_synced_y2(zoom_y_range, [
1561
- y2_center - y2_span / scale / 2,
1562
- y2_center + y2_span / scale / 2,
1563
- ])
1564
- } else {
1565
- // Pan
1566
- const x_delta = pixels_to_data_delta(
1567
- -dx,
1568
- touch_state.initial_x_range,
1569
- plot_width,
1570
- )
1571
- const x2_delta = pixels_to_data_delta(
1572
- -dx,
1573
- touch_state.initial_x2_range,
1574
- plot_width,
1575
- )
1576
- const y_delta = pixels_to_data_delta(
1577
- dy,
1578
- touch_state.initial_y_range,
1579
- plot_height,
1580
- )
1581
- const y2_delta = pixels_to_data_delta(
1582
- dy,
1583
- touch_state.initial_y2_range,
1584
- plot_height,
1585
- )
1586
- zoom_x_range = pan_range(touch_state.initial_x_range, x_delta)
1587
- zoom_x2_range = pan_range(touch_state.initial_x2_range, x2_delta)
1588
- zoom_y_range = pan_range(touch_state.initial_y_range, y_delta)
1589
- zoom_y2_range = get_synced_y2(
1590
- zoom_y_range,
1591
- pan_range(touch_state.initial_y2_range, y2_delta),
1592
- )
1593
- }
1594
- }
1595
-
1596
- function handle_touch_end() {
1597
- touch_state = null
1598
- }
1599
-
1600
- // tooltip logic: find closest point and update tooltip state
1601
- function update_tooltip_point(x_rel: number, y_rel: number, evt?: MouseEvent) {
1602
- if (!width || !height) return
1603
-
1604
- let closest_point: InternalPoint<Metadata> | null = null
1605
- let closest_series: DataSeries<Metadata> | null = null
1606
- let min_screen_dist_sq = Infinity
1607
- const { threshold_px = 20 } = hover_config // Use configured threshold
1608
- const hover_threshold_px_sq = threshold_px * threshold_px
1609
-
1610
- // Iterate through points to find the closest one in screen coordinates
1611
- for (const series_data of filtered_series) {
1612
- if (!series_data?.filtered_data) continue
1613
-
1614
- const tooltip_use_x2 = series_data.x_axis === `x2`
1615
- const tooltip_x_scale = tooltip_use_x2 ? x2_scale_fn : x_scale_fn
1616
- const tooltip_is_time_x = tooltip_use_x2 ? is_time_x2 : is_time_x
1617
- for (const point of series_data.filtered_data) {
1618
- // Calculate screen coordinates of the point
1619
- const point_cx = tooltip_is_time_x
1620
- ? tooltip_x_scale(new Date(point.x))
1621
- : tooltip_x_scale(point.x)
1622
- const point_cy = (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(
1623
- point.y,
1624
- )
1625
-
1626
- // Calculate squared screen distance between mouse and point
1627
- const screen_dx = x_rel - point_cx
1628
- const screen_dy = y_rel - point_cy
1629
- const screen_distance_sq = screen_dx * screen_dx + screen_dy * screen_dy
1630
-
1631
- // Update if this point is closer
1632
- if (screen_distance_sq < min_screen_dist_sq) {
1633
- min_screen_dist_sq = screen_distance_sq
1634
- closest_point = point
1635
- closest_series = series_data
1636
- }
1637
- }
1638
- }
1639
-
1640
- // Check if the closest point is within the hover threshold
1641
- if (
1642
- closest_point &&
1643
- closest_series &&
1644
- min_screen_dist_sq <= hover_threshold_px_sq
1645
- ) {
1646
- // Construct handler props synchronously to avoid stale derived reads
1647
- const props = construct_handler_props(closest_point)
1648
- tooltip_point = closest_point
1649
- // Construct object matching change signature
1650
- const { x, y, metadata } = closest_point
1651
- change({ x, y, metadata, series: closest_series })
1652
- // Call hover handler with synchronously constructed props
1653
- if (evt && props) {
1654
- on_point_hover?.({ ...props, event: evt, point: closest_point })
1655
- }
1656
- } else {
1657
- tooltip_point = null
1658
- change(null)
1659
- on_point_hover?.(null)
1660
- }
1661
- }
1662
-
1663
- function on_mouse_move(evt: MouseEvent) {
1664
- hovered = true
1665
-
1666
- const coords = get_relative_coords(evt)
1667
- if (!coords) return
1668
-
1669
- update_tooltip_point(coords.x, coords.y, evt)
1670
- }
1671
-
1672
- // Merge user config with defaults before the effect that uses it
1673
- let actual_label_config = $derived({
1674
- sa_iterations: 2000,
1675
- max_labels: 300,
1676
- leader_line_threshold: 15,
1677
- ...label_placement_config,
1678
- })
1679
-
1680
- $effect(() => {
1681
- if (!width || !height) {
1682
- label_positions = {}
1683
- return
1684
- }
1685
-
1686
- label_positions = compute_label_positions(
1687
- filtered_series,
1688
- actual_label_config,
1689
- { x_scale_fn, y_scale_fn, y2_scale_fn, x_axis: final_x_axis },
1690
- { width, height, pad },
1691
- )
1692
- })
1693
-
1694
- // Legend drag handlers
1695
- function handle_legend_drag_start(event: MouseEvent) {
1696
- if (!svg_element) return
1697
-
1698
- legend_is_dragging = true
1699
-
1700
- // Get the actual rendered position of the legend element (accounts for transforms)
1701
- const legend_el = event.currentTarget
1702
- if (!(legend_el instanceof HTMLElement)) return
1703
- const legend_rect = legend_el.getBoundingClientRect()
1704
-
1705
- // Calculate offset from mouse to legend's actual rendered position relative to SVG
1706
- const [x, y] = [event.clientX - legend_rect.left, event.clientY - legend_rect.top]
1707
- legend_drag_offset = { x, y }
1708
- }
1709
-
1710
- function handle_legend_drag(event: MouseEvent) {
1711
- if (!legend_is_dragging || !svg_element || !legend_element) return
1712
-
1713
- const svg_rect = svg_element.getBoundingClientRect()
1714
-
1715
- // Calculate new position: mouse position relative to SVG, minus the offset within the legend
1716
- const new_x = event.clientX - svg_rect.left - legend_drag_offset.x
1717
- const new_y = event.clientY - svg_rect.top - legend_drag_offset.y
1718
-
1719
- // Get actual legend dimensions for accurate bounds checking using the bound element reference
1720
- const { width: legend_width, height: legend_height } = legend_element
1721
- .getBoundingClientRect()
1722
-
1723
- // Constrain to plot bounds using measured legend size
1724
- const constrained_x = Math.max(0, Math.min(width - legend_width, new_x))
1725
- const constrained_y = Math.max(0, Math.min(height - legend_height, new_y))
1726
-
1727
- legend_manual_position = { x: constrained_x, y: constrained_y }
1728
- }
1729
-
1730
- function get_screen_coords(point: Point, data_series?: DataSeries): [number, number] {
1731
- // convert data coordinates to potentially non-finite screen coordinates
1732
- const use_x2 = data_series?.x_axis === `x2`
1733
- const active_x_scale = use_x2 ? x2_scale_fn : x_scale_fn
1734
- const active_is_time_x = use_x2 ? is_time_x2 : is_time_x
1735
- const screen_x = active_is_time_x
1736
- ? active_x_scale(new Date(point.x))
1737
- : active_x_scale(point.x)
1738
-
1739
- const y_val = point.y
1740
- // Determine which y-scale to use based on series y_axis property
1741
- const use_y2 = data_series?.y_axis === `y2`
1742
- const y_scale = use_y2 ? y2_scale_fn : y_scale_fn
1743
- const y_scale_type = use_y2
1744
- ? get_scale_type_name(final_y2_axis.scale_type)
1745
- : get_scale_type_name(final_y_axis.scale_type)
1746
- // Only log scale needs domain clamping; linear and arcsinh can handle any value
1747
- const min_domain_y = y_scale_type === `log` ? y_scale.domain()[0] : -Infinity
1748
- const safe_y_val = y_scale_type === `log` ? Math.max(y_val, min_domain_y) : y_val
1749
- const screen_y = y_scale(safe_y_val) // This might be non-finite
1750
-
1751
- return [screen_x, screen_y]
1752
- }
1753
-
1754
- // Helper function to construct ScatterHandlerProps synchronously from InternalPoint
1755
- function construct_handler_props(
1756
- point: InternalPoint<Metadata>,
1757
- ): ScatterHandlerProps<Metadata> | null {
1758
- const hovered_series = series_with_ids[point.series_idx]
1759
- if (!hovered_series) return null
1760
- const { x, y, color_value, metadata, series_idx } = point
1761
- const handler_use_x2 = hovered_series.x_axis === `x2`
1762
- const handler_x_scale = handler_use_x2 ? x2_scale_fn : x_scale_fn
1763
- const handler_is_time_x = handler_use_x2 ? is_time_x2 : is_time_x
1764
- const cx = handler_is_time_x ? handler_x_scale(new Date(x)) : handler_x_scale(x)
1765
- const cy = (hovered_series.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(y)
1766
- const active_x_config = handler_use_x2 ? final_x2_axis : final_x_axis
1767
- const active_y_config = hovered_series.y_axis === `y2`
1768
- ? final_y2_axis
1769
- : final_y_axis
1770
- const coords = {
1771
- x,
1772
- y,
1773
- cx,
1774
- cy,
1775
- x_axis: active_x_config,
1776
- x2_axis: final_x2_axis,
1777
- y_axis: active_y_config,
1778
- y2_axis: final_y2_axis,
1779
- }
1780
- return {
1781
- ...coords,
1782
- fullscreen,
1783
- metadata,
1784
- label: hovered_series.label ?? null,
1785
- series_idx,
1786
- x_formatted: format_value(x, active_x_config.format || `.3~s`),
1787
- y_formatted: format_value(y, active_y_config.format || `.3~s`),
1788
- color_value: color_value ?? null,
1789
- colorbar: {
1790
- value: color_value ?? null,
1791
- title: color_bar?.title ?? null,
1792
- scale: color_scale,
1793
- tick_format: color_bar?.tick_format ?? null,
1794
- },
1795
- }
1796
- }
1797
-
1798
- // Derive handler props from hovered point for both tooltip and event handlers
1799
- let handler_props = $derived.by((): ScatterHandlerProps<Metadata> | null => {
1800
- if (!tooltip_point) return null
1801
- return construct_handler_props(tooltip_point)
1802
- })
1803
-
1804
- let using_controls = $derived(controls.show)
1805
- let has_multiple_series = $derived(series_with_ids.filter(Boolean).length > 1)
1806
-
1807
- // Precompute non-click event names from point_events so we don't rebuild
1808
- // the entries array on every point render.
1809
- let point_event_names = $derived(
1810
- point_events
1811
- ? Object.keys(point_events).filter((name) => name !== `onclick`)
1812
- : [],
1813
- )
1814
-
1815
- // Set theme-aware background when entering fullscreen
1816
- $effect(() => {
1817
- set_fullscreen_bg(wrapper, fullscreen, `--scatter-fullscreen-bg`)
1818
- })
1819
-
1820
- // State accessors for shared axis change handler
1821
- const axis_state: AxisChangeState<DataSeries<Metadata>> = {
1822
- get_axis: (axis) => {
1823
- if (axis === `x`) return x_axis
1824
- if (axis === `x2`) return x2_axis
1825
- if (axis === `y`) return y_axis
1826
- return y2_axis
1827
- },
1828
- set_axis: (axis, config) => {
1829
- // Spread into existing state to preserve merged type structure
1830
- if (axis === `x`) x_axis = { ...x_axis, ...config }
1831
- else if (axis === `x2`) x2_axis = { ...x2_axis, ...config }
1832
- else if (axis === `y`) y_axis = { ...y_axis, ...config }
1833
- else y2_axis = { ...y2_axis, ...config }
1834
- },
1835
- get_series: () => series,
1836
- set_series: (new_series) => (series = new_series),
1837
- get_loading: () => axis_loading,
1838
- set_loading: (axis) => (axis_loading = axis),
1839
- }
1840
-
1841
- // Create shared handler bound to this component's state
1842
- // Using $derived so handler updates when callback props change
1843
- const handle_axis_change = $derived(create_axis_change_handler(
1844
- axis_state,
1845
- data_loader,
1846
- on_axis_change,
1847
- on_error,
1848
- ))
1849
-
1850
- let auto_load_attempted = false // prevent infinite retries on failure
1851
-
1852
- // Auto-load data if series is empty but options exist (runs once)
1853
- $effect(() => {
1854
- if (series.length === 0 && data_loader && !auto_load_attempted) {
1855
- // Check x-axis first, then y-axis
1856
- if (x_axis.options?.length) {
1857
- auto_load_attempted = true
1858
- const first_key = x_axis.selected_key ?? x_axis.options[0].key
1859
- handle_axis_change(`x`, first_key).catch(() => {})
1860
- } else if (y_axis.options?.length) {
1861
- auto_load_attempted = true
1862
- const first_key = y_axis.selected_key ?? y_axis.options[0].key
1863
- handle_axis_change(`y`, first_key).catch(() => {})
1864
- }
1865
- }
1866
- })
1867
- </script>
1868
-
1869
- {#snippet fill_regions_layer(fills: typeof computed_fills)}
1870
- {#each fills as fill (fill.id ?? fill.idx)}
1871
- {#each fill.path_segments as
1872
- path_d,
1873
- segment_idx
1874
- (`${fill.id ?? fill.idx}-${segment_idx}`)
1875
- }
1876
- <FillArea
1877
- region={fill}
1878
- region_idx={fill.idx}
1879
- path={path_d}
1880
- {clip_path_id}
1881
- {x_scale_fn}
1882
- {y_scale_fn}
1883
- hovered_region={hovered_fill_idx}
1884
- on_click={(event) => {
1885
- fill.on_click?.(event)
1886
- on_fill_click?.(event)
1887
- }}
1888
- on_hover={(event) => {
1889
- hovered_fill_idx = event?.region_idx ?? null
1890
- fill.on_hover?.(event)
1891
- on_fill_hover?.(event)
1892
- }}
1893
- />
1894
- {/each}
1895
- {/each}
1896
- {/snippet}
1897
-
1898
- {#snippet ref_lines_layer(lines: IndexedRefLine[])}
1899
- {#each lines as line (line.id ?? line.idx)}
1900
- <ReferenceLine
1901
- ref_line={line}
1902
- line_idx={line.idx}
1903
- x_min={line.x_axis === `x2` ? x2_min : x_min}
1904
- x_max={line.x_axis === `x2` ? x2_max : x_max}
1905
- y_min={line.y_axis === `y2` ? y2_min : y_min}
1906
- y_max={line.y_axis === `y2` ? y2_max : y_max}
1907
- x_scale={x_scale_fn}
1908
- x2_scale={x2_scale_fn}
1909
- y_scale={y_scale_fn}
1910
- y2_scale={y2_scale_fn}
1911
- {clip_path_id}
1912
- hovered_line_idx={hovered_ref_line_idx}
1913
- on_click={(event: RefLineEvent) => {
1914
- line.on_click?.(event)
1915
- on_ref_line_click?.(event)
1916
- }}
1917
- on_hover={(event: RefLineEvent | null) => {
1918
- hovered_ref_line_idx = event?.line_idx ?? null
1919
- line.on_hover?.(event)
1920
- on_ref_line_hover?.(event)
1921
- }}
1922
- />
1923
- {/each}
1924
- {/snippet}
1925
-
1926
- <svelte:window
1927
- onkeydown={(evt) => {
1928
- if (evt.key === `Escape` && fullscreen) {
1929
- evt.preventDefault()
1930
- fullscreen = false
1931
- }
1932
- if (evt.key === `Shift`) shift_held = true
1933
- }}
1934
- onkeyup={(evt) => {
1935
- if (evt.key === `Shift`) shift_held = false
1936
- }}
1937
- />
1938
-
1939
- <div
1940
- bind:this={wrapper}
1941
- bind:clientWidth={width}
1942
- bind:clientHeight={height}
1943
- {...rest}
1944
- class="scatter {rest.class ?? ``}"
1945
- class:fullscreen
1946
- >
1947
- {#if width && height}
1948
- <div class="header-controls">
1949
- {@render header_controls?.({ height, width, fullscreen })}
1950
- {#if fullscreen_toggle}
1951
- <FullscreenToggle bind:fullscreen />
1952
- {/if}
1953
- </div>
1954
- <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
1955
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
1956
- <svg
1957
- bind:this={svg_element}
1958
- role="application"
1959
- aria-label={rest[`aria-label`] ??
1960
- ([final_x_axis.label, final_y_axis.label].filter(Boolean).join(` vs `) ||
1961
- `Scatter plot`)}
1962
- tabindex="0"
1963
- onfocusin={() => (is_focused = true)}
1964
- onfocusout={() => (is_focused = false)}
1965
- onmouseenter={() => (hovered = true)}
1966
- onmousedown={handle_mouse_down}
1967
- onmousemove={(evt: MouseEvent) => {
1968
- // Only find closest point if not actively dragging
1969
- if (!drag_start_coords && !pan_drag_state) on_mouse_move(evt)
1970
- }}
1971
- onmouseleave={() => {
1972
- hovered = false
1973
- tooltip_point = null
1974
- on_point_hover?.(null)
1975
- }}
1976
- ondblclick={() => {
1977
- // Reset to current auto ranges (not stale initial_*_range which may have expanded)
1978
- // This ensures lazy expansion restarts fresh from current data bounds
1979
- initial_x_range = [...auto_x_range] as [number, number]
1980
- initial_x2_range = [...auto_x2_range] as [number, number]
1981
- initial_y_range = [...auto_y_range] as [number, number]
1982
- initial_y2_range = [...auto_y2_range] as [number, number]
1983
- zoom_x_range = [...auto_x_range] as [number, number]
1984
- zoom_x2_range = [...auto_x2_range] as [number, number]
1985
- zoom_y_range = [...auto_y_range] as [number, number]
1986
- zoom_y2_range = get_synced_y2(auto_y_range, [...auto_y2_range] as Vec2)
1987
- // Also reset axis props so future data changes recalculate auto ranges
1988
- x_axis = { ...x_axis, range: [null, null] }
1989
- x2_axis = { ...x2_axis, range: [null, null] }
1990
- y_axis = { ...y_axis, range: [null, null] }
1991
- y2_axis = { ...y2_axis, range: [null, null] }
1992
- }}
1993
- onwheel={handle_wheel}
1994
- ontouchstart={handle_touch_start}
1995
- ontouchmove={handle_touch_move}
1996
- ontouchend={handle_touch_end}
1997
- style:cursor={pan_drag_state
1998
- ? `grabbing`
1999
- : shift_held && pan?.enabled !== false
2000
- ? `grab`
2001
- : `crosshair`}
2002
- >
2003
- {@render user_content?.({
2004
- height,
2005
- width,
2006
- x_scale_fn,
2007
- x2_scale_fn,
2008
- y_scale_fn,
2009
- y2_scale_fn,
2010
- pad,
2011
- x_range: [x_min, x_max],
2012
- x2_range: [x2_min, x2_max],
2013
- y_range: [y_min, y_max],
2014
- y2_range: [y2_min, y2_max],
2015
- fullscreen,
2016
- })}
2017
-
2018
- <!-- Fill regions: below grid -->
2019
- {@render fill_regions_layer(fills_by_z.below_grid)}
2020
- <!-- Reference lines: below grid -->
2021
- {@render ref_lines_layer(ref_lines_by_z.below_grid)}
2022
-
2023
- <g class="x-axis">
2024
- {#if width > 0 && height > 0}
2025
- {#each x_tick_values as tick (tick)}
2026
- {@const tick_pos_raw = is_time_x ? x_scale_fn(new Date(tick)) : x_scale_fn(tick)}
2027
- {#if isFinite(tick_pos_raw)}
2028
- // Check if tick position is finite
2029
- {@const tick_pos = tick_pos_raw}
2030
- {#if tick_pos >= pad.l && tick_pos <= width - pad.r}
2031
- {@const inside = final_x_axis.tick?.label?.inside ?? false}
2032
- <g class="tick" transform="translate({tick_pos}, {height - pad.b})">
2033
- {#if final_display.x_grid}
2034
- <line
2035
- y1={-(height - pad.b - pad.t)}
2036
- y2="0"
2037
- {...DEFAULT_GRID_STYLE}
2038
- {...final_x_axis.grid_style}
2039
- />
2040
- {/if}
2041
- <line y1="0" y2={inside ? -5 : 5} stroke="var(--border-color, gray)" />
2042
-
2043
- {#if tick >= Math.min(x_min, x_max) && tick <= Math.max(x_min, x_max)}
2044
- {@const base_y = inside ? -8 : 20}
2045
- {@const shift = final_x_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
2046
- {@const x = shift.x ?? 0}
2047
- {@const y = base_y + (shift.y ?? 0)}
2048
- {@const custom_label = get_tick_label(tick, final_x_axis.ticks)}
2049
- {@const dominant_baseline = inside ? `auto` : `hanging`}
2050
- <text
2051
- {x}
2052
- {y}
2053
- dominant-baseline={dominant_baseline}
2054
- fill={final_x_axis.color}
2055
- >
2056
- {custom_label ?? format_value(tick, final_x_axis.format ?? ``)}
2057
- </text>
2058
- {/if}
2059
- </g>
2060
- {/if}
2061
- {/if}
2062
- {/each}
2063
- {/if}
2064
-
2065
- <!-- Current frame indicator -->
2066
- {#if current_x_value != null}
2067
- {@const current_pos_raw = is_time_x
2068
- ? x_scale_fn(new Date(current_x_value))
2069
- : x_scale_fn(current_x_value)}
2070
- {#if isFinite(current_pos_raw)}
2071
- {@const current_pos = current_pos_raw}
2072
- {#if current_pos >= pad.l && current_pos <= width - pad.r}
2073
- {@const active_tick_height = 7}
2074
- <rect
2075
- x={current_pos - 1.5}
2076
- y={height - pad.b - active_tick_height / 2}
2077
- width="3"
2078
- height={active_tick_height}
2079
- fill="var(--scatter-current-frame-color, #ff6b35)"
2080
- stroke="white"
2081
- stroke-width="1"
2082
- class="current-frame-indicator"
2083
- />
2084
- {/if}
2085
- {/if}
2086
- {/if}
2087
-
2088
- {#if final_x_axis.label || final_x_axis.options?.length}
2089
- {@const { label_shift, label = ``, options, selected_key, color } = final_x_axis}
2090
- <AxisLabel
2091
- x={width / 2 + (label_shift?.x ?? 0)}
2092
- y={height - pad.b - (label_shift?.y ?? -40)}
2093
- {label}
2094
- {options}
2095
- {selected_key}
2096
- loading={axis_loading === `x`}
2097
- axis_type="x"
2098
- {color}
2099
- on_select={(key) => handle_axis_change(`x`, key)}
2100
- />
2101
- {/if}
2102
- </g>
2103
-
2104
- <g class="y-axis">
2105
- {#if width > 0 && height > 0}
2106
- {#each y_tick_values as tick, idx (tick)}
2107
- {@const tick_pos_raw = y_scale_fn(tick)}
2108
- {#if isFinite(tick_pos_raw)}
2109
- // Check if tick position is finite
2110
- {@const tick_pos = tick_pos_raw}
2111
- {#if tick_pos >= pad.t && tick_pos <= height - pad.b}
2112
- {@const inside = final_y_axis.tick?.label?.inside ?? false}
2113
- <g class="tick" transform="translate({pad.l}, {tick_pos})">
2114
- {#if final_display.y_grid}
2115
- <line
2116
- x1="0"
2117
- x2={width - pad.l - pad.r}
2118
- {...DEFAULT_GRID_STYLE}
2119
- {...final_y_axis.grid_style}
2120
- />
2121
- {/if}
2122
- <line
2123
- x1={inside ? 0 : -5}
2124
- x2={inside ? 5 : 0}
2125
- stroke="var(--border-color, gray)"
2126
- />
2127
-
2128
- {#if tick >= Math.min(y_min, y_max) && tick <= Math.max(y_min, y_max)}
2129
- {@const base_x = inside ? 8 : -8}
2130
- {@const shift = final_y_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
2131
- {@const x = base_x + (shift.x ?? 0)}
2132
- {@const y = shift.y ?? 0}
2133
- {@const custom_label = get_tick_label(tick, final_y_axis.ticks)}
2134
- {@const text_anchor = inside ? `start` : `end`}
2135
- <text {x} {y} text-anchor={text_anchor} fill={final_y_axis.color}>
2136
- {custom_label ?? format_value(tick, final_y_axis.format ?? ``)}
2137
- {#if final_y_axis.unit && idx === 0}
2138
- &zwnj;&ensp;{final_y_axis.unit}
2139
- {/if}
2140
- </text>
2141
- {/if}
2142
- </g>
2143
- {/if}
2144
- {/if}
2145
- {/each}
2146
- {/if}
2147
-
2148
- {#if height > 0 && (final_y_axis.label || final_y_axis.options?.length)}
2149
- {@const { label_shift, label = ``, options, selected_key, color, tick } =
2150
- final_y_axis}
2151
- {@const y_inside = tick?.label?.inside ?? false}
2152
- {@const y_label_x = Math.max(
2153
- 12,
2154
- pad.l - (y_inside ? 0 : tick_label_widths.y_max) - LABEL_GAP_DEFAULT,
2155
- ) +
2156
- (label_shift?.x ?? 0)}
2157
- <AxisLabel
2158
- x={y_label_x}
2159
- y={pad.t + (height - pad.t - pad.b) / 2 + (label_shift?.y ?? 0)}
2160
- rotate
2161
- {label}
2162
- {options}
2163
- {selected_key}
2164
- loading={axis_loading === `y`}
2165
- axis_type="y"
2166
- {color}
2167
- on_select={(key) => handle_axis_change(`y`, key)}
2168
- />
2169
- {/if}
2170
- </g>
2171
-
2172
- <!-- Y2-axis (Right) -->
2173
- {#if y2_points.length > 0}
2174
- <g class="y2-axis">
2175
- {#if width > 0 && height > 0}
2176
- {#each y2_tick_values as tick, idx (tick)}
2177
- {@const tick_pos_raw = y2_scale_fn(tick)}
2178
- {#if isFinite(tick_pos_raw)}
2179
- // Check if tick position is finite
2180
- {@const tick_pos = tick_pos_raw}
2181
- {#if tick_pos >= pad.t && tick_pos <= height - pad.b}
2182
- {@const inside = final_y2_axis.tick?.label?.inside ?? false}
2183
- <g class="tick" transform="translate({width - pad.r}, {tick_pos})">
2184
- {#if final_display.y2_grid}
2185
- <line
2186
- x1={-(width - pad.l - pad.r)}
2187
- x2="0"
2188
- {...DEFAULT_GRID_STYLE}
2189
- {...final_y2_axis.grid_style}
2190
- />
2191
- {/if}
2192
- <line
2193
- x1={inside ? -5 : 0}
2194
- x2={inside ? 0 : 5}
2195
- stroke="var(--border-color, gray)"
2196
- />
2197
-
2198
- {#if tick >= Math.min(y2_min, y2_max) && tick <= Math.max(y2_min, y2_max)}
2199
- {@const base_x = inside ? -8 : 8}
2200
- {@const shift = final_y2_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
2201
- {@const x = base_x + (shift.x ?? 0)}
2202
- {@const y = shift.y ?? 0}
2203
- {@const custom_label = get_tick_label(tick, final_y2_axis.ticks)}
2204
- {@const text_anchor = inside ? `end` : `start`}
2205
- <text {x} {y} text-anchor={text_anchor} fill={final_y2_axis.color}>
2206
- {custom_label ?? format_value(tick, final_y2_axis.format ?? ``)}
2207
- {#if final_y2_axis.unit && idx === 0}
2208
- &zwnj;&ensp;{final_y2_axis.unit}
2209
- {/if}
2210
- </text>
2211
- {/if}
2212
- </g>
2213
- {/if}
2214
- {/if}
2215
- {/each}
2216
- {/if}
2217
-
2218
- {#if height > 0 && (final_y2_axis.label || final_y2_axis.options?.length)}
2219
- {@const { label_shift, label = ``, options, selected_key, color, tick } =
2220
- final_y2_axis}
2221
- {@const inside = tick?.label?.inside ?? false}
2222
- {@const tick_shift = inside ? 0 : (tick?.label?.shift?.x ?? 0) + 8}
2223
- {@const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max}
2224
- <AxisLabel
2225
- x={width - pad.r + tick_shift + tick_width_contribution +
2226
- LABEL_GAP_DEFAULT + (label_shift?.x ?? 0)}
2227
- y={pad.t + (height - pad.t - pad.b) / 2 + (label_shift?.y ?? 0)}
2228
- rotate
2229
- {label}
2230
- {options}
2231
- {selected_key}
2232
- loading={axis_loading === `y2`}
2233
- axis_type="y2"
2234
- {color}
2235
- on_select={(key) => handle_axis_change(`y2`, key)}
2236
- />
2237
- {/if}
2238
- </g>
2239
- {/if}
2240
-
2241
- <!-- X2-axis (Top) -->
2242
- {#if x2_points.length > 0}
2243
- <g class="x2-axis">
2244
- {#if width > 0 && height > 0}
2245
- {#each x2_tick_values as tick (tick)}
2246
- {@const tick_pos_raw = is_time_x2
2247
- ? x2_scale_fn(new Date(tick))
2248
- : x2_scale_fn(tick)}
2249
- {#if isFinite(tick_pos_raw)}
2250
- {@const tick_pos = tick_pos_raw}
2251
- {#if tick_pos >= pad.l && tick_pos <= width - pad.r}
2252
- {@const inside = final_x2_axis.tick?.label?.inside ?? false}
2253
- <g class="tick" transform="translate({tick_pos}, {pad.t})">
2254
- {#if final_display.x2_grid}
2255
- <line
2256
- y1="0"
2257
- y2={height - pad.b - pad.t}
2258
- {...DEFAULT_GRID_STYLE}
2259
- {...final_x2_axis.grid_style}
2260
- />
2261
- {/if}
2262
- <line
2263
- y1="0"
2264
- y2={inside ? 5 : -5}
2265
- stroke={final_x2_axis.color || `var(--border-color, gray)`}
2266
- />
2267
-
2268
- {#if tick >= Math.min(x2_min, x2_max) && tick <= Math.max(x2_min, x2_max)}
2269
- {@const base_y = inside ? 8 : -20}
2270
- {@const shift = final_x2_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
2271
- {@const x = shift.x ?? 0}
2272
- {@const y = base_y + (shift.y ?? 0)}
2273
- {@const custom_label = get_tick_label(tick, final_x2_axis.ticks)}
2274
- {@const dominant_baseline = inside ? `hanging` : `auto`}
2275
- <text
2276
- {x}
2277
- {y}
2278
- dominant-baseline={dominant_baseline}
2279
- fill={final_x2_axis.color}
2280
- >
2281
- {custom_label ?? format_value(tick, final_x2_axis.format ?? ``)}
2282
- </text>
2283
- {/if}
2284
- </g>
2285
- {/if}
2286
- {/if}
2287
- {/each}
2288
- {/if}
2289
-
2290
- {#if final_x2_axis.label || final_x2_axis.options?.length}
2291
- {@const { label_shift, label = ``, options, selected_key, color } =
2292
- final_x2_axis}
2293
- <AxisLabel
2294
- x={width / 2 + (label_shift?.x ?? 0)}
2295
- y={Math.max(12, pad.t - (label_shift?.y ?? 40))}
2296
- {label}
2297
- {options}
2298
- {selected_key}
2299
- loading={axis_loading === `x2`}
2300
- axis_type="x2"
2301
- {color}
2302
- on_select={(key) => handle_axis_change(`x2`, key)}
2303
- />
2304
- {/if}
2305
- </g>
2306
- {/if}
2307
-
2308
- <!-- Tooltip rendered inside overlay (moved outside SVG for stacking above colorbar) -->
2309
-
2310
- <ZoomRect start={drag_start_coords} current={drag_current_coords} />
2311
-
2312
- <ZeroLines
2313
- display={final_display}
2314
- {x_scale_fn}
2315
- {x2_scale_fn}
2316
- {y_scale_fn}
2317
- {y2_scale_fn}
2318
- x_range={zoom_x_range}
2319
- x2_range={zoom_x2_range}
2320
- y_range={zoom_y_range}
2321
- y2_range={zoom_y2_range}
2322
- x_scale_type={final_x_axis.scale_type}
2323
- x2_scale_type={final_x2_axis.scale_type}
2324
- y_scale_type={final_y_axis.scale_type}
2325
- y2_scale_type={final_y2_axis.scale_type}
2326
- x_is_time={is_time_x}
2327
- x2_is_time={is_time_x2}
2328
- has_x2={x2_points.length > 0}
2329
- has_y2={y2_points.length > 0}
2330
- {width}
2331
- {height}
2332
- {pad}
2333
- />
2334
-
2335
- <defs>
2336
- <clipPath id={clip_path_id}>
2337
- <rect
2338
- x={clip_area.x}
2339
- y={clip_area.y}
2340
- width={clip_area.width}
2341
- height={clip_area.height}
2342
- />
2343
- </clipPath>
2344
- </defs>
2345
-
2346
- <!-- Fill regions: below lines (default z-index) -->
2347
- {@render fill_regions_layer(fills_by_z.below_lines)}
2348
- <!-- Reference lines: below lines (default z-index) -->
2349
- {@render ref_lines_layer(ref_lines_by_z.below_lines)}
2350
-
2351
- <!-- Lines -->
2352
- {#if styles.show_lines}
2353
- {#each filtered_series ?? [] as series_data (series_data._id)}
2354
- {@const series_markers = series_data.markers ?? DEFAULT_MARKERS}
2355
- {@const series_default_color = get_series_color(series_data.orig_series_idx ?? 0)}
2356
- <g
2357
- data-series-id={series_data._id}
2358
- clip-path="url(#{clip_path_id})"
2359
- opacity={hovered_legend_series_idx !== null &&
2360
- hovered_legend_series_idx !== series_data.orig_series_idx
2361
- ? 0.25
2362
- : 1}
2363
- >
2364
- {#if series_markers?.includes(`line`)}
2365
- {@const all_line_points = series_data.x.map((x, idx) => ({
2366
- x,
2367
- y: series_data.y[idx],
2368
- }))}
2369
- {@const finite_screen_points = all_line_points
2370
- .map((point) => get_screen_coords(point, series_data))
2371
- .filter(([sx, sy]) => isFinite(sx) && isFinite(sy))}
2372
- {@const apply_line_controls = using_controls &&
2373
- (!has_multiple_series ||
2374
- series_data._id === series_with_ids[selected_series_idx]?._id)}
2375
- {@const ls = series_data.line_style}
2376
- {@const tc = (key: string) => apply_line_controls && touched.has(key)}
2377
- {@const color_fallback = ls?.stroke ??
2378
- (Array.isArray(series_data.point_style)
2379
- ? series_data.point_style[0]?.fill
2380
- : series_data.point_style?.fill) ??
2381
- (series_data.color_values?.[0] != null
2382
- ? color_scale_fn(series_data.color_values[0])
2383
- : series_default_color)}
2384
- <Line
2385
- points={finite_screen_points}
2386
- origin={[
2387
- is_time_x ? x_scale_fn(new Date(x_min)) : x_scale_fn(x_min),
2388
- series_data.y_axis === `y2` ? y2_scale_fn(y2_min) : y_scale_fn(y_min),
2389
- ]}
2390
- line_color={(tc(`line.color`) ? styles.line?.color : null) ?? color_fallback}
2391
- line_width={(tc(`line.width`) ? styles.line?.width : null) ?? ls?.stroke_width ?? 2}
2392
- line_dash={(tc(`line.dash`) ? styles.line?.dash : null) ?? ls?.line_dash}
2393
- area_color="transparent"
2394
- {line_tween}
2395
- />
2396
- {/if}
2397
- </g>
2398
- {/each}
2399
- {/if}
2400
-
2401
- <!-- Fill regions: below points -->
2402
- {@render fill_regions_layer(fills_by_z.below_points)}
2403
- <!-- Reference lines: below points -->
2404
- {@render ref_lines_layer(ref_lines_by_z.below_points)}
2405
-
2406
- <!-- Points -->
2407
- {#if styles.show_points}
2408
- {#each filtered_series ?? [] as series_data (series_data._id)}
2409
- {@const series_markers = series_data.markers ?? DEFAULT_MARKERS}
2410
- {@const series_default_color = get_series_color(series_data.orig_series_idx ?? 0)}
2411
- {@const series_default_symbol = get_series_symbol(series_data.orig_series_idx ?? 0)}
2412
- <g data-series-id={series_data._id}>
2413
- {#if series_markers?.includes(`points`)}
2414
- {#each series_data.filtered_data as
2415
- point
2416
- (`${point.series_idx}-${point.point_idx}`)
2417
- }
2418
- {@const label_id = `${point.series_idx}-${point.point_idx}`}
2419
- {@const calculated_label_pos = label_positions[label_id]}
2420
- {@const label_style = point.point_label ?? {}}
2421
- {@const final_label = calculated_label_pos
2422
- ? {
2423
- ...label_style,
2424
- offset: {
2425
- x: calculated_label_pos.x -
2426
- (is_time_x ? x_scale_fn(new Date(point.x)) : x_scale_fn(point.x)),
2427
- y: calculated_label_pos.y - (series_data.y_axis === `y2`
2428
- ? y2_scale_fn(point.y)
2429
- : y_scale_fn(point.y)),
2430
- },
2431
- }
2432
- : label_style}
2433
- {@const [raw_screen_x, raw_screen_y] = get_screen_coords(point, series_data)}
2434
- {@const screen_x = isFinite(raw_screen_x) ? raw_screen_x : x_scale_fn.range()[0]}
2435
- {@const screen_y = isFinite(raw_screen_y)
2436
- ? raw_screen_y
2437
- : (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn).range()[0]}
2438
- {@const apply_controls = using_controls &&
2439
- (!has_multiple_series ||
2440
- series_data._id === series_with_ids[selected_series_idx]?._id)}
2441
- {@const pt = point.point_style}
2442
- {@const tc = (key: string) => apply_controls && touched.has(key)}
2443
- {@const computed_radius = point.size_value != null
2444
- ? size_scale_fn(point.size_value)
2445
- : (tc(`point.size`) ? styles.point?.size : null) ?? pt?.radius ?? 4}
2446
- <ScatterPoint
2447
- x={screen_x}
2448
- y={screen_y}
2449
- is_dimmed={hovered_legend_series_idx !== null &&
2450
- hovered_legend_series_idx !== point.series_idx}
2451
- is_hovered={tooltip_point?.series_idx === point.series_idx &&
2452
- tooltip_point?.point_idx === point.point_idx}
2453
- is_selected={selected_point?.series_idx === point.series_idx &&
2454
- selected_point?.point_idx === point.point_idx}
2455
- leader_line_threshold={actual_label_config.leader_line_threshold}
2456
- style={{
2457
- symbol_type: pt?.symbol_type ?? series_default_symbol,
2458
- ...pt,
2459
- radius: computed_radius,
2460
- stroke_width:
2461
- (tc(`point.stroke_width`) ? styles.point?.stroke_width : null) ??
2462
- pt?.stroke_width ?? 1,
2463
- stroke:
2464
- (tc(`point.stroke_color`) ? styles.point?.stroke_color : null) ??
2465
- pt?.stroke ?? `#000`,
2466
- stroke_opacity:
2467
- (tc(`point.stroke_opacity`) ? styles.point?.stroke_opacity : null) ??
2468
- pt?.stroke_opacity ?? 1,
2469
- fill_opacity: (tc(`point.opacity`) ? styles.point?.opacity : null) ??
2470
- pt?.fill_opacity ?? 1,
2471
- cursor: (on_point_click || point_events?.onclick)
2472
- ? `pointer`
2473
- : undefined,
2474
- }}
2475
- hover={point.point_hover ?? {}}
2476
- label={final_label}
2477
- offset={point.point_offset ?? { x: 0, y: 0 }}
2478
- {point_tween}
2479
- origin={{ x: plot_center_x, y: plot_center_y }}
2480
- --point-fill-color={point.color_value != null
2481
- ? color_scale_fn(point.color_value)
2482
- : (tc(`point.color`) ? styles.point?.color : null) ?? pt?.fill ??
2483
- series_default_color}
2484
- {...point_events &&
2485
- Object.fromEntries(
2486
- point_event_names.map((name) => [name, (event: Event) =>
2487
- point_events?.[name]?.({ point, event })]
2488
- ),
2489
- )}
2490
- onclick={(event: MouseEvent) => {
2491
- // Call user-provided onclick handler first if it exists
2492
- point_events?.onclick?.({ point, event })
2493
- // then handle internal logic
2494
- const props = construct_handler_props(point)
2495
- tooltip_point = point
2496
- if (props) on_point_click?.({ ...props, event, point })
2497
- }}
2498
- />
2499
- {/each}
2500
- {/if}
2501
- </g>
2502
- {/each}
2503
- {/if}
2504
-
2505
- <!-- Fill regions: above all -->
2506
- {@render fill_regions_layer(fills_by_z.above_all)}
2507
- <!-- Reference lines: above all -->
2508
- {@render ref_lines_layer(ref_lines_by_z.above_all)}
2509
- </svg>
2510
-
2511
- <!-- Tooltip overlay above all plot overlays (legend, colorbar) -->
2512
- {#if handler_props && hovered && tooltip_point}
2513
- {@const { color_value, point_label, point_style, series_idx } = tooltip_point}
2514
- {@const hovered_series = series_with_ids[series_idx]}
2515
- {@const series_markers = hovered_series?.markers ?? DEFAULT_MARKERS}
2516
- {@const is_transparent_or_none = (color: string | undefined | null): boolean =>
2517
- !color ||
2518
- color === `none` ||
2519
- color === `transparent` ||
2520
- /rgba\([^)]+[,/]\s*0(\.0*)?\s*\)$/.test(color)}
2521
- {@const tooltip_bg_color = (() => {
2522
- const scale_color = color_value != null
2523
- ? color_scale_fn(color_value)
2524
- : undefined
2525
- if (!is_transparent_or_none(scale_color)) return scale_color
2526
- const fill_color = point_style?.fill
2527
- if (!is_transparent_or_none(fill_color)) return fill_color
2528
- if (series_markers?.includes(`points`)) {
2529
- const stroke_color = point_style?.stroke
2530
- if (!is_transparent_or_none(stroke_color)) return stroke_color
2531
- }
2532
- if (series_markers?.includes(`line`)) {
2533
- const line_style = hovered_series?.line_style ?? {}
2534
- const first_point_style = Array.isArray(hovered_series?.point_style)
2535
- ? hovered_series?.point_style[0]
2536
- : hovered_series?.point_style
2537
- const first_color_value = hovered_series?.color_values?.[0]
2538
- let line_color_candidate = line_style.stroke
2539
- if (is_transparent_or_none(line_color_candidate)) {line_color_candidate =
2540
- first_point_style?.fill}
2541
- if (
2542
- is_transparent_or_none(line_color_candidate) && first_color_value != null
2543
- ) line_color_candidate = color_scale_fn(first_color_value)
2544
- if (
2545
- is_transparent_or_none(line_color_candidate) &&
2546
- series_markers?.includes(`points`)
2547
- ) line_color_candidate = first_point_style?.stroke
2548
- if (!is_transparent_or_none(line_color_candidate)) return line_color_candidate
2549
- }
2550
- return `rgba(0, 0, 0, 0.7)`
2551
- })()}
2552
- {@const tooltip_pos = constrain_tooltip_position(
2553
- handler_props.cx,
2554
- handler_props.cy,
2555
- tooltip_el?.offsetWidth ?? 120,
2556
- tooltip_el?.offsetHeight ?? 50,
2557
- width,
2558
- height,
2559
- { offset_x: 10, offset_y: 5 },
2560
- )}
2561
- <PlotTooltip
2562
- x={tooltip_pos.x}
2563
- y={tooltip_pos.y}
2564
- offset={{ x: 0, y: 0 }}
2565
- bg_color={tooltip_bg_color}
2566
- bind:wrapper={tooltip_el}
2567
- >
2568
- {#if tooltip}
2569
- {@render tooltip(handler_props)}
2570
- {:else}
2571
- {@const hp = handler_props}
2572
- {#if has_multiple_series && hp.label}<strong>{hp.label}</strong><br />{/if}
2573
- {@html sanitize_html(point_label?.text ? `${point_label.text}<br />` : ``)}
2574
- {@html sanitize_html(hp.x_axis.label || `x`)}: {hp.x_formatted}<br />
2575
- {@html sanitize_html(hp.y_axis.label || `y`)}: {hp.y_formatted}
2576
- {#if hp.colorbar?.value != null}
2577
- <br />{@html sanitize_html(hp.colorbar.title || `Color`)}: {
2578
- format_value(hp.colorbar.value, hp.colorbar.tick_format || `.3~g`)
2579
- }
2580
- {/if}
2581
- {/if}
2582
- </PlotTooltip>
2583
- {/if}
2584
-
2585
- <!-- Control Pane -->
2586
- {#if controls.show}
2587
- <ScatterPlotControls
2588
- toggle_props={{
2589
- ...controls.toggle_props,
2590
- style:
2591
- `--ctrl-btn-right: var(--fullscreen-btn-offset, 30px); top: var(--ctrl-btn-top, 5pt); ${
2592
- controls.toggle_props?.style ?? ``
2593
- }`,
2594
- }}
2595
- pane_props={controls.pane_props}
2596
- bind:x_axis
2597
- bind:x2_axis
2598
- bind:y_axis
2599
- bind:y2_axis
2600
- bind:display
2601
- bind:styles
2602
- {auto_x_range}
2603
- {auto_x2_range}
2604
- {auto_y_range}
2605
- {auto_y2_range}
2606
- bind:selected_series_idx
2607
- series={series_with_ids}
2608
- has_x2_points={x2_points.length > 0}
2609
- has_y2_points={y2_points.length > 0}
2610
- children={controls_extra}
2611
- on_touch={(key) => touched.add(key)}
2612
- />
2613
- {/if}
2614
-
2615
- <!-- Color Bar -->
2616
- {#if color_bar && all_color_values.length > 0 && color_bar_placement}
2617
- {@const color_domain = [
2618
- (typeof color_scale === `string` ? undefined : color_scale.value_range)?.[0] ??
2619
- auto_color_range[0],
2620
- (typeof color_scale === `string` ? undefined : color_scale.value_range)?.[1] ??
2621
- auto_color_range[1],
2622
- ] as Vec2}
2623
- <div
2624
- bind:this={colorbar_element}
2625
- onmouseenter={() => colorbar_hover.set_locked(true)}
2626
- onmouseleave={() => colorbar_hover.set_locked(false)}
2627
- class="colorbar-wrapper"
2628
- role="img"
2629
- aria-label="Color scale legend"
2630
- style={`
2631
- position: absolute;
2632
- left: ${tweened_colorbar_coords.current.x}px;
2633
- top: ${tweened_colorbar_coords.current.y}px;
2634
- pointer-events: auto;
2635
- `}
2636
- >
2637
- <ColorBar
2638
- tick_labels={4}
2639
- tick_side="primary"
2640
- {color_scale_fn}
2641
- color_scale_domain={color_domain}
2642
- scale_type={typeof color_scale === `string` ? undefined : color_scale.type}
2643
- range={color_domain?.every((val) => val != null) ? color_domain : undefined}
2644
- wrapper_style={color_bar?.wrapper_style ?? ``}
2645
- bar_style="width: 220px; height: 20px; {color_bar?.style ?? ``}"
2646
- {...color_bar}
2647
- />
2648
- </div>
2649
- {/if}
2650
-
2651
- <!-- Legend -->
2652
- <!-- Only render if multiple series or if legend prop was explicitly provided by user (even if empty object) -->
2653
- {#if legend != null && legend_data.length > 0 &&
2654
- (legend_data.length > 1 || Object.keys(legend).length > 0)}
2655
- {@const default_x = pad.l + 10}
2656
- {@const default_y = pad.t + 10}
2657
- {@const current_x = legend_is_dragging && legend_manual_position
2658
- ? legend_manual_position.x
2659
- : legend_placement
2660
- ? tweened_legend_coords.current.x
2661
- : default_x}
2662
- {@const current_y = legend_is_dragging && legend_manual_position
2663
- ? legend_manual_position.y
2664
- : legend_placement
2665
- ? tweened_legend_coords.current.y
2666
- : default_y}
2667
- <PlotLegend
2668
- bind:root_element={legend_element}
2669
- series_data={legend_data}
2670
- on_drag_start={handle_legend_drag_start}
2671
- on_drag={handle_legend_drag}
2672
- on_drag_end={() => (legend_is_dragging = false)}
2673
- on_hover_change={legend_hover.set_locked}
2674
- on_item_hover={(series_idx) =>
2675
- (hovered_legend_series_idx = series_idx != null && series_idx >= 0
2676
- ? series_idx
2677
- : null)}
2678
- active_series_idx={tooltip_point?.series_idx ?? hovered_legend_series_idx}
2679
- draggable={legend?.draggable ?? true}
2680
- {...legend}
2681
- on_toggle={legend?.on_toggle ??
2682
- ((series_idx) => {
2683
- series = toggle_series_visibility(series, series_idx)
2684
- })}
2685
- on_double_click={legend?.on_double_click ??
2686
- ((double_clicked_idx) => {
2687
- const result = handle_legend_double_click(
2688
- series,
2689
- double_clicked_idx,
2690
- previous_series_visibility,
2691
- )
2692
- series = result.series
2693
- previous_series_visibility = result.previous_visibility
2694
- })}
2695
- on_group_toggle={legend?.on_group_toggle ??
2696
- ((_group_name, series_indices) => {
2697
- series = toggle_group_visibility(series, series_indices)
2698
- })}
2699
- on_fill_toggle={(source_type, source_idx) => {
2700
- // Only fill_regions can be toggled (error_bands are not bindable)
2701
- if (source_type === `fill_region`) {
2702
- fill_regions = fill_regions.map((region, idx) =>
2703
- idx === source_idx
2704
- ? { ...region, visible: !(region.visible !== false) }
2705
- : region
2706
- )
2707
- }
2708
- }}
2709
- on_fill_double_click={(source_type, source_idx) => {
2710
- // Only fill_regions can be toggled (error_bands are not bindable)
2711
- if (source_type !== `fill_region`) return
2712
- // Toggle: if only this fill is visible, show all; otherwise show only this one
2713
- const visible_count = fill_regions.filter((r) => r.visible !== false).length
2714
- const this_visible = fill_regions[source_idx]?.visible !== false
2715
- if (visible_count === 1 && this_visible) {
2716
- // Show all fills
2717
- fill_regions = fill_regions.map((region) => ({ ...region, visible: true }))
2718
- } else {
2719
- // Show only this fill
2720
- fill_regions = fill_regions.map((region, idx) => ({
2721
- ...region,
2722
- visible: idx === source_idx,
2723
- }))
2724
- }
2725
- }}
2726
- style={`
2727
- position: absolute;
2728
- left: ${current_x}px;
2729
- top: ${current_y}px;
2730
- pointer-events: auto;
2731
- ${legend?.style ?? ``}
2732
- `}
2733
- />
2734
- {/if}
2735
- {/if}
2736
-
2737
- <!-- User-provided children (e.g. for custom absolutely-positioned overlays) -->
2738
- {@render children?.({ height, width, fullscreen })}
2739
- </div>
2740
-
2741
- <style>
2742
- div.scatter {
2743
- position: relative; /* Needed for absolute positioning of children like ColorBar */
2744
- width: var(--scatter-width, 100%);
2745
- height: var(--scatter-height, auto);
2746
- min-height: var(--scatter-min-height, 350px);
2747
- container-type: size; /* enable cqh for panes */
2748
- container-name: scatter-plot;
2749
- z-index: var(--scatter-z-index);
2750
- flex: var(--scatter-flex, 1); /* Allow filling available space in flex containers */
2751
- display: var(--scatter-display, flex);
2752
- flex-direction: column;
2753
- background: var(--scatter-bg, var(--plot-bg));
2754
- border-radius: var(--scatter-border-radius, var(--border-radius, 3pt));
2755
- }
2756
- div.scatter.fullscreen {
2757
- position: fixed;
2758
- top: 0;
2759
- left: 0;
2760
- width: 100vw !important;
2761
- height: 100vh !important;
2762
- /* Must be higher than Structure.svelte's --struct-buttons-z-index. */
2763
- z-index: var(--scatter-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
2764
- margin: 0;
2765
- border-radius: 0;
2766
- background: var(--scatter-fullscreen-bg, var(--scatter-bg, var(--plot-bg)));
2767
- max-height: none !important;
2768
- overflow: hidden;
2769
- /* Add padding to prevent titles from being cropped at top */
2770
- padding-top: var(--plot-fullscreen-padding-top, 2em);
2771
- box-sizing: border-box;
2772
- }
2773
- .header-controls {
2774
- position: absolute;
2775
- top: var(--ctrl-btn-top, 5pt);
2776
- right: var(--fullscreen-btn-right, 4px);
2777
- z-index: var(--fullscreen-btn-z-index, 10);
2778
- display: flex;
2779
- align-items: center;
2780
- gap: 8px;
2781
- }
2782
- .header-controls :global(.fullscreen-toggle) {
2783
- position: static; /* Override absolute positioning since container handles it */
2784
- opacity: 1; /* Always visible when inside header-controls, container controls visibility */
2785
- }
2786
- /* Hide controls and fullscreen toggles by default, show on hover */
2787
- div.scatter :global(.pane-toggle),
2788
- div.scatter .header-controls {
2789
- opacity: 0;
2790
- transition: opacity 0.2s, background-color 0.2s;
2791
- }
2792
- div.scatter:hover :global(.pane-toggle),
2793
- div.scatter:hover .header-controls,
2794
- div.scatter :global(.pane-toggle:focus-visible),
2795
- div.scatter :global(.pane-toggle[aria-expanded='true']),
2796
- div.scatter .header-controls:focus-within {
2797
- opacity: 1;
2798
- }
2799
- svg {
2800
- width: var(--scatter-svg-width, 100%);
2801
- height: var(--scatter-svg-height, 100%);
2802
- flex: var(--scatter-svg-flex, 1);
2803
- overflow: var(--scatter-svg-overflow, visible);
2804
- fill: var(--text-color);
2805
- font-weight: var(--scatter-font-weight);
2806
- font-size: var(--scatter-font-size);
2807
- }
2808
- line {
2809
- stroke: var(--scatter-grid-stroke, gray);
2810
- stroke-dasharray: var(--scatter-grid-dash, 4);
2811
- stroke-width: var(--scatter-grid-width, 0.4);
2812
- }
2813
- g:is(.x-axis, .x2-axis) text {
2814
- text-anchor: middle;
2815
- dominant-baseline: top;
2816
- }
2817
- g:is(.y-axis, .y2-axis) text {
2818
- dominant-baseline: central;
2819
- }
2820
- g:is(.x-axis, .x2-axis, .y-axis, .y2-axis) .tick text {
2821
- font-size: var(--tick-font-size, 0.8em); /* shrink tick labels */
2822
- }
2823
- .scatter :global(.axis-label) {
2824
- text-align: center;
2825
- width: 100%;
2826
- height: 100%;
2827
- font-size: var(--scatter-font-size, inherit);
2828
- font-weight: var(--scatter-font-weight, normal);
2829
- color: var(--text-color);
2830
- white-space: nowrap;
2831
- /* Use line-height to center text vertically without flexbox */
2832
- line-height: var(
2833
- --scatter-axis-label-line-height,
2834
- 20px
2835
- ); /* Match foreignObject height */
2836
- display: block;
2837
- }
2838
- .current-frame-indicator {
2839
- filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
2840
- transition: opacity 0.2s ease;
2841
- }
2842
- .current-frame-indicator:hover {
2843
- opacity: 0.8;
2844
- }
2845
- </style>