matterviz 0.3.2 → 0.3.4

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 (281) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +123 -82
  3. package/dist/Icon.svelte +18 -12
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -207
  7. package/dist/brillouin/BrillouinZone.svelte +292 -149
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
  20. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
  21. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  22. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  23. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  24. package/dist/chempot-diagram/chempot-worker.js +11 -0
  25. package/dist/chempot-diagram/color.js +1 -2
  26. package/dist/chempot-diagram/compute.d.ts +10 -0
  27. package/dist/chempot-diagram/compute.js +250 -88
  28. package/dist/chempot-diagram/index.d.ts +2 -1
  29. package/dist/chempot-diagram/index.js +2 -1
  30. package/dist/chempot-diagram/temperature.js +8 -9
  31. package/dist/chempot-diagram/types.d.ts +3 -0
  32. package/dist/chempot-diagram/types.js +1 -0
  33. package/dist/colors/index.d.ts +1 -1
  34. package/dist/colors/index.js +5 -3
  35. package/dist/composition/BarChart.svelte +128 -55
  36. package/dist/composition/BubbleChart.svelte +102 -49
  37. package/dist/composition/Composition.svelte +100 -79
  38. package/dist/composition/Formula.svelte +108 -62
  39. package/dist/composition/FormulaFilter.svelte +665 -537
  40. package/dist/composition/PieChart.svelte +183 -108
  41. package/dist/composition/format.d.ts +5 -0
  42. package/dist/composition/format.js +20 -3
  43. package/dist/composition/parse.js +14 -9
  44. package/dist/convex-hull/ConvexHull.svelte +93 -40
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +549 -360
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  53. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  54. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  55. package/dist/convex-hull/ConvexHullStats.svelte +425 -328
  56. package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
  57. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  58. package/dist/convex-hull/StructurePopup.svelte +25 -4
  59. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  60. package/dist/convex-hull/barycentric-coords.js +13 -7
  61. package/dist/convex-hull/demo-temperature.js +8 -4
  62. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  63. package/dist/convex-hull/helpers.d.ts +9 -0
  64. package/dist/convex-hull/helpers.js +77 -34
  65. package/dist/convex-hull/thermodynamics.js +61 -56
  66. package/dist/convex-hull/types.d.ts +9 -14
  67. package/dist/convex-hull/types.js +0 -17
  68. package/dist/coordination/CoordinationBarPlot.svelte +227 -154
  69. package/dist/element/BohrAtom.svelte +55 -12
  70. package/dist/element/ElementHeading.svelte +7 -2
  71. package/dist/element/ElementPhoto.svelte +15 -9
  72. package/dist/element/ElementStats.svelte +10 -4
  73. package/dist/element/ElementTile.svelte +137 -73
  74. package/dist/element/Nucleus.svelte +39 -11
  75. package/dist/element/data.js +1 -1
  76. package/dist/feedback/ClickFeedback.svelte +16 -5
  77. package/dist/feedback/DragOverlay.svelte +10 -2
  78. package/dist/feedback/Spinner.svelte +4 -2
  79. package/dist/feedback/StatusMessage.svelte +8 -2
  80. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  81. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  82. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  84. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  86. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  87. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  88. package/dist/fermi-surface/compute.js +16 -20
  89. package/dist/fermi-surface/parse.js +24 -14
  90. package/dist/fermi-surface/symmetry.js +2 -7
  91. package/dist/fermi-surface/types.d.ts +3 -5
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  93. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  95. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  96. package/dist/icons.js +47 -0
  97. package/dist/index.d.ts +2 -1
  98. package/dist/index.js +2 -1
  99. package/dist/io/decompress.js +1 -1
  100. package/dist/io/export.d.ts +3 -0
  101. package/dist/io/export.js +129 -143
  102. package/dist/io/is-binary.js +2 -3
  103. package/dist/io/url-drop.js +1 -2
  104. package/dist/isosurface/Isosurface.svelte +202 -148
  105. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  106. package/dist/isosurface/parse.js +34 -29
  107. package/dist/isosurface/slice.js +5 -10
  108. package/dist/isosurface/types.d.ts +2 -1
  109. package/dist/isosurface/types.js +61 -12
  110. package/dist/labels.js +11 -8
  111. package/dist/layout/FullscreenToggle.svelte +11 -2
  112. package/dist/layout/InfoCard.svelte +38 -6
  113. package/dist/layout/InfoTag.svelte +63 -32
  114. package/dist/layout/PropertyFilter.svelte +82 -37
  115. package/dist/layout/SettingsSection.svelte +85 -55
  116. package/dist/layout/SubpageGrid.svelte +10 -2
  117. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  118. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  119. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  120. package/dist/layout/json-tree/utils.js +4 -2
  121. package/dist/marching-cubes.js +25 -2
  122. package/dist/math.d.ts +13 -17
  123. package/dist/math.js +133 -67
  124. package/dist/overlays/ContextMenu.svelte +65 -40
  125. package/dist/overlays/DraggablePane.svelte +211 -139
  126. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  127. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  128. package/dist/periodic-table/PropertySelect.svelte +25 -7
  129. package/dist/periodic-table/TableInset.svelte +8 -3
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  131. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  133. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  134. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  136. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  137. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  138. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  139. package/dist/phase-diagram/build-diagram.js +9 -9
  140. package/dist/phase-diagram/colors.js +1 -3
  141. package/dist/phase-diagram/parse.js +10 -9
  142. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  143. package/dist/phase-diagram/utils.d.ts +1 -0
  144. package/dist/phase-diagram/utils.js +80 -25
  145. package/dist/plot/AxisLabel.svelte +28 -3
  146. package/dist/plot/BarPlot.svelte +1182 -734
  147. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  148. package/dist/plot/BarPlotControls.svelte +31 -5
  149. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  150. package/dist/plot/ColorBar.svelte +479 -329
  151. package/dist/plot/ColorScaleSelect.svelte +27 -6
  152. package/dist/plot/ElementScatter.svelte +36 -15
  153. package/dist/plot/FillArea.svelte +152 -95
  154. package/dist/plot/Histogram.svelte +934 -571
  155. package/dist/plot/Histogram.svelte.d.ts +1 -1
  156. package/dist/plot/HistogramControls.svelte +53 -9
  157. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  158. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  159. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  160. package/dist/plot/Line.svelte +63 -28
  161. package/dist/plot/PlotControls.svelte +157 -114
  162. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  163. package/dist/plot/PlotLegend.svelte +174 -91
  164. package/dist/plot/PlotTooltip.svelte +45 -6
  165. package/dist/plot/PortalSelect.svelte +175 -147
  166. package/dist/plot/ReferenceLine.svelte +76 -22
  167. package/dist/plot/ReferenceLine3D.svelte +132 -107
  168. package/dist/plot/ReferencePlane.svelte +146 -121
  169. package/dist/plot/ScatterPlot.svelte +1681 -1091
  170. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  171. package/dist/plot/ScatterPlot3D.svelte +256 -131
  172. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  173. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  174. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  175. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  176. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  177. package/dist/plot/ScatterPlotControls.svelte +65 -25
  178. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  179. package/dist/plot/ScatterPoint.svelte +98 -26
  180. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  181. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  182. package/dist/plot/Surface3D.svelte +159 -108
  183. package/dist/plot/ZeroLines.svelte +55 -3
  184. package/dist/plot/ZoomRect.svelte +4 -2
  185. package/dist/plot/axis-utils.js +1 -3
  186. package/dist/plot/data-cleaning.js +12 -28
  187. package/dist/plot/data-transform.js +2 -1
  188. package/dist/plot/fill-utils.js +2 -0
  189. package/dist/plot/layout.d.ts +4 -1
  190. package/dist/plot/layout.js +33 -14
  191. package/dist/plot/reference-line.d.ts +2 -2
  192. package/dist/plot/reference-line.js +7 -5
  193. package/dist/plot/scales.js +24 -36
  194. package/dist/plot/types.d.ts +11 -23
  195. package/dist/plot/types.js +6 -11
  196. package/dist/plot/utils/label-placement.d.ts +32 -15
  197. package/dist/plot/utils/label-placement.js +227 -66
  198. package/dist/plot/utils/series-visibility.js +2 -3
  199. package/dist/rdf/RdfPlot.svelte +143 -91
  200. package/dist/rdf/calc-rdf.js +4 -5
  201. package/dist/sanitize.d.ts +4 -0
  202. package/dist/sanitize.js +107 -0
  203. package/dist/settings.d.ts +18 -6
  204. package/dist/settings.js +46 -16
  205. package/dist/spectral/Bands.svelte +632 -453
  206. package/dist/spectral/BandsAndDos.svelte +90 -49
  207. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  208. package/dist/spectral/Dos.svelte +389 -258
  209. package/dist/spectral/helpers.js +55 -43
  210. package/dist/state.svelte.d.ts +1 -1
  211. package/dist/state.svelte.js +3 -2
  212. package/dist/structure/Arrow.svelte +59 -20
  213. package/dist/structure/AtomLegend.svelte +215 -134
  214. package/dist/structure/Bond.svelte +73 -47
  215. package/dist/structure/CanvasTooltip.svelte +10 -2
  216. package/dist/structure/CellSelect.svelte +72 -45
  217. package/dist/structure/Cylinder.svelte +33 -17
  218. package/dist/structure/Lattice.svelte +88 -33
  219. package/dist/structure/Structure.svelte +1063 -797
  220. package/dist/structure/Structure.svelte.d.ts +1 -1
  221. package/dist/structure/StructureControls.svelte +349 -118
  222. package/dist/structure/StructureExportPane.svelte +124 -89
  223. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  224. package/dist/structure/StructureInfoPane.svelte +304 -237
  225. package/dist/structure/StructureScene.svelte +879 -443
  226. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  227. package/dist/structure/atom-properties.js +8 -8
  228. package/dist/structure/bonding.js +6 -7
  229. package/dist/structure/export.js +14 -29
  230. package/dist/structure/ferrox-wasm.js +1 -1
  231. package/dist/structure/index.d.ts +13 -3
  232. package/dist/structure/index.js +83 -23
  233. package/dist/structure/measure.d.ts +2 -2
  234. package/dist/structure/measure.js +4 -44
  235. package/dist/structure/parse.js +113 -141
  236. package/dist/structure/partial-occupancy.js +7 -10
  237. package/dist/structure/pbc.d.ts +1 -0
  238. package/dist/structure/pbc.js +16 -6
  239. package/dist/structure/supercell.d.ts +2 -2
  240. package/dist/structure/supercell.js +12 -22
  241. package/dist/structure/validation.js +1 -2
  242. package/dist/symmetry/SymmetryStats.svelte +84 -41
  243. package/dist/symmetry/WyckoffTable.svelte +26 -6
  244. package/dist/symmetry/cell-transform.js +5 -3
  245. package/dist/symmetry/index.js +8 -7
  246. package/dist/symmetry/spacegroups.js +148 -148
  247. package/dist/table/HeatmapTable.svelte +790 -554
  248. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  249. package/dist/table/ToggleMenu.svelte +125 -92
  250. package/dist/table/index.js +2 -4
  251. package/dist/theme/ThemeControl.svelte +21 -12
  252. package/dist/time.js +4 -1
  253. package/dist/tooltip/TooltipContent.svelte +33 -8
  254. package/dist/trajectory/Trajectory.svelte +758 -558
  255. package/dist/trajectory/TrajectoryError.svelte +14 -3
  256. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  257. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  258. package/dist/trajectory/extract.js +10 -26
  259. package/dist/trajectory/format-detect.js +5 -5
  260. package/dist/trajectory/frame-reader.d.ts +1 -1
  261. package/dist/trajectory/frame-reader.js +5 -12
  262. package/dist/trajectory/helpers.d.ts +0 -1
  263. package/dist/trajectory/helpers.js +2 -17
  264. package/dist/trajectory/index.js +14 -12
  265. package/dist/trajectory/parse/ase.js +5 -4
  266. package/dist/trajectory/parse/hdf5.js +26 -18
  267. package/dist/trajectory/parse/index.js +13 -18
  268. package/dist/trajectory/parse/lammps.js +17 -7
  269. package/dist/trajectory/parse/vasp.js +5 -2
  270. package/dist/trajectory/parse/xyz.js +8 -7
  271. package/dist/trajectory/plotting.js +13 -8
  272. package/dist/utils.d.ts +1 -0
  273. package/dist/utils.js +13 -0
  274. package/dist/xrd/XrdPlot.svelte +337 -247
  275. package/dist/xrd/broadening.js +14 -9
  276. package/dist/xrd/calc-xrd.js +12 -18
  277. package/dist/xrd/parse.d.ts +1 -1
  278. package/dist/xrd/parse.js +17 -17
  279. package/package.json +99 -103
  280. package/readme.md +1 -1
  281. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -1,100 +1,183 @@
