matterviz 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) 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/feedback/ClickFeedback.svelte +16 -5
  76. package/dist/feedback/DragOverlay.svelte +10 -2
  77. package/dist/feedback/Spinner.svelte +4 -2
  78. package/dist/feedback/StatusMessage.svelte +8 -2
  79. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  80. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  81. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  82. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  84. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  86. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  87. package/dist/fermi-surface/compute.js +16 -20
  88. package/dist/fermi-surface/parse.js +24 -14
  89. package/dist/fermi-surface/symmetry.js +2 -7
  90. package/dist/fermi-surface/types.d.ts +3 -5
  91. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  93. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  95. package/dist/icons.js +47 -0
  96. package/dist/index.d.ts +2 -1
  97. package/dist/index.js +2 -1
  98. package/dist/io/decompress.js +1 -1
  99. package/dist/io/export.d.ts +3 -0
  100. package/dist/io/export.js +129 -143
  101. package/dist/io/is-binary.js +2 -3
  102. package/dist/io/url-drop.js +1 -2
  103. package/dist/isosurface/Isosurface.svelte +202 -148
  104. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  105. package/dist/isosurface/parse.js +34 -29
  106. package/dist/isosurface/slice.js +5 -10
  107. package/dist/isosurface/types.d.ts +2 -1
  108. package/dist/isosurface/types.js +61 -12
  109. package/dist/labels.js +11 -8
  110. package/dist/layout/FullscreenToggle.svelte +11 -2
  111. package/dist/layout/InfoCard.svelte +38 -6
  112. package/dist/layout/InfoTag.svelte +63 -32
  113. package/dist/layout/PropertyFilter.svelte +82 -37
  114. package/dist/layout/SettingsSection.svelte +85 -55
  115. package/dist/layout/SubpageGrid.svelte +10 -2
  116. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  117. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  118. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  119. package/dist/layout/json-tree/utils.js +4 -2
  120. package/dist/marching-cubes.js +25 -2
  121. package/dist/math.d.ts +13 -17
  122. package/dist/math.js +133 -67
  123. package/dist/overlays/ContextMenu.svelte +65 -40
  124. package/dist/overlays/DraggablePane.svelte +211 -139
  125. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  126. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  127. package/dist/periodic-table/PropertySelect.svelte +25 -7
  128. package/dist/periodic-table/TableInset.svelte +8 -3
  129. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  131. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  133. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  134. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  136. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  137. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  138. package/dist/phase-diagram/build-diagram.js +9 -9
  139. package/dist/phase-diagram/colors.js +1 -3
  140. package/dist/phase-diagram/parse.js +10 -9
  141. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  142. package/dist/phase-diagram/utils.d.ts +1 -0
  143. package/dist/phase-diagram/utils.js +80 -25
  144. package/dist/plot/AxisLabel.svelte +28 -3
  145. package/dist/plot/BarPlot.svelte +1182 -734
  146. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  147. package/dist/plot/BarPlotControls.svelte +31 -5
  148. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  149. package/dist/plot/ColorBar.svelte +479 -329
  150. package/dist/plot/ColorScaleSelect.svelte +27 -6
  151. package/dist/plot/ElementScatter.svelte +36 -15
  152. package/dist/plot/FillArea.svelte +152 -95
  153. package/dist/plot/Histogram.svelte +934 -571
  154. package/dist/plot/Histogram.svelte.d.ts +1 -1
  155. package/dist/plot/HistogramControls.svelte +53 -9
  156. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  157. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  158. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  159. package/dist/plot/Line.svelte +63 -28
  160. package/dist/plot/PlotControls.svelte +157 -114
  161. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  162. package/dist/plot/PlotLegend.svelte +174 -91
  163. package/dist/plot/PlotTooltip.svelte +45 -6
  164. package/dist/plot/PortalSelect.svelte +175 -147
  165. package/dist/plot/ReferenceLine.svelte +76 -22
  166. package/dist/plot/ReferenceLine3D.svelte +132 -107
  167. package/dist/plot/ReferencePlane.svelte +146 -121
  168. package/dist/plot/ScatterPlot.svelte +1681 -1091
  169. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  170. package/dist/plot/ScatterPlot3D.svelte +256 -131
  171. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  172. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  173. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  174. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  175. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  176. package/dist/plot/ScatterPlotControls.svelte +65 -25
  177. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  178. package/dist/plot/ScatterPoint.svelte +98 -26
  179. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  180. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  181. package/dist/plot/Surface3D.svelte +159 -108
  182. package/dist/plot/ZeroLines.svelte +55 -3
  183. package/dist/plot/ZoomRect.svelte +4 -2
  184. package/dist/plot/axis-utils.js +1 -3
  185. package/dist/plot/data-cleaning.js +12 -28
  186. package/dist/plot/data-transform.js +2 -1
  187. package/dist/plot/fill-utils.js +2 -0
  188. package/dist/plot/layout.d.ts +4 -1
  189. package/dist/plot/layout.js +33 -14
  190. package/dist/plot/reference-line.d.ts +2 -2
  191. package/dist/plot/reference-line.js +7 -5
  192. package/dist/plot/scales.js +24 -36
  193. package/dist/plot/types.d.ts +11 -23
  194. package/dist/plot/types.js +6 -11
  195. package/dist/plot/utils/label-placement.d.ts +32 -15
  196. package/dist/plot/utils/label-placement.js +227 -66
  197. package/dist/plot/utils/series-visibility.js +2 -3
  198. package/dist/rdf/RdfPlot.svelte +143 -91
  199. package/dist/rdf/calc-rdf.js +4 -5
  200. package/dist/sanitize.d.ts +4 -0
  201. package/dist/sanitize.js +107 -0
  202. package/dist/settings.d.ts +18 -6
  203. package/dist/settings.js +46 -16
  204. package/dist/spectral/Bands.svelte +632 -453
  205. package/dist/spectral/BandsAndDos.svelte +90 -49
  206. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  207. package/dist/spectral/Dos.svelte +389 -258
  208. package/dist/spectral/helpers.js +55 -43
  209. package/dist/state.svelte.d.ts +1 -1
  210. package/dist/state.svelte.js +3 -2
  211. package/dist/structure/Arrow.svelte +59 -20
  212. package/dist/structure/AtomLegend.svelte +215 -134
  213. package/dist/structure/Bond.svelte +73 -47
  214. package/dist/structure/CanvasTooltip.svelte +10 -2
  215. package/dist/structure/CellSelect.svelte +72 -45
  216. package/dist/structure/Cylinder.svelte +33 -17
  217. package/dist/structure/Lattice.svelte +88 -33
  218. package/dist/structure/Structure.svelte +1063 -797
  219. package/dist/structure/Structure.svelte.d.ts +1 -1
  220. package/dist/structure/StructureControls.svelte +349 -118
  221. package/dist/structure/StructureExportPane.svelte +124 -89
  222. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  223. package/dist/structure/StructureInfoPane.svelte +304 -237
  224. package/dist/structure/StructureScene.svelte +879 -443
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  226. package/dist/structure/atom-properties.js +8 -8
  227. package/dist/structure/bonding.js +6 -7
  228. package/dist/structure/export.js +14 -29
  229. package/dist/structure/ferrox-wasm.js +1 -1
  230. package/dist/structure/index.d.ts +13 -3
  231. package/dist/structure/index.js +83 -23
  232. package/dist/structure/measure.d.ts +2 -2
  233. package/dist/structure/measure.js +4 -44
  234. package/dist/structure/parse.js +113 -141
  235. package/dist/structure/partial-occupancy.js +7 -10
  236. package/dist/structure/pbc.d.ts +1 -0
  237. package/dist/structure/pbc.js +16 -6
  238. package/dist/structure/supercell.d.ts +2 -2
  239. package/dist/structure/supercell.js +12 -22
  240. package/dist/structure/validation.js +1 -2
  241. package/dist/symmetry/SymmetryStats.svelte +84 -41
  242. package/dist/symmetry/WyckoffTable.svelte +26 -6
  243. package/dist/symmetry/cell-transform.js +5 -3
  244. package/dist/symmetry/index.js +8 -7
  245. package/dist/symmetry/spacegroups.js +148 -148
  246. package/dist/table/HeatmapTable.svelte +790 -554
  247. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  248. package/dist/table/ToggleMenu.svelte +125 -92
  249. package/dist/table/index.js +2 -4
  250. package/dist/theme/ThemeControl.svelte +21 -12
  251. package/dist/time.js +4 -1
  252. package/dist/tooltip/TooltipContent.svelte +33 -8
  253. package/dist/trajectory/Trajectory.svelte +758 -558
  254. package/dist/trajectory/TrajectoryError.svelte +14 -3
  255. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  256. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  257. package/dist/trajectory/extract.js +10 -26
  258. package/dist/trajectory/format-detect.js +5 -5
  259. package/dist/trajectory/frame-reader.d.ts +1 -1
  260. package/dist/trajectory/frame-reader.js +5 -12
  261. package/dist/trajectory/helpers.d.ts +0 -1
  262. package/dist/trajectory/helpers.js +2 -17
  263. package/dist/trajectory/index.js +14 -12
  264. package/dist/trajectory/parse/ase.js +5 -4
  265. package/dist/trajectory/parse/hdf5.js +26 -18
  266. package/dist/trajectory/parse/index.js +13 -18
  267. package/dist/trajectory/parse/lammps.js +17 -7
  268. package/dist/trajectory/parse/vasp.js +5 -2
  269. package/dist/trajectory/parse/xyz.js +8 -7
  270. package/dist/trajectory/plotting.js +13 -8
  271. package/dist/utils.d.ts +1 -0
  272. package/dist/utils.js +13 -0
  273. package/dist/xrd/XrdPlot.svelte +337 -247
  274. package/dist/xrd/broadening.js +14 -9
  275. package/dist/xrd/calc-xrd.js +12 -18
  276. package/dist/xrd/parse.d.ts +1 -1
  277. package/dist/xrd/parse.js +17 -17
  278. package/package.json +99 -103
  279. package/readme.md +1 -1
  280. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -1,170 +1,198 @@
