matterviz 0.3.5 → 0.3.7

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 (229) hide show
  1. package/dist/MillerIndexInput.svelte +5 -5
  2. package/dist/api/optimade.js +3 -3
  3. package/dist/brillouin/BrillouinZone.svelte +5 -2
  4. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  5. package/dist/brillouin/BrillouinZoneExportPane.svelte +1 -3
  6. package/dist/brillouin/BrillouinZoneInfoPane.svelte +1 -1
  7. package/dist/brillouin/BrillouinZoneScene.svelte +5 -5
  8. package/dist/brillouin/compute.js +21 -21
  9. package/dist/brillouin/index.d.ts +1 -1
  10. package/dist/brillouin/index.js +0 -1
  11. package/dist/brillouin/types.d.ts +8 -13
  12. package/dist/chempot-diagram/ChemPotDiagram.svelte +3 -3
  13. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +3 -4
  14. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +33 -34
  15. package/dist/chempot-diagram/compute.js +1 -7
  16. package/dist/chempot-diagram/temperature.d.ts +1 -1
  17. package/dist/chempot-diagram/temperature.js +1 -3
  18. package/dist/chempot-diagram/types.d.ts +4 -9
  19. package/dist/colors/index.js +5 -5
  20. package/dist/composition/Composition.svelte +2 -1
  21. package/dist/composition/Formula.svelte +7 -4
  22. package/dist/composition/FormulaFilter.svelte +1 -3
  23. package/dist/composition/format.js +4 -4
  24. package/dist/composition/parse.d.ts +2 -1
  25. package/dist/composition/parse.js +61 -46
  26. package/dist/convex-hull/ConvexHull2D.svelte +62 -51
  27. package/dist/convex-hull/ConvexHull3D.svelte +101 -90
  28. package/dist/convex-hull/ConvexHull4D.svelte +70 -58
  29. package/dist/convex-hull/ConvexHullControls.svelte +24 -35
  30. package/dist/convex-hull/ConvexHullInfoPane.svelte +8 -5
  31. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +2 -0
  32. package/dist/convex-hull/ConvexHullStats.svelte +9 -2
  33. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +2 -0
  34. package/dist/convex-hull/GasPressureControls.svelte +7 -7
  35. package/dist/convex-hull/StructurePopup.svelte +65 -30
  36. package/dist/convex-hull/StructurePopup.svelte.d.ts +6 -6
  37. package/dist/convex-hull/TemperatureSlider.svelte +8 -5
  38. package/dist/convex-hull/barycentric-coords.d.ts +2 -2
  39. package/dist/convex-hull/barycentric-coords.js +2 -2
  40. package/dist/convex-hull/gas-thermodynamics.js +2 -4
  41. package/dist/convex-hull/helpers.d.ts +13 -2
  42. package/dist/convex-hull/helpers.js +37 -16
  43. package/dist/convex-hull/index.d.ts +1 -0
  44. package/dist/convex-hull/index.js +1 -0
  45. package/dist/convex-hull/thermodynamics.d.ts +2 -1
  46. package/dist/convex-hull/thermodynamics.js +7 -7
  47. package/dist/convex-hull/types.d.ts +15 -15
  48. package/dist/effects.svelte.d.ts +12 -0
  49. package/dist/effects.svelte.js +37 -0
  50. package/dist/element/BohrAtom.svelte +4 -4
  51. package/dist/element/data.json.gz.d.ts +3 -1
  52. package/dist/element/index.d.ts +1 -1
  53. package/dist/element/index.js +0 -1
  54. package/dist/fermi-surface/FermiSurface.svelte +4 -4
  55. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  56. package/dist/fermi-surface/FermiSurfaceControls.svelte +15 -19
  57. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  58. package/dist/fermi-surface/FermiSurfaceScene.svelte +8 -6
  59. package/dist/fermi-surface/compute.js +2 -2
  60. package/dist/fermi-surface/export.js +13 -26
  61. package/dist/fermi-surface/parse.js +8 -12
  62. package/dist/fermi-surface/types.d.ts +2 -5
  63. package/dist/heatmap-matrix/HeatmapMatrix.svelte +21 -3
  64. package/dist/heatmap-matrix/index.js +6 -6
  65. package/dist/io/decompress.d.ts +2 -1
  66. package/dist/io/decompress.js +1 -1
  67. package/dist/io/export.js +1 -1
  68. package/dist/io/index.d.ts +1 -1
  69. package/dist/io/index.js +0 -1
  70. package/dist/io/url-drop.js +7 -1
  71. package/dist/isosurface/IsosurfaceControls.svelte +11 -25
  72. package/dist/isosurface/slice.js +1 -1
  73. package/dist/isosurface/types.js +12 -12
  74. package/dist/labels.d.ts +1 -1
  75. package/dist/labels.js +14 -11
  76. package/dist/layout/InfoTag.svelte +6 -4
  77. package/dist/layout/PropertyFilter.svelte +4 -2
  78. package/dist/layout/json-tree/JsonTree.svelte +22 -14
  79. package/dist/layout/json-tree/JsonValue.svelte +2 -2
  80. package/dist/layout/json-tree/types.d.ts +3 -2
  81. package/dist/layout/json-tree/types.js +0 -1
  82. package/dist/layout/json-tree/utils.d.ts +4 -4
  83. package/dist/layout/json-tree/utils.js +12 -20
  84. package/dist/marching-cubes.js +13 -15
  85. package/dist/math.d.ts +11 -1
  86. package/dist/math.js +15 -6
  87. package/dist/overlays/DragControlTab.svelte +98 -0
  88. package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
  89. package/dist/overlays/DraggablePane.svelte +7 -84
  90. package/dist/overlays/index.d.ts +1 -0
  91. package/dist/overlays/index.js +1 -0
  92. package/dist/periodic-table/PeriodicTable.svelte +11 -11
  93. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +4 -2
  94. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  95. package/dist/phase-diagram/PhaseDiagramControls.svelte +4 -9
  96. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  97. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +2 -10
  98. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +2 -3
  99. package/dist/phase-diagram/TdbInfoPanel.svelte +3 -3
  100. package/dist/phase-diagram/build-diagram.js +11 -18
  101. package/dist/phase-diagram/diagram-input.d.ts +5 -9
  102. package/dist/phase-diagram/index.d.ts +2 -2
  103. package/dist/phase-diagram/index.js +0 -2
  104. package/dist/phase-diagram/parse.d.ts +2 -2
  105. package/dist/phase-diagram/parse.js +6 -10
  106. package/dist/phase-diagram/svg-to-diagram.js +15 -15
  107. package/dist/phase-diagram/types.d.ts +5 -11
  108. package/dist/phase-diagram/utils.d.ts +2 -2
  109. package/dist/phase-diagram/utils.js +9 -11
  110. package/dist/plot/BarPlot.svelte +162 -314
  111. package/dist/plot/BarPlot.svelte.d.ts +5 -4
  112. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  113. package/dist/plot/BinnedScatterPlot.svelte +1114 -0
  114. package/dist/plot/BinnedScatterPlot.svelte.d.ts +66 -0
  115. package/dist/plot/ColorBar.svelte +19 -17
  116. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  117. package/dist/plot/FillArea.svelte +2 -4
  118. package/dist/plot/FillArea.svelte.d.ts +1 -1
  119. package/dist/plot/Histogram.svelte +167 -281
  120. package/dist/plot/Histogram.svelte.d.ts +1 -1
  121. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  122. package/dist/plot/InteractiveAxisLabel.svelte +5 -3
  123. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  124. package/dist/plot/PlotAxis.svelte +169 -0
  125. package/dist/plot/PlotAxis.svelte.d.ts +24 -0
  126. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  127. package/dist/plot/ReferenceLine3D.svelte +53 -51
  128. package/dist/plot/ReferencePlane.svelte +39 -42
  129. package/dist/plot/ScatterPlot.svelte +300 -367
  130. package/dist/plot/ScatterPlot.svelte.d.ts +8 -5
  131. package/dist/plot/ScatterPlot3D.svelte +33 -6
  132. package/dist/plot/ScatterPlot3D.svelte.d.ts +3 -2
  133. package/dist/plot/ScatterPlot3DControls.svelte +9 -9
  134. package/dist/plot/ScatterPlotControls.svelte +3 -4
  135. package/dist/plot/ScatterPoint.svelte +18 -27
  136. package/dist/plot/ScatterPoint.svelte.d.ts +4 -3
  137. package/dist/plot/Surface3D.svelte +4 -7
  138. package/dist/plot/ZeroLines.svelte +2 -1
  139. package/dist/plot/ZeroLines.svelte.d.ts +2 -1
  140. package/dist/plot/ZoomRect.svelte +2 -2
  141. package/dist/plot/ZoomRect.svelte.d.ts +3 -3
  142. package/dist/plot/adaptive-density.d.ts +69 -0
  143. package/dist/plot/adaptive-density.js +191 -0
  144. package/dist/plot/auto-place.d.ts +43 -0
  145. package/dist/plot/auto-place.js +122 -0
  146. package/dist/plot/axis-utils.js +3 -5
  147. package/dist/plot/binned-scatter-types.d.ts +59 -0
  148. package/dist/plot/binned-scatter-types.js +1 -0
  149. package/dist/plot/data-cleaning.js +1 -1
  150. package/dist/plot/data-transform.js +1 -1
  151. package/dist/plot/fill-utils.d.ts +4 -9
  152. package/dist/plot/fill-utils.js +29 -44
  153. package/dist/plot/index.d.ts +4 -0
  154. package/dist/plot/index.js +2 -0
  155. package/dist/plot/interactions.d.ts +4 -4
  156. package/dist/plot/interactions.js +4 -3
  157. package/dist/plot/layout.d.ts +20 -2
  158. package/dist/plot/layout.js +59 -16
  159. package/dist/plot/reference-line.d.ts +1 -1
  160. package/dist/plot/reference-line.js +9 -11
  161. package/dist/plot/scales.d.ts +1 -1
  162. package/dist/plot/scales.js +20 -23
  163. package/dist/plot/types.d.ts +30 -58
  164. package/dist/plot/types.js +2 -6
  165. package/dist/plot/utils/label-placement.d.ts +24 -3
  166. package/dist/plot/utils/label-placement.js +82 -12
  167. package/dist/plot/utils/series-visibility.d.ts +8 -2
  168. package/dist/plot/utils/series-visibility.js +23 -5
  169. package/dist/rdf/RdfPlot.svelte +5 -5
  170. package/dist/rdf/calc-rdf.js +3 -3
  171. package/dist/sanitize.d.ts +2 -0
  172. package/dist/sanitize.js +2 -0
  173. package/dist/spectral/Bands.svelte +1 -1
  174. package/dist/spectral/BandsAndDos.svelte +22 -16
  175. package/dist/spectral/BrillouinBandsDos.svelte +20 -16
  176. package/dist/spectral/Dos.svelte +1 -1
  177. package/dist/spectral/helpers.d.ts +4 -2
  178. package/dist/spectral/helpers.js +44 -35
  179. package/dist/spectral/index.d.ts +1 -1
  180. package/dist/spectral/index.js +0 -1
  181. package/dist/structure/AtomLegend.svelte +23 -6
  182. package/dist/structure/AtomLegend.svelte.d.ts +1 -0
  183. package/dist/structure/CanvasTooltip.svelte +9 -9
  184. package/dist/structure/CanvasTooltip.svelte.d.ts +1 -1
  185. package/dist/structure/CellSelect.svelte +14 -16
  186. package/dist/structure/Structure.svelte +317 -68
  187. package/dist/structure/Structure.svelte.d.ts +4 -2
  188. package/dist/structure/StructureControls.svelte +20 -45
  189. package/dist/structure/StructureExportPane.svelte +2 -1
  190. package/dist/structure/StructureInfoPane.svelte +10 -8
  191. package/dist/structure/StructureScene.svelte +527 -177
  192. package/dist/structure/StructureScene.svelte.d.ts +5 -2
  193. package/dist/structure/atom-properties.js +4 -4
  194. package/dist/structure/bond-order-perception.js +115 -98
  195. package/dist/structure/bonding.d.ts +27 -1
  196. package/dist/structure/bonding.js +187 -16
  197. package/dist/structure/export.js +1 -1
  198. package/dist/structure/index.d.ts +3 -2
  199. package/dist/structure/index.js +0 -2
  200. package/dist/structure/parse.js +88 -59
  201. package/dist/symmetry/WyckoffTable.svelte +7 -0
  202. package/dist/symmetry/index.js +13 -14
  203. package/dist/table/HeatmapTable.svelte +45 -66
  204. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  205. package/dist/table/ToggleMenu.svelte +19 -10
  206. package/dist/theme/themes.mjs +12 -0
  207. package/dist/tooltip/index.d.ts +1 -1
  208. package/dist/tooltip/index.js +0 -1
  209. package/dist/trajectory/Trajectory.svelte +43 -15
  210. package/dist/trajectory/TrajectoryInfoPane.svelte +2 -2
  211. package/dist/trajectory/extract.js +1 -1
  212. package/dist/trajectory/frame-reader.js +4 -4
  213. package/dist/trajectory/helpers.d.ts +5 -4
  214. package/dist/trajectory/helpers.js +9 -17
  215. package/dist/trajectory/index.d.ts +2 -2
  216. package/dist/trajectory/index.js +2 -2
  217. package/dist/trajectory/parse/ase.js +4 -4
  218. package/dist/trajectory/parse/hdf5.js +1 -1
  219. package/dist/trajectory/parse/index.js +2 -3
  220. package/dist/trajectory/parse/lammps.js +1 -1
  221. package/dist/trajectory/parse/vasp.js +1 -1
  222. package/dist/trajectory/plotting.d.ts +1 -1
  223. package/dist/trajectory/plotting.js +38 -38
  224. package/dist/trajectory/types.d.ts +1 -1
  225. package/dist/utils.d.ts +1 -0
  226. package/dist/utils.js +9 -0
  227. package/dist/xrd/calc-xrd.js +3 -4
  228. package/dist/xrd/parse.js +1 -1
  229. package/package.json +42 -22