1
- <script lang="ts">import { onDestroy } from 'svelte';
2
- import { SvelteMap, SvelteSet } from 'svelte/reactivity';
3
- // Unique instance ID to prevent gradient ID collisions when multiple legends render on the same page
4
- const instance_id = crypto.randomUUID().slice(0, 8);
5
- let { series_data = [], layout = `vertical`, layout_tracks = 1, // Default to 1 column/row
6
- style = ``, item_style = ``, collapsed_groups = $bindable(new SvelteSet()), on_toggle = () => { }, on_double_click = () => { }, on_fill_toggle, on_fill_double_click, on_group_toggle, on_group_double_click, on_drag_start = () => { }, on_drag = () => { }, on_drag_end = () => { }, on_hover_change, draggable = true, root_element = $bindable(undefined), ...rest } = $props();
7
- let is_dragging = $state(false);
8
- let drag_start_coords = $state(null);
9
- let grouped_series = $derived.by(() => {
10
- const groups = [];
11
- const group_map = new SvelteMap();
1
+ <script lang="ts">
2
+ import type { LegendItem, Orientation } from './'
3
+ import { sanitize_html } from '../sanitize'
4
+ import { strip_html } from '../table'
5
+ import { onDestroy } from 'svelte'
6
+ import type { HTMLAttributes } from 'svelte/elements'
7
+ import { SvelteMap, SvelteSet } from 'svelte/reactivity'
8
+
9
+ // Unique instance ID to prevent gradient ID collisions when multiple legends render on the same page
10
+ const instance_id = crypto.randomUUID().slice(0, 8)
11
+
12
+ let {
13
+ series_data = [],
14
+ layout = `vertical`,
15
+ layout_tracks = 1, // Default to 1 column/row
16
+ style = ``,
17
+ item_style = ``,
18
+ collapsed_groups = $bindable(new SvelteSet<string>()),
19
+ on_toggle = () => {},
20
+ on_double_click = () => {},
21
+ on_fill_toggle,
22
+ on_fill_double_click,
23
+ on_group_toggle,
24
+ on_group_double_click,
25
+ on_drag_start = () => {},
26
+ on_drag = () => {},
27
+ on_drag_end = () => {},
28
+ on_hover_change,
29
+ draggable = true,
30
+ root_element = $bindable<HTMLDivElement | undefined>(undefined),
31
+ ...rest
32
+ }: Omit<HTMLAttributes<HTMLDivElement>, `style`> & {
33
+ series_data: LegendItem[]
34
+ layout?: Orientation
35
+ layout_tracks?: number // Number of columns for horizontal, rows for vertical
36
+ style?: string // Inline styles forwarded to wrapper div
37
+ item_style?: string
38
+ // Bindable set of collapsed group names (pass initial values to collapse groups by default)
39
+ collapsed_groups?: Set<string>
40
+ on_toggle?: (series_idx: number) => void
41
+ on_double_click?: (series_idx: number) => void
42
+ on_fill_toggle?: (
43
+ source_type: `fill_region` | `error_band`,
44
+ source_idx: number,
45
+ ) => void
46
+ on_fill_double_click?: (
47
+ source_type: `fill_region` | `error_band`,
48
+ source_idx: number,
49
+ ) => void
50
+ on_group_toggle?: (group_name: string, series_indices: number[]) => void
51
+ on_group_double_click?: (group_name: string, series_indices: number[]) => void
52
+ on_drag_start?: (event: MouseEvent) => void
53
+ on_drag?: (event: MouseEvent) => void
54
+ on_drag_end?: (event: MouseEvent) => void
55
+ // Callback when hover state changes (for placement stability)
56
+ on_hover_change?: (is_hovered: boolean) => void
57
+ draggable?: boolean
58
+ // Bindable reference to the root DOM element for size measurements
59
+ root_element?: HTMLDivElement
60
+ } = $props()
61
+
62
+ let is_dragging = $state(false)
63
+ let drag_start_coords = $state<{ x: number; y: number } | null>(null)
64
+
65
+ // Group series by legend_group, preserving order
66
+ type GroupedData = { group_name: string | null; items: LegendItem[] }
67
+ let grouped_series = $derived.by<GroupedData[]>(() => {
68
+ const groups: GroupedData[] = []
69
+ const group_map = new SvelteMap<string | null, LegendItem[]>()
70
+
12
71
  for (const item of series_data) {
13
- const group_key = item.legend_group ?? null;
14
- if (!group_map.has(group_key)) {
15
- group_map.set(group_key, []);
16
- groups.push({ group_name: group_key, items: group_map.get(group_key) });
17
- }
18
- group_map.get(group_key).push(item);
72
+ const group_key = item.legend_group ?? null
73
+ let group_items = group_map.get(group_key)
74
+ if (!group_items) {
75
+ group_items = []
76
+ group_map.set(group_key, group_items)
77
+ groups.push({ group_name: group_key, items: group_items })
78
+ }
79
+ group_items.push(item)
19
80
  }
20
- return groups;
21
- });
22
- // Check if any grouping is present
23
- let has_groups = $derived(grouped_series.some((group) => group.group_name !== null && group.items.length > 0));
24
- function toggle_group_collapse(group_name) {
81
+ return groups
82
+ })
83
+
84
+ // Check if any grouping is present
85
+ let has_groups = $derived(
86
+ grouped_series.some((group) =>
87
+ group.group_name !== null && group.items.length > 0
88
+ ),
89
+ )
90
+
91
+ function toggle_group_collapse(group_name: string) {
25
92
  // Normalize to SvelteSet if a plain Set was passed (ensures reactivity)
26
93
  if (!(collapsed_groups instanceof SvelteSet)) {
27
- collapsed_groups = new SvelteSet(collapsed_groups);
94
+ collapsed_groups = new SvelteSet(collapsed_groups)
28
95
  }
29
96
  // Set.delete returns true if element existed, so add if delete failed
30
- if (!collapsed_groups.delete(group_name))
31
- collapsed_groups.add(group_name);
32
- }
33
- const handle_group_click = (group_name, items) => on_group_toggle?.(group_name, items.map((item) => item.series_idx));
34
- function cleanup_drag_listeners() {
97
+ if (!collapsed_groups.delete(group_name)) collapsed_groups.add(group_name)
98
+ }
99
+
100
+ const handle_group_click = (group_name: string, items: LegendItem[]) =>
101
+ on_group_toggle?.(group_name, items.map((item) => item.series_idx))
102
+
103
+ function cleanup_drag_listeners() {
35
104
  if (is_dragging) {
36
- // Remove global event listeners
37
- window.removeEventListener(`mousemove`, handle_window_mouse_move);
38
- window.removeEventListener(`mouseup`, handle_window_mouse_up);
39
- // Reset cursor and text selection
40
- document.body.style.cursor = `default`;
41
- document.body.style.userSelect = `auto`;
105
+ // Remove global event listeners
106
+ window.removeEventListener(`mousemove`, handle_window_mouse_move)
107
+ window.removeEventListener(`mouseup`, handle_window_mouse_up)
108
+
109
+ // Reset cursor and text selection
110
+ document.body.style.cursor = `default`
111
+ document.body.style.userSelect = `auto`
42
112
  }
43
- }
44
- onDestroy(cleanup_drag_listeners);
45
- function handle_legend_mouse_down(event) {
46
- if (!draggable)
47
- return;
113
+ }
114
+ onDestroy(cleanup_drag_listeners)
115
+
116
+ function handle_legend_mouse_down(event: MouseEvent) {
117
+ if (!draggable) return
118
+
48
119
  // Only start drag if clicking on empty areas (not on legend items)
49
- const target = event.target;
50
- if (target instanceof Element && target.closest(`.legend-item`))
51
- return;
52
- event.preventDefault();
53
- event.stopPropagation();
54
- is_dragging = true;
55
- drag_start_coords = { x: event.clientX, y: event.clientY };
56
- on_drag_start(event);
120
+ const target = event.target
121
+ if (target instanceof Element && target.closest(`.legend-item`)) return
122
+
123
+ event.preventDefault()
124
+ event.stopPropagation()
125
+
126
+ is_dragging = true
127
+ drag_start_coords = { x: event.clientX, y: event.clientY }
128
+
129
+ on_drag_start(event)
130
+
57
131
  // Add global event listeners
58
- window.addEventListener(`mousemove`, handle_window_mouse_move);
59
- window.addEventListener(`mouseup`, handle_window_mouse_up);
60
- }
61
- function handle_window_mouse_move(event) {
62
- if (!is_dragging || !drag_start_coords)
63
- return;
64
- event.preventDefault();
65
- on_drag(event);
66
- }
67
- function handle_window_mouse_up(event) {
68
- if (!is_dragging)
69
- return;
70
- is_dragging = false;
71
- drag_start_coords = null;
72
- on_drag_end(event);
132
+ window.addEventListener(`mousemove`, handle_window_mouse_move)
133
+ window.addEventListener(`mouseup`, handle_window_mouse_up)
134
+ }
135
+
136
+ function handle_window_mouse_move(event: MouseEvent) {
137
+ if (!is_dragging || !drag_start_coords) return
138
+
139
+ event.preventDefault()
140
+ on_drag(event)
141
+ }
142
+
143
+ function handle_window_mouse_up(event: MouseEvent) {
144
+ if (!is_dragging) return
145
+
146
+ is_dragging = false
147
+ drag_start_coords = null
148
+
149
+ on_drag_end(event)
150
+
73
151
  // Remove global event listeners
74
- window.removeEventListener(`mousemove`, handle_window_mouse_move);
75
- window.removeEventListener(`mouseup`, handle_window_mouse_up);
76
- }
77
- let div_style = $derived({
78
- horizontal: `grid-template-columns: repeat(${layout_tracks}, auto);`,
79
- vertical: `grid-template-rows: repeat(${layout_tracks}, auto); grid-template-columns: auto;`,
80
- }[layout] + style);
81
- // Extracted toggle handlers to reduce duplication
82
- function toggle_item(item) {
83
- if (item.item_type === `fill` && on_fill_toggle && item.fill_source_type &&
84
- item.fill_source_idx !== undefined) {
85
- on_fill_toggle(item.fill_source_type, item.fill_source_idx);
86
- }
87
- else
88
- on_toggle(item.series_idx);
89
- }
90
- function double_click_item(item) {
91
- if (item.item_type === `fill` && on_fill_double_click && item.fill_source_type &&
92
- item.fill_source_idx !== undefined) {
93
- on_fill_double_click(item.fill_source_type, item.fill_source_idx);
94
- }
95
- else
96
- on_double_click(item.series_idx);
97
- }
152
+ window.removeEventListener(`mousemove`, handle_window_mouse_move)
153
+ window.removeEventListener(`mouseup`, handle_window_mouse_up)
154
+ }
155
+
156
+ let div_style = $derived(
157
+ {
158
+ horizontal: `grid-template-columns: repeat(${layout_tracks}, auto);`,
159
+ vertical:
160
+ `grid-template-rows: repeat(${layout_tracks}, auto); grid-template-columns: auto;`,
161
+ }[layout] + style,
162
+ )
163
+
164
+ // Extracted toggle handlers to reduce duplication
165
+ function toggle_item(item: LegendItem) {
166
+ if (
167
+ item.item_type === `fill` && on_fill_toggle && item.fill_source_type &&
168
+ item.fill_source_idx !== undefined
169
+ ) {
170
+ on_fill_toggle(item.fill_source_type, item.fill_source_idx)
171
+ } else on_toggle(item.series_idx)
172
+ }
173
+ function double_click_item(item: LegendItem) {
174
+ if (
175
+ item.item_type === `fill` && on_fill_double_click && item.fill_source_type &&
176
+ item.fill_source_idx !== undefined
177
+ ) {
178
+ on_fill_double_click(item.fill_source_type, item.fill_source_idx)
179
+ } else on_double_click(item.series_idx)
180
+ }
98
181
  </script>