1
- <script module lang="ts">"use strict";
2
- // Track active dropdown across all instances - only one can be open at a time
3
- let active_close_fn = null;
1
+ <script module lang="ts">
2
+ // Track active dropdown across all instances - only one can be open at a time
3
+ let active_close_fn: (() => void) | null = null
4
4
  </script>
5
5
 
6
- <script lang="ts">let { options, selected_key = $bindable(), on_select, disabled = false, format_option = (opt) => (opt.unit ? `${opt.label} (${opt.unit})` : opt.label), ...rest } = $props();
7
- let dropdown_open = $state(false);
8
- let trigger_el = $state();
9
- let portal_el;
10
- const selected_option = $derived(options?.find((opt) => opt.key === selected_key) ?? options?.[0]);
11
- function update_position() {
12
- if (!trigger_el || !portal_el)
13
- return;
14
- const rect = trigger_el.getBoundingClientRect();
15
- const dropdown_rect = portal_el.getBoundingClientRect();
16
- const gap = 4;
17
- const vw = window.innerWidth;
18
- const vh = window.innerHeight;
6
+ <script lang="ts">
7
+ import { sanitize_html } from '../sanitize'
8
+ import type { HTMLButtonAttributes } from 'svelte/elements'
9
+
10
+ type Option = { key: string; label: string; unit?: string }
11
+
12
+ let {
13
+ options,
14
+ selected_key = $bindable(),
15
+ on_select,
16
+ disabled = false,
17
+ format_option = (
18
+ opt: Option,
19
+ ) => (opt.unit ? `${opt.label} (${opt.unit})` : opt.label),
20
+ ...rest
21
+ }: Omit<HTMLButtonAttributes, `onclick`> & {
22
+ options: Option[]
23
+ selected_key?: string
24
+ on_select?: (key: string, prev_key?: string) => void | Promise<void>
25
+ disabled?: boolean
26
+ format_option?: (opt: Option) => string
27
+ } = $props()
28
+
29
+ let dropdown_open = $state(false)
30
+ let trigger_el: HTMLButtonElement | undefined = $state()
31
+ let portal_el: HTMLDivElement | undefined
32
+
33
+ const selected_option = $derived(
34
+ options?.find((opt) => opt.key === selected_key) ?? options?.[0],
35
+ )
36
+
37
+ function update_position() {
38
+ if (!trigger_el || !portal_el) return
39
+ const rect = trigger_el.getBoundingClientRect()
40
+ const dropdown_rect = portal_el.getBoundingClientRect()
41
+ const gap = 4
42
+ const vw = window.innerWidth
43
+ const vh = window.innerHeight
44
+
19
45
  // Vertical: prefer below, flip above if no room
20
- const below_y = rect.bottom + gap;
21
- const above_y = rect.top - gap - dropdown_rect.height;
22
- const fits_below = below_y + dropdown_rect.height <= vh;
23
- portal_el.style.top = `${fits_below ? below_y : Math.max(gap, above_y)}px`;
46
+ const below_y = rect.bottom + gap
47
+ const above_y = rect.top - gap - dropdown_rect.height
48
+ const fits_below = below_y + dropdown_rect.height <= vh
49
+ portal_el.style.top = `${fits_below ? below_y : Math.max(gap, above_y)}px`
50
+
24
51
  // Horizontal: center, but clamp to viewport edges
25
- const center_x = rect.left + rect.width / 2;
26
- const half_width = dropdown_rect.width / 2;
27
- const min_x = half_width + gap;
28
- const max_x = vw - half_width - gap;
29
- portal_el.style.left = `${Math.max(min_x, Math.min(max_x, center_x))}px`;
30
- }
31
- // Inline styles for portal elements (can't use scoped CSS for elements in document.body)
32
- const portal_styles = {
52
+ const center_x = rect.left + rect.width / 2
53
+ const half_width = dropdown_rect.width / 2
54
+ const min_x = half_width + gap
55
+ const max_x = vw - half_width - gap
56
+ portal_el.style.left = `${Math.max(min_x, Math.min(max_x, center_x))}px`
57
+ }
58
+
59
+ // Inline styles for portal elements (can't use scoped CSS for elements in document.body)
60
+ const portal_styles = {
33
61
  container: `position: fixed; transform: translateX(-50%); z-index: 10000;`,
34
- ul: `margin: 0; padding: 0; list-style: none; background: var(--dropdown-bg, white); border: 1px solid var(--dropdown-border, #ccc); border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: max-content; max-height: 300px; overflow-y: auto; font-size: 14px;`,
62
+ ul:
63
+ `margin: 0; padding: 0; list-style: none; background: var(--dropdown-bg, white); border: 1px solid var(--dropdown-border, #ccc); border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: max-content; max-height: 300px; overflow-y: auto; font-size: 14px;`,
35
64
  li: `margin: 0;`,
36
- btn: `display: block; width: 100%; padding: var(--dropdown-padding-v, 3px) var(--dropdown-padding-h, 10px); border: none; background: transparent; font: inherit; color: var(--dropdown-color, black); text-align: left; cursor: pointer; white-space: nowrap;`,
65
+ btn:
66
+ `display: block; width: 100%; padding: var(--dropdown-padding-v, 3px) var(--dropdown-padding-h, 10px); border: none; background: transparent; font: inherit; color: var(--dropdown-color, black); text-align: left; cursor: pointer; white-space: nowrap;`,
37
67
  btn_selected: `font-weight: 500; background: rgba(0, 100, 200, 0.15);`,
38
- };
39
- function style_sub_sup(el) {
68
+ }
69
+
70
+ function style_sub_sup(el: HTMLElement) {
40
71
  for (const sub of el.querySelectorAll(`sub`)) {
41
- ;
42
- sub.style.cssText =
43
- `font-size: 0.75em; line-height: 0; margin: 0; padding: 0; position: relative; top: 0.15em;`;
72
+ ;(sub as HTMLElement).style.cssText =
73
+ `font-size: 0.75em; line-height: 0; margin: 0; padding: 0; position: relative; top: 0.15em;`
44
74
  }
45
75
  for (const sup of el.querySelectorAll(`sup`)) {
46
- ;
47
- sup.style.cssText =
48
- `font-size: 0.75em; line-height: 0; margin: 0; padding: 0; position: relative; top: -0.4em;`;
76
+ ;(sup as HTMLElement).style.cssText =
77
+ `font-size: 0.75em; line-height: 0; margin: 0; padding: 0; position: relative; top: -0.4em;`
49
78
  }
50
- }
51
- function open_dropdown() {
52
- if (!trigger_el || !options?.length)
53
- return;
54
- if (active_close_fn && active_close_fn !== close_dropdown)
55
- active_close_fn();
56
- portal_el = document.createElement(`div`);
57
- portal_el.className = `portal-select-dropdown`;
58
- portal_el.style.cssText = portal_styles.container;
59
- portal_el.setAttribute(`role`, `listbox`);
60
- const ul = document.createElement(`ul`);
61
- ul.style.cssText = portal_styles.ul;
62
- let selected_btn;
79
+ }
80
+
81
+ function open_dropdown() {
82
+ if (!trigger_el || !options?.length) return
83
+ if (active_close_fn && active_close_fn !== close_dropdown) active_close_fn()
84
+
85
+ portal_el = document.createElement(`div`)
86
+ portal_el.className = `portal-select-dropdown`
87
+ portal_el.style.cssText = portal_styles.container
88
+ portal_el.setAttribute(`role`, `listbox`)
89
+
90
+ const ul = document.createElement(`ul`)
91
+ ul.style.cssText = portal_styles.ul
92
+ let selected_btn: HTMLButtonElement | undefined
63
93
  for (const opt of options) {
64
- const li = document.createElement(`li`);
65
- li.style.cssText = portal_styles.li;
66
- li.setAttribute(`role`, `presentation`);
67
- const btn = document.createElement(`button`);
68
- btn.type = `button`;
69
- btn.style.cssText = portal_styles.btn;
70
- btn.setAttribute(`role`, `option`);
71
- btn.innerHTML = format_option(opt);
72
- style_sub_sup(btn);
73
- btn.onclick = () => select(opt.key);
74
- btn.onmouseenter = () => {
75
- if (!btn.classList.contains(`selected`)) {
76
- btn.style.background = `rgba(128, 128, 128, 0.15)`;
77
- }
78
- };
79
- btn.onmouseleave = () => {
80
- if (!btn.classList.contains(`selected`))
81
- btn.style.background = `transparent`;
82
- };
83
- if (opt.key === selected_key) {
84
- btn.classList.add(`selected`);
85
- btn.style.cssText = portal_styles.btn + portal_styles.btn_selected;
86
- btn.setAttribute(`aria-selected`, `true`);
87
- selected_btn = btn;
94
+ const li = document.createElement(`li`)
95
+ li.style.cssText = portal_styles.li
96
+ li.setAttribute(`role`, `presentation`)
97
+ const btn = document.createElement(`button`)
98
+ btn.type = `button`
99
+ btn.style.cssText = portal_styles.btn
100
+ btn.setAttribute(`role`, `option`)
101
+ btn.innerHTML = sanitize_html(format_option(opt))
102
+ style_sub_sup(btn)
103
+ btn.onclick = () => select(opt.key)
104
+ btn.onmouseenter = () => {
105
+ if (!btn.classList.contains(`selected`)) {
106
+ btn.style.background = `rgba(128, 128, 128, 0.15)`
88
107
  }
89
- li.appendChild(btn);
90
- ul.appendChild(li);
108
+ }
109
+ btn.onmouseleave = () => {
110
+ if (!btn.classList.contains(`selected`)) btn.style.background = `transparent`
111
+ }
112
+ if (opt.key === selected_key) {
113
+ btn.classList.add(`selected`)
114
+ btn.style.cssText = portal_styles.btn + portal_styles.btn_selected
115
+ btn.setAttribute(`aria-selected`, `true`)
116
+ selected_btn = btn
117
+ }
118
+ li.appendChild(btn)
119
+ ul.appendChild(li)
91
120
  }
92
- portal_el.appendChild(ul);
93
- document.body.appendChild(portal_el);
94
- update_position();
95
- dropdown_open = true;
96
- active_close_fn = close_dropdown;
97
- window.addEventListener(`scroll`, update_position, true);
98
- window.addEventListener(`resize`, update_position);
99
- window.addEventListener(`keydown`, handle_keydown);
100
- selected_btn?.focus();
101
- }
102
- function close_dropdown(return_focus = true) {
121
+
122
+ portal_el.appendChild(ul)
123
+ document.body.appendChild(portal_el)
124
+ update_position()
125
+ dropdown_open = true
126
+ active_close_fn = close_dropdown
127
+ window.addEventListener(`scroll`, update_position, true)
128
+ window.addEventListener(`resize`, update_position)
129
+ window.addEventListener(`keydown`, handle_keydown)
130
+ selected_btn?.focus()
131
+ }
132
+
133
+ function close_dropdown(return_focus = true) {
103
134
  if (portal_el) {
104
- window.removeEventListener(`scroll`, update_position, true);
105
- window.removeEventListener(`resize`, update_position);
106
- window.removeEventListener(`keydown`, handle_keydown);
107
- portal_el.remove();
108
- portal_el = undefined;
135
+ window.removeEventListener(`scroll`, update_position, true)
136
+ window.removeEventListener(`resize`, update_position)
137
+ window.removeEventListener(`keydown`, handle_keydown)
138
+ portal_el.remove()
139
+ portal_el = undefined
109
140
  }
110
- if (active_close_fn === close_dropdown)
111
- active_close_fn = null;
112
- dropdown_open = false;
113
- if (return_focus)
114
- trigger_el?.focus();
115
- }
116
- async function select(key) {
141
+ if (active_close_fn === close_dropdown) active_close_fn = null
142
+ dropdown_open = false
143
+ if (return_focus) trigger_el?.focus()
144
+ }
145
+
146
+ async function select(key: string) {
117
147
  if (key !== selected_key) {
118
- const prev_key = selected_key;
119
- selected_key = key; // Optimistic update for responsive UI
120
- try {
121
- await on_select?.(key, prev_key);
122
- }
123
- catch {
124
- selected_key = prev_key; // Roll back on error
125
- }
148
+ const prev_key = selected_key
149
+ selected_key = key // Optimistic update for responsive UI
150
+ try {
151
+ await on_select?.(key, prev_key)
152
+ } catch {
153
+ selected_key = prev_key // Roll back on error
154
+ }
126
155
  }
127
- close_dropdown();
128
- }
129
- function handle_click_outside(evt) {
130
- if (!dropdown_open)
131
- return;
132
- const target = evt.target;
133
- if (!(target instanceof Node) ||
134
- (!trigger_el?.contains(target) && !portal_el?.contains(target))) {
135
- close_dropdown();
156
+ close_dropdown()
157
+ }
158
+
159
+ function handle_click_outside(evt: MouseEvent) {
160
+ if (!dropdown_open) return
161
+ const target = evt.target
162
+ if (
163
+ !(target instanceof Node) ||
164
+ (!trigger_el?.contains(target) && !portal_el?.contains(target))
165
+ ) {
166
+ close_dropdown()
136
167
  }
137
- }
138
- function handle_keydown(evt) {
139
- if (!portal_el)
140
- return;
141
- const buttons = [...portal_el.querySelectorAll(`button`)];
142
- const idx = buttons.indexOf(document.activeElement);
143
- const len = buttons.length;
168
+ }
169
+
170
+ function handle_keydown(evt: KeyboardEvent) {
171
+ if (!portal_el) return
172
+ const buttons = [...portal_el.querySelectorAll(`button`)] as HTMLButtonElement[]
173
+ const idx = buttons.indexOf(document.activeElement as HTMLButtonElement)
174
+ const len = buttons.length
175
+
144
176
  if (evt.key === `Escape`) {
145
- evt.preventDefault();
146
- close_dropdown();
147
- }
148
- else if (evt.key === `ArrowDown`) {
149
- evt.preventDefault();
150
- buttons[(idx + 1) % len]?.focus();
177
+ evt.preventDefault()
178
+ close_dropdown()
179
+ } else if (evt.key === `ArrowDown`) {
180
+ evt.preventDefault()
181
+ buttons[(idx + 1) % len]?.focus()
182
+ } else if (evt.key === `ArrowUp`) {
183
+ evt.preventDefault()
184
+ buttons[idx < 0 ? len - 1 : (idx - 1 + len) % len]?.focus()
185
+ } else if (evt.key === `Enter` && idx >= 0) {
186
+ evt.preventDefault()
187
+ buttons[idx].click()
151
188
  }
152
- else if (evt.key === `ArrowUp`) {
153
- evt.preventDefault();
154
- buttons[idx < 0 ? len - 1 : (idx - 1 + len) % len]?.focus();
155
- }
156
- else if (evt.key === `Enter` && idx >= 0) {
157
- evt.preventDefault();
158
- buttons[idx].click();
159
- }
160
- }
161
- // Close dropdown when disabled, options empty, or component unmounts
162
- $effect(() => {
163
- if ((disabled || !options?.length) && dropdown_open)
164
- close_dropdown(false);
165
- return () => close_dropdown(false);
166
- });
167
- export {};
189
+ }
190
+
191
+ // Close dropdown when disabled, options empty, or component unmounts
192
+ $effect(() => {
193
+ if ((disabled || !options?.length) && dropdown_open) close_dropdown(false)
194
+ return () => close_dropdown(false)
195
+ })
168
196
  </script>
169
197
 
170
198
  <svelte:window onclick={handle_click_outside} />
@@ -180,7 +208,7 @@ export {};
180
208
  {...rest}
181
209
  class="portal-select-trigger {rest.class ?? ``}"
182
210
  >
183
- {@html format_option(selected_option)}
211
+ {@html sanitize_html(format_option(selected_option))}
184
212
  <span class="arrow">▾</span>
185
213
  </button>
186
214
  {/if}
@@ -1,40 +1,94 @@
1
- <script lang="ts">// ReferenceLine: 2D reference lines with annotations (horizontal, vertical, diagonal, segment, line)
2
- import { calculate_annotation_position, resolve_line_endpoints, } from './reference-line';
3
- import { REF_LINE_STYLE_DEFAULTS } from './types';
4
- let { ref_line, line_idx, x_min, x_max, y_min, y_max, x_scale, x2_scale, y_scale, y2_scale, clip_path_id, hovered_line_idx = null, on_click, on_hover, } = $props();
5
- let endpoints = $derived(resolve_line_endpoints(ref_line, { x_min, x_max, y_min, y_max }, {
1
+ <script lang="ts">
2
+ // ReferenceLine: 2D reference lines with annotations (horizontal, vertical, diagonal, segment, line)
3
+ import {
4
+ calculate_annotation_position,
5
+ resolve_line_endpoints,
6
+ } from './reference-line'
7
+ import type { RefLine, RefLineEvent, RefLineStyle } from './types'
8
+ import { REF_LINE_STYLE_DEFAULTS } from './types'
9
+
10
+ let {
11
+ ref_line,
12
+ line_idx,
13
+ x_min,
14
+ x_max,
15
+ y_min,
16
+ y_max,
6
17
  x_scale,
7
18
  x2_scale,
8
19
  y_scale,
9
20
  y2_scale,
10
- }));
11
- let is_focused = $state(false);
12
- let is_hovered = $derived(hovered_line_idx === line_idx || is_focused);
13
- let is_clickable = $derived(Boolean(on_click || ref_line.on_click));
14
- let style = $derived({
21
+ clip_path_id,
22
+ hovered_line_idx = null,
23
+ on_click,
24
+ on_hover,
25
+ }: {
26
+ ref_line: RefLine
27
+ line_idx: number
28
+ x_min: number
29
+ x_max: number
30
+ y_min: number
31
+ y_max: number
32
+ x_scale: (val: number) => number
33
+ x2_scale?: (val: number) => number
34
+ y_scale: (val: number) => number
35
+ y2_scale?: (val: number) => number
36
+ clip_path_id: string
37
+ hovered_line_idx?: number | null
38
+ on_click?: (event: RefLineEvent) => void
39
+ on_hover?: (event: RefLineEvent | null) => void
40
+ } = $props()
41
+
42
+ let endpoints = $derived(
43
+ resolve_line_endpoints(ref_line, { x_min, x_max, y_min, y_max }, {
44
+ x_scale,
45
+ x2_scale,
46
+ y_scale,
47
+ y2_scale,
48
+ }),
49
+ )
50
+
51
+ let is_focused = $state(false)
52
+ let is_hovered = $derived(hovered_line_idx === line_idx || is_focused)
53
+ let is_clickable = $derived(Boolean(on_click || ref_line.on_click))
54
+
55
+ let style = $derived<Required<RefLineStyle>>({
15
56
  ...REF_LINE_STYLE_DEFAULTS,
16
57
  ...ref_line.style,
17
58
  ...(is_hovered && ref_line.hover_style),
18
- });
19
- let annotation_pos = $derived(endpoints && ref_line.annotation
20
- ? calculate_annotation_position(endpoints[0], endpoints[1], endpoints[2], endpoints[3], ref_line.annotation)
21
- : null);
22
- const make_event = (event) => ({
59
+ })
60
+
61
+ let annotation_pos = $derived(
62
+ endpoints && ref_line.annotation
63
+ ? calculate_annotation_position(
64
+ endpoints[0],
65
+ endpoints[1],
66
+ endpoints[2],
67
+ endpoints[3],
68
+ ref_line.annotation,
69
+ )
70
+ : null,
71
+ )
72
+
73
+ const make_event = (
74
+ event: MouseEvent | KeyboardEvent | FocusEvent,
75
+ ): RefLineEvent => ({
23
76
  event,
24
77
  line_idx,
25
78
  line_id: ref_line.id,
26
79
  type: ref_line.type,
27
80
  label: ref_line.label ?? ref_line.annotation?.text,
28
81
  metadata: ref_line.metadata,
29
- });
30
- function handle_keydown(event) {
82
+ })
83
+
84
+ function handle_keydown(event: KeyboardEvent) {
31
85
  if (event.key === `Enter` || event.key === ` `) {
32
- event.preventDefault();
33
- const evt = make_event(event);
34
- ref_line.on_click?.(evt);
35
- on_click?.(evt);
86
+ event.preventDefault()
87
+ const evt = make_event(event)
88
+ ref_line.on_click?.(evt)
89
+ on_click?.(evt)
36
90
  }
37
- }
91
+ }
38
92
  </script>
39
93
 
40
94
  {#if endpoints && ref_line.visible !== false}