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,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}