99
182
 
100
183
  {#snippet legend_item(series: LegendItem, indent: boolean = false)}
@@ -124,7 +207,7 @@ function double_click_item(item) {
124
207
  role="button"
125
208
  tabindex="0"
126
209
  aria-pressed={series.visible}
127
- aria-label="Toggle visibility for {series.label}"
210
+ aria-label="Toggle visibility for {strip_html(series.label)}"
128
211
  >
129
212
  <span class="legend-marker">
130
213
  <!-- Fill region swatch -->
@@ -213,7 +296,7 @@ function double_click_item(item) {
213
296
  {/if}
214
297
  {/if}
215
298
  </span>
216
- <span class="legend-label">{@html series.label}</span>
299
+ <span class="legend-label">{@html sanitize_html(series.label)}</span>
217
300
  </div>
218
301
  {/snippet}
219
302
 
@@ -256,7 +339,7 @@ function double_click_item(item) {
256
339
  role="button"
257
340
  tabindex="0"
258
341
  aria-expanded={!is_collapsed}
259
- aria-label="Toggle group {group_name}"
342
+ aria-label="Toggle group {strip_html(group_name)}"
260
343
  >
261
344
  <span
262
345
  class="group-chevron"
@@ -275,11 +358,11 @@ function double_click_item(item) {
275
358
  }}
276
359
  role="button"
277
360
  tabindex="0"
278
- aria-label="{is_collapsed ? `Expand` : `Collapse`} group {group_name}"
361
+ aria-label="{is_collapsed ? `Expand` : `Collapse`} group {strip_html(group_name)}"
279
362
  >
280
363
 
281
364
  </span>
282
- <span class="group-label">{@html group_name}</span>
365
+ <span class="group-label">{@html sanitize_html(group_name)}</span>
283
366
  </div>
284
367
  <!-- Group items (collapsible) -->
285
368
  {#if !is_collapsed}
@@ -1,9 +1,48 @@
1
- <script lang="ts">import { luminance } from '../colors';
2
- let { x, y, bg_color, offset = { x: 6, y: 0 }, fixed = false, wrapper = $bindable(), children, ...rest } = $props();
3
- // Auto-compute contrasting text color based on background luminance only if bg_color is defined
4
- const text_color = $derived(bg_color != null ? (luminance(bg_color) > 0.5 ? `#000000` : `#ffffff`) : null);
5
- const style = $derived(`position: ${fixed ? `fixed` : `absolute`}; pointer-events: none;
6
- left: ${x + offset.x}px; top: ${y + offset.y}px; ${rest.style ?? ``}`);
1
+ <script lang="ts">
2
+ import { luminance } from '../colors'
3
+ import type { Snippet } from 'svelte'
4
+ import type { HTMLAttributes } from 'svelte/elements'
5
+
6
+ let {
7
+ x,
8
+ y,
9
+ bg_color,
10
+ offset = { x: 6, y: 0 },
11
+ fixed = false,
12
+ wrapper = $bindable(),
13
+ children,
14
+ ...rest
15
+ }: HTMLAttributes<HTMLDivElement> & {
16
+ x: number
17
+ y: number
18
+ bg_color?: string | null
19
+ offset?: { x: number; y: number }
20
+ fixed?: boolean // Use position: fixed (for viewport coords) vs absolute
21
+ wrapper?: HTMLDivElement // Bindable reference for measuring tooltip size
22
+ children: Snippet
23
+ } = $props()
24
+
25
+ // Auto-compute contrasting text color based on background luminance only if bg_color is defined
26
+ const text_color = $derived(
27
+ bg_color != null ? (luminance(bg_color) > 0.5 ? `#000000` : `#ffffff`) : null,
28
+ )
29
+
30
+ // For fixed positioning (viewport coords), flip to opposite side when near viewport edges
31
+ const pos = $derived.by(() => {
32
+ const raw_x = x + offset.x
33
+ const raw_y = y + offset.y
34
+ if (!fixed) return { x: raw_x, y: raw_y }
35
+ const tw = wrapper?.offsetWidth ?? 0
36
+ const th = wrapper?.offsetHeight ?? 0
37
+ const cx = raw_x + tw > globalThis.innerWidth ? x - Math.abs(offset.x) - tw : raw_x
38
+ const cy = raw_y + th > globalThis.innerHeight ? y - Math.abs(offset.y) - th : raw_y
39
+ return { x: Math.max(0, cx), y: Math.max(0, cy) }
40
+ })
41
+
42
+ const style = $derived(
43
+ `position: ${fixed ? `fixed` : `absolute`}; pointer-events: none;
44
+ left: ${pos.x}px; top: ${pos.y}px; ${rest.style ?? ``}`,
45
+ )
7
46
  </script>
8
47
 
9
48
  <div