@@ -11,9 +11,9 @@
11
11
  RefLineEvent,
12
12
  } from './'
13
13
  import {
14
- AxisLabel,
15
14
  compute_element_placement,
16
15
  HistogramControls,
16
+ PlotAxis,
17
17
  PlotLegend,
18
18
  ReferenceLine,
19
19
  } from './'
@@ -38,6 +38,13 @@
38
38
  LABEL_GAP_DEFAULT,
39
39
  measure_max_tick_width,
40
40
  } from './layout'
41
+ import {
42
+ build_obstacles_norm,
43
+ clip_bar,
44
+ has_explicit_position,
45
+ measured_footprint,
46
+ place_decorations,
47
+ } from './auto-place'
41
48
  import type { IndexedRefLine } from './reference-line'
42
49
  import { group_ref_lines_by_z, index_ref_lines } from './reference-line'
43
50
  import {
@@ -228,6 +235,17 @@
228
235
 
229
236
  // Derived data
230
237
  type IndexedSeries = { series_data: DataSeries; series_idx: number }
238
+ let visible_series_labels = $derived(
239
+ series
240
+ .filter((series_data) => series_data.visible ?? true)
241
+ .map((series_data) => series_data.label)
242
+ .filter((label): label is string => typeof label === `string` && label.length > 0),
243
+ )
244
+ $effect(() => {
245
+ if (mode !== `single`) return
246
+ if (selected_property && visible_series_labels.includes(selected_property)) return
247
+ selected_property = visible_series_labels[0] ?? ``
248
+ })
231
249
  let selected_series_entries = $derived<IndexedSeries[]>(
232
250
  series
233
251
  .map((series_data: DataSeries, series_idx: number) => ({ series_data, series_idx }))
@@ -389,7 +407,8 @@
389
407
 
390
408
  // Layout: dynamic padding based on tick label widths
391
409
  const default_padding = { t: 20, b: 60, l: 60, r: 20 }
392
- let pad = $derived(filter_padding(padding, default_padding))
410
+ // base_pad reserves space for tick labels/axis titles; pad (below) adds decoration reservations
411
+ let base_pad = $derived(filter_padding(padding, default_padding))
393
412
 
394
413
  // Update padding based on tick label widths (untrack breaks circular dependency)
395
414
  $effect(() => {
@@ -441,11 +460,60 @@
441
460
 
442
461
  // Only update if padding actually changed
443
462
  if (
444
- pad.t !== new_pad.t || pad.b !== new_pad.b || pad.l !== new_pad.l ||
445
- pad.r !== new_pad.r
446
- ) pad = new_pad
463
+ base_pad.t !== new_pad.t || base_pad.b !== new_pad.b ||
464
+ base_pad.l !== new_pad.l || base_pad.r !== new_pad.r
465
+ ) base_pad = new_pad
466
+ })
467
+
468
+ const legend_footprint = $derived(measured_footprint(legend_element, { width: 120, height: 60 }))
469
+ const legend_has_explicit_pos = $derived(has_explicit_position(legend?.style))
470
+
471
+ // Obstacle field in normalized [0,1] plot coords (y=0 at top). Each filled bar is modeled as a
472
+ // vertical segment (top -> baseline) so the legend can't hide inside a tall bar. Built from
473
+ // histogram_bins (pad-independent) + ranges so the crowding decision can't see its own reservation.
474
+ const obstacles_norm = $derived.by(() => {
475
+ if (!width || !height || !histogram_bins.length) return []
476
+ const base_w = width - base_pad.l - base_pad.r
477
+ const base_h = height - base_pad.t - base_pad.b
478
+ if (base_w <= 0 || base_h <= 0) return []
479
+ const bars: { points: { x: number; y: number }[]; draws_line: boolean }[] = []
480
+ for (const hist of histogram_bins) {
481
+ const [rx0, rx1] = hist.x_axis === `x2` ? ranges.current.x2 : ranges.current.x
482
+ const [ry0, ry1] = hist.y_axis === `y2` ? ranges.current.y2 : ranges.current.y
483
+ const x_span = rx1 - rx0
484
+ const y_span = ry1 - ry0
485
+ if (!(x_span > 0) || !(y_span > 0)) continue
486
+ for (const series_bin of hist.bins) {
487
+ if (series_bin.length <= 0) continue
488
+ const x_norm = (((series_bin.x0 ?? 0) + (series_bin.x1 ?? 0)) / 2 - rx0) / x_span
489
+ const top = 1 - (series_bin.length - ry0) / y_span
490
+ const baseline = 1 + ry0 / y_span // normalized y of count=0 (bar foot)
491
+ const seg = clip_bar(true, x_norm, top, baseline)
492
+ if (seg) bars.push(seg)
493
+ }
494
+ }
495
+ return build_obstacles_norm(bars, base_w, base_h)
447
496
  })
448
497
 
498
+ // Move the legend to the bottom margin when no interior spot avoids the bars
499
+ const decor = $derived.by(() =>
500
+ place_decorations({
501
+ base_pad,
502
+ width,
503
+ height,
504
+ obstacles_norm,
505
+ // gate on legend_element (the render signal) not legend_data, whose entries can read pad
506
+ legend: show_legend && legend != null && series.length > 1 &&
507
+ legend_element != null && !legend_has_explicit_pos
508
+ ? { footprint: legend_footprint, clearance: legend?.axis_clearance }
509
+ : null,
510
+ })
511
+ )
512
+ const pad = $derived(decor.pad)
513
+ const legend_auto_outside = $derived(decor.legend_outside)
514
+ const legend_outside_x = $derived(decor.legend_pos.x)
515
+ const legend_outside_y = $derived(decor.legend_pos.y)
516
+
449
517
  // Scales and data
450
518
  let scales = $derived({
451
519
  x: create_scale(
@@ -470,7 +538,8 @@
470
538
  ),
471
539
  })
472
540
 
473
- let histogram_data = $derived.by(() => {
541
+ // Pad-independent binning (no pixel scales) so the auto-place obstacle field can reuse it
542
+ let histogram_bins = $derived.by(() => {
474
543
  if (!selected_series.length || !width || !height) return []
475
544
  const hist_generator = bin()
476
545
  .domain([ranges.current.x[0], ranges.current.x[1]])
@@ -480,11 +549,8 @@
480
549
  : null
481
550
  return selected_series_entries.map(({ series_data, series_idx }) => {
482
551
  const use_x2 = series_data.x_axis === `x2`
483
- const active_hist = use_x2 && x2_hist_generator
484
- ? x2_hist_generator
485
- : hist_generator
552
+ const active_hist = use_x2 && x2_hist_generator ? x2_hist_generator : hist_generator
486
553
  const bins_arr = active_hist(series_data.y)
487
- const use_y2 = series_data.y_axis === `y2`
488
554
  return {
489
555
  id: series_data.id ?? series_idx,
490
556
  series_idx,
@@ -496,11 +562,17 @@
496
562
  max_count: max(bins_arr, (data) => data.length) || 0,
497
563
  x_axis: series_data.x_axis,
498
564
  y_axis: series_data.y_axis,
499
- x_scale: use_x2 ? scales.x2 : scales.x,
500
- y_scale: use_y2 ? scales.y2 : scales.y,
501
565
  }
502
566
  })
503
567
  })
568
+ // Render-time data adds the pixel scales (pad-dependent)
569
+ let histogram_data = $derived(
570
+ histogram_bins.map((hist) => ({
571
+ ...hist,
572
+ x_scale: hist.x_axis === `x2` ? scales.x2 : scales.x,
573
+ y_scale: hist.y_axis === `y2` ? scales.y2 : scales.y,
574
+ })),
575
+ )
504
576
 
505
577
  let ticks = $derived({
506
578
  x: width && height
@@ -582,14 +654,10 @@
582
654
  const plot_width = width - pad.l - pad.r
583
655
  const plot_height = height - pad.t - pad.b
584
656
 
585
- // Use measured size if available, otherwise estimate
586
- const legend_size = legend_element
587
- ? { width: legend_element.offsetWidth, height: legend_element.offsetHeight }
588
- : { width: 120, height: 60 }
589
-
590
657
  const result = compute_element_placement({
591
658
  plot_bounds: { x: pad.l, y: pad.t, width: plot_width, height: plot_height },
592
- element_size: legend_size,
659
+ element: legend_element,
660
+ element_size: { width: 120, height: 60 }, // fallback before first render
593
661
  axis_clearance: legend?.axis_clearance,
594
662
  exclude_rects: [],
595
663
  points: hist_points_for_placement,
@@ -1167,272 +1235,83 @@
1167
1235
  {@render ref_lines_layer(ref_lines_by_z.below_points)}
1168
1236
 
1169
1237
  <!-- X-axis -->
1170
- <g class="x-axis">
1171
- <line
1172
- x1={pad.l}
1173
- x2={width - pad.r}
1174
- y1={height - pad.b}
1175
- y2={height - pad.b}
1176
- stroke={final_x_axis.color || `var(--border-color, gray)`}
1177
- stroke-width="1"
1178
- />
1179
- {#each ticks.x as tick (tick)}
1180
- {@const tick_x = scales.x(tick as number)}
1181
- {@const custom_label = get_tick_label(tick as number, final_x_axis.ticks)}
1182
- {@const inside = final_x_axis.tick?.label?.inside ?? false}
1183
- {@const shift_x = final_x_axis.tick?.label?.shift?.x ?? 0}
1184
- {@const shift_y = final_x_axis.tick?.label?.shift?.y ?? 0}
1185
- {@const base_y = inside ? -8 : 8}
1186
- {@const text_y = base_y + shift_y}
1187
- {@const dominant_baseline = inside ? `auto` : `hanging`}
1188
- <g class="tick" transform="translate({tick_x}, {height - pad.b})">
1189
- {#if display.x_grid}
1190
- <line
1191
- y1={-(height - pad.b - pad.t)}
1192
- y2="0"
1193
- stroke="var(--border-color, gray)"
1194
- stroke-dasharray="4"
1195
- stroke-width="1"
1196
- {...final_x_axis.grid_style ?? {}}
1197
- />
1198
- {/if}
1199
- <line
1200
- y1="0"
1201
- y2={inside ? -5 : 5}
1202
- stroke={final_x_axis.color || `var(--border-color, gray)`}
1203
- stroke-width="1"
1204
- />
1205
- <text
1206
- x={shift_x}
1207
- y={text_y}
1208
- text-anchor="middle"
1209
- dominant-baseline={dominant_baseline}
1210
- fill={final_x_axis.color || `var(--text-color)`}
1211
- >
1212
- {custom_label ?? format_value(tick, final_x_axis.format)}
1213
- </text>
1214
- </g>
1215
- {/each}
1216
- {#if final_x_axis.label || x_axis.options?.length}
1217
- {@const { label_shift, label = ``, options, selected_key, color } = final_x_axis}
1218
- <AxisLabel
1219
- x={(pad.l + width - pad.r) / 2 + (label_shift?.x ?? 0)}
1220
- y={height - 10 + (label_shift?.y ?? 0)}
1221
- {label}
1222
- {options}
1223
- {selected_key}
1224
- loading={axis_loading === `x`}
1225
- axis_type="x"
1226
- {color}
1227
- on_select={(key) => handle_axis_change(`x`, key)}
1228
- />
1229
- {/if}
1230
- </g>
1238
+ <PlotAxis
1239
+ side="x"
1240
+ ticks={ticks.x as number[]}
1241
+ place={scales.x}
1242
+ axis={final_x_axis}
1243
+ {pad}
1244
+ {width}
1245
+ {height}
1246
+ show_grid={display.x_grid}
1247
+ tick_label={(tick) => get_tick_label(tick, final_x_axis.ticks)}
1248
+ label_x={(pad.l + width - pad.r) / 2 + (final_x_axis.label_shift?.x ?? 0)}
1249
+ label_y={height - 10 + (final_x_axis.label_shift?.y ?? 0)}
1250
+ axis_loading={axis_loading === `x`}
1251
+ on_axis_change={(key) => handle_axis_change(`x`, key)}
1252
+ />
1231
1253
 
1232
1254
  <!-- X2-axis (Top) -->
1233
1255
  {#if x2_series.length > 0}
1234
- <g class="x2-axis">
1235
- <line
1236
- x1={pad.l}
1237
- x2={width - pad.r}
1238
- y1={pad.t}
1239
- y2={pad.t}
1240
- stroke={final_x2_axis.color || `var(--border-color, gray)`}
1241
- stroke-width="1"
1242
- />
1243
- {#each ticks.x2 as tick (tick)}
1244
- {@const tick_x = scales.x2(tick as number)}
1245
- {@const custom_label = get_tick_label(tick as number, final_x2_axis.ticks)}
1246
- {@const inside = final_x2_axis.tick?.label?.inside ?? false}
1247
- {@const shift_x = final_x2_axis.tick?.label?.shift?.x ?? 0}
1248
- {@const shift_y = final_x2_axis.tick?.label?.shift?.y ?? 0}
1249
- {@const base_y = inside ? 8 : -20}
1250
- {@const text_y = base_y + shift_y}
1251
- {@const dominant_baseline = inside ? `hanging` : `auto`}
1252
- <g class="tick" transform="translate({tick_x}, {pad.t})">
1253
- {#if display.x2_grid}
1254
- <line
1255
- y1="0"
1256
- y2={height - pad.b - pad.t}
1257
- stroke="var(--border-color, gray)"
1258
- stroke-dasharray="4"
1259
- stroke-width="1"
1260
- {...final_x2_axis.grid_style ?? {}}
1261
- />
1262
- {/if}
1263
- <line
1264
- y1={inside ? 0 : -5}
1265
- y2={inside ? 5 : 0}
1266
- stroke={final_x2_axis.color || `var(--border-color, gray)`}
1267
- stroke-width="1"
1268
- />
1269
- <text
1270
- x={shift_x}
1271
- y={text_y}
1272
- text-anchor="middle"
1273
- dominant-baseline={dominant_baseline}
1274
- fill={final_x2_axis.color || `var(--text-color)`}
1275
- >
1276
- {custom_label ?? format_value(tick, final_x2_axis.format)}
1277
- </text>
1278
- </g>
1279
- {/each}
1280
- {#if final_x2_axis.label || x2_axis.options?.length}
1281
- {@const { label_shift, label = ``, options, selected_key, color } =
1282
- final_x2_axis}
1283
- <AxisLabel
1284
- x={(pad.l + width - pad.r) / 2 + (label_shift?.x ?? 0)}
1285
- y={Math.max(12, pad.t - (label_shift?.y ?? 40))}
1286
- {label}
1287
- {options}
1288
- {selected_key}
1289
- loading={axis_loading === `x2`}
1290
- axis_type="x2"
1291
- {color}
1292
- on_select={(key) => handle_axis_change(`x2`, key)}
1293
- />
1294
- {/if}
1295
- </g>
1256
+ <PlotAxis
1257
+ side="x2"
1258
+ ticks={ticks.x2 as number[]}
1259
+ place={scales.x2}
1260
+ axis={final_x2_axis}
1261
+ {pad}
1262
+ {width}
1263
+ {height}
1264
+ show_grid={display.x2_grid}
1265
+ tick_label={(tick) => get_tick_label(tick, final_x2_axis.ticks)}
1266
+ label_x={(pad.l + width - pad.r) / 2 + (final_x2_axis.label_shift?.x ?? 0)}
1267
+ label_y={Math.max(12, pad.t - (final_x2_axis.label_shift?.y ?? 40))}
1268
+ axis_loading={axis_loading === `x2`}
1269
+ on_axis_change={(key) => handle_axis_change(`x2`, key)}
1270
+ />
1296
1271
  {/if}
1297
1272
 
1298
1273
  <!-- Y-axis -->
1299
- <g class="y-axis">
1300
- <line
1301
- x1={pad.l}
1302
- x2={pad.l}
1303
- y1={pad.t}
1304
- y2={height - pad.b}
1305
- stroke={final_y_axis.color || `var(--border-color, gray)`}
1306
- stroke-width="1"
1307
- />
1308
- {#each ticks.y as tick (tick)}
1309
- {@const tick_y = scales.y(tick as number)}
1310
- {@const custom_label = get_tick_label(tick as number, final_y_axis.ticks)}
1311
- {@const inside = final_y_axis.tick?.label?.inside ?? false}
1312
- {@const shift_x = final_y_axis.tick?.label?.shift?.x ?? 0}
1313
- {@const shift_y = final_y_axis.tick?.label?.shift?.y ?? 0}
1314
- {@const base_x = inside ? 8 : -10}
1315
- {@const text_x = base_x + shift_x}
1316
- {@const text_anchor = inside ? `start` : `end`}
1317
- <g class="tick" transform="translate({pad.l}, {tick_y})">
1318
- {#if display.y_grid}
1319
- <line
1320
- x1="0"
1321
- x2={width - pad.l - pad.r}
1322
- stroke="var(--border-color, gray)"
1323
- stroke-dasharray="4"
1324
- stroke-width="1"
1325
- {...final_y_axis.grid_style ?? {}}
1326
- />
1327
- {/if}
1328
- <line
1329
- x1={inside ? 0 : -5}
1330
- x2={inside ? 5 : 0}
1331
- stroke={final_y_axis.color || `var(--border-color, gray)`}
1332
- stroke-width="1"
1333
- />
1334
- <text
1335
- x={text_x}
1336
- y={shift_y}
1337
- text-anchor={text_anchor}
1338
- dominant-baseline="central"
1339
- fill={final_y_axis.color || `var(--text-color)`}
1340
- >
1341
- {custom_label ?? format_value(tick, final_y_axis.format)}
1342
- </text>
1343
- </g>
1344
- {/each}
1345
- {#if final_y_axis.label || y_axis.options?.length}
1346
- {@const { label_shift, label = ``, options, selected_key, color, tick } =
1347
- final_y_axis}
1348
- {@const y_inside = tick?.label?.inside ?? false}
1349
- <AxisLabel
1350
- x={Math.max(
1351
- 12,
1352
- pad.l - (y_inside ? 0 : tick_label_widths.y_max) - LABEL_GAP_DEFAULT,
1353
- ) +
1354
- (label_shift?.x ?? 0)}
1355
- y={pad.t + (height - pad.t - pad.b) / 2 + (label_shift?.y ?? 0)}
1356
- rotate
1357
- {label}
1358
- {options}
1359
- {selected_key}
1360
- loading={axis_loading === `y`}
1361
- axis_type="y"
1362
- {color}
1363
- on_select={(key) => handle_axis_change(`y`, key)}
1364
- />
1365
- {/if}
1366
- </g>
1274
+ <PlotAxis
1275
+ side="y"
1276
+ ticks={ticks.y as number[]}
1277
+ place={scales.y}
1278
+ axis={final_y_axis}
1279
+ {pad}
1280
+ {width}
1281
+ {height}
1282
+ show_grid={display.y_grid}
1283
+ tick_label={(tick) => get_tick_label(tick, final_y_axis.ticks)}
1284
+ label_x={Math.max(
1285
+ 12,
1286
+ pad.l - (final_y_axis.tick?.label?.inside ? 0 : tick_label_widths.y_max) -
1287
+ LABEL_GAP_DEFAULT,
1288
+ ) + (final_y_axis.label_shift?.x ?? 0)}
1289
+ label_y={pad.t + (height - pad.t - pad.b) / 2 + (final_y_axis.label_shift?.y ?? 0)}
1290
+ axis_loading={axis_loading === `y`}
1291
+ on_axis_change={(key) => handle_axis_change(`y`, key)}
1292
+ />
1367
1293
 
1368
1294
  <!-- Y2-axis (Right) -->
1369
1295
  {#if y2_series.length > 0}
1370
- <g class="y2-axis">
1371
- <line
1372
- x1={width - pad.r}
1373
- x2={width - pad.r}
1374
- y1={pad.t}
1375
- y2={height - pad.b}
1376
- stroke={final_y2_axis.color || `var(--border-color, gray)`}
1377
- stroke-width="1"
1378
- />
1379
- {#each ticks.y2 as tick (tick)}
1380
- {@const tick_y = scales.y2(tick as number)}
1381
- {@const custom_label = get_tick_label(tick as number, final_y2_axis.ticks)}
1382
- {@const inside = final_y2_axis.tick?.label?.inside ?? false}
1383
- {@const base_x = inside ? -8 : 8}
1384
- {@const shift_x = (final_y2_axis.tick?.label?.shift?.x ?? 0) + base_x}
1385
- {@const shift_y = final_y2_axis.tick?.label?.shift?.y ?? 0}
1386
- {@const text_anchor = inside ? `end` : `start`}
1387
- <g class="tick" transform="translate({width - pad.r}, {tick_y})">
1388
- {#if display.y2_grid}
1389
- <line
1390
- x1={-(width - pad.l - pad.r)}
1391
- x2="0"
1392
- stroke="var(--border-color, gray)"
1393
- stroke-dasharray="4"
1394
- stroke-width="1"
1395
- {...final_y2_axis.grid_style ?? {}}
1396
- />
1397
- {/if}
1398
- <line
1399
- x1={inside ? -5 : 0}
1400
- x2={inside ? 0 : 5}
1401
- stroke={final_y2_axis.color || `var(--border-color, gray)`}
1402
- stroke-width="1"
1403
- />
1404
- <text
1405
- x={shift_x}
1406
- y={shift_y}
1407
- text-anchor={text_anchor}
1408
- dominant-baseline="central"
1409
- fill={final_y2_axis.color || `var(--text-color)`}
1410
- >
1411
- {custom_label ?? format_value(tick, final_y2_axis.format)}
1412
- </text>
1413
- </g>
1414
- {/each}
1415
- {#if final_y2_axis.label || y2_axis.options?.length}
1416
- {@const { label_shift, label = ``, options, selected_key, color, tick } =
1417
- final_y2_axis}
1418
- {@const inside = tick?.label?.inside ?? false}
1419
- {@const tick_shift = inside ? 0 : (tick?.label?.shift?.x ?? 0) + 8}
1420
- {@const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max}
1421
- <AxisLabel
1422
- x={width - pad.r + tick_shift + tick_width_contribution +
1423
- LABEL_GAP_DEFAULT + (label_shift?.x ?? 0)}
1424
- y={pad.t + (height - pad.t - pad.b) / 2 + (label_shift?.y ?? 0)}
1425
- rotate
1426
- {label}
1427
- {options}
1428
- {selected_key}
1429
- loading={axis_loading === `y2`}
1430
- axis_type="y2"
1431
- {color}
1432
- on_select={(key) => handle_axis_change(`y2`, key)}
1433
- />
1434
- {/if}
1435
- </g>
1296
+ {@const y2_inside = final_y2_axis.tick?.label?.inside ?? false}
1297
+ {@const y2_tick_shift = y2_inside ? 0 : (final_y2_axis.tick?.label?.shift?.x ?? 0) + 8}
1298
+ {@const y2_tick_width = y2_inside ? 0 : tick_label_widths.y2_max}
1299
+ <PlotAxis
1300
+ side="y2"
1301
+ ticks={ticks.y2 as number[]}
1302
+ place={scales.y2}
1303
+ axis={final_y2_axis}
1304
+ {pad}
1305
+ {width}
1306
+ {height}
1307
+ show_grid={display.y2_grid}
1308
+ tick_label={(tick) => get_tick_label(tick, final_y2_axis.ticks)}
1309
+ label_x={width - pad.r + y2_tick_shift + y2_tick_width + LABEL_GAP_DEFAULT +
1310
+ (final_y2_axis.label_shift?.x ?? 0)}
1311
+ label_y={pad.t + (height - pad.t - pad.b) / 2 + (final_y2_axis.label_shift?.y ?? 0)}
1312
+ axis_loading={axis_loading === `y2`}
1313
+ on_axis_change={(key) => handle_axis_change(`y2`, key)}
1314
+ />
1436
1315
  {/if}
1437
1316
 
1438
1317
  <!-- Histogram bars (rendered after axes so bars appear above grid lines) -->
@@ -1568,21 +1447,31 @@
1568
1447
  {/if}
1569
1448
 
1570
1449
  {#if show_legend && legend != null && series.length > 1}
1450
+ {@const legend_left = legend_auto_outside
1451
+ ? legend_outside_x
1452
+ : legend_placement
1453
+ ? tweened_legend_coords.current.x
1454
+ : pad.l + 10}
1455
+ {@const legend_top = legend_auto_outside
1456
+ ? legend_outside_y
1457
+ : legend_placement
1458
+ ? tweened_legend_coords.current.y
1459
+ : pad.t + 10}
1571
1460
  <PlotLegend
1572
1461
  bind:root_element={legend_element}
1573
1462
  {...legend}
1574
1463
  series_data={legend_data}
1575
1464
  on_toggle={legend?.on_toggle || toggle_series_visibility}
1576
1465
  on_hover_change={legend_hover.set_locked}
1577
- on_item_hover={(series_idx) =>
1466
+ on_item_hover={(series_idx: number | null) =>
1578
1467
  (hovered_legend_series_idx = series_idx != null && series_idx >= 0
1579
1468
  ? series_idx
1580
1469
  : null)}
1581
1470
  active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
1582
1471
  style={`
1583
1472
  position: absolute;
1584
- left: ${legend_placement ? tweened_legend_coords.current.x : pad.l + 10}px;
1585
- top: ${legend_placement ? tweened_legend_coords.current.y : pad.t + 10}px;
1473
+ left: ${legend_left}px;
1474
+ top: ${legend_top}px;
1586
1475
  pointer-events: auto;
1587
1476
  ${legend?.style || ``}
1588
1477
  `}
@@ -1660,9 +1549,6 @@
1660
1549
  font-weight: var(--histogram-font-weight);
1661
1550
  font-size: var(--histogram-font-size);
1662
1551
  }
1663
- g:is(.x-axis, .x2-axis, .y-axis, .y2-axis) .tick text {
1664
- font-size: var(--tick-font-size, 0.8em); /* shrink tick labels */
1665
- }
1666
1552
  .histogram-series path {
1667
1553
  transition: opacity 0.2s ease;
1668
1554
  }
@@ -45,6 +45,6 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & BasePlotProps & PlotCon
45
45
  on_error?: (error: AxisLoadError) => void;
46
46
  pan?: PanConfig;
47
47
  };
48
- declare const Histogram: import("svelte").Component<$$ComponentProps, {}, "mode" | "fullscreen" | "series" | "bins" | "show_legend" | "selected_property" | "hovered" | "ref_lines" | "show_controls" | "controls_open">;
48
+ declare const Histogram: import("svelte").Component<$$ComponentProps, {}, "mode" | "show_controls" | "fullscreen" | "series" | "bins" | "show_legend" | "selected_property" | "hovered" | "ref_lines" | "controls_open">;
49
49
  type Histogram = ReturnType<typeof Histogram>;
50
50
  export default Histogram;
@@ -17,6 +17,6 @@ type $$ComponentProps = Omit<PlotControlsProps, `children` | `post_children`> &
17
17
  has_y2_points?: boolean;
18
18
  children?: Snippet<[Required<PlotConfig>]>;
19
19
  };
20
- declare const HistogramControls: import("svelte").Component<$$ComponentProps, {}, "display" | "mode" | "bins" | "show_legend" | "selected_property" | "show_controls" | "controls_open" | "x_axis" | "y_axis" | "bar" | "x2_axis" | "y2_axis">;
20
+ declare const HistogramControls: import("svelte").Component<$$ComponentProps, {}, "display" | "mode" | "show_controls" | "x_axis" | "y_axis" | "bar" | "bins" | "show_legend" | "selected_property" | "controls_open" | "x2_axis" | "y2_axis">;
21
21
  type HistogramControls = ReturnType<typeof HistogramControls>;
22
22
  export default HistogramControls;
@@ -75,7 +75,6 @@
75
75
  .static-label {
76
76
  display: inline-flex;
77
77
  align-items: baseline;
78
- gap: 0.2em;
79
78
  }
80
79
  .loading :global(.axis-trigger) {
81
80
  opacity: 0.7;
@@ -84,11 +83,14 @@
84
83
  .interactive-axis-label :global(:is(sub, sup)) {
85
84
  font-size: 0.75em;
86
85
  line-height: 0;
86
+ /* vertical-align is ignored in the flex label wrapper. */
87
+ position: relative;
88
+ vertical-align: baseline;
87
89
  }
88
90
  .interactive-axis-label :global(sub) {
89
- vertical-align: sub;
91
+ top: 0.35em;
90
92
  }
91
93
  .interactive-axis-label :global(sup) {
92
- vertical-align: super;
94
+ top: -0.5em;
93
95
  }
94
96
  </style>
@@ -9,6 +9,6 @@ type $$ComponentProps = {
9
9
  on_select?: (key: string) => void;
10
10
  [key: string]: unknown;
11
11
  };
12
- declare const InteractiveAxisLabel: import("svelte").Component<$$ComponentProps, {}, "color" | "loading" | "selected_key">;
12
+ declare const InteractiveAxisLabel: import("svelte").Component<$$ComponentProps, {}, "color" | "selected_key" | "loading">;
13
13
  type InteractiveAxisLabel = ReturnType<typeof InteractiveAxisLabel>;
14
14
  export default InteractiveAxisLabel;