@ixfx/components 0.4.3 → 0.5.0

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 (239) hide show
  1. package/bundle/index.d.ts +518 -1176
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +4861 -17274
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +1 -1
  6. package/dist/ac-text.d.ts +12 -7
  7. package/dist/ac-text.d.ts.map +1 -1
  8. package/dist/ac-text.js +2 -3
  9. package/dist/ac-text.js.map +1 -1
  10. package/dist/{button-C3fLMKUv.js → button-DxYee1ZH.js} +2 -2
  11. package/dist/{button-C3fLMKUv.js.map → button-DxYee1ZH.js.map} +1 -1
  12. package/dist/button.d.ts +1 -2
  13. package/dist/button.d.ts.map +1 -1
  14. package/dist/button.js +1 -2
  15. package/dist/checkbox.d.ts +0 -1
  16. package/dist/checkbox.d.ts.map +1 -1
  17. package/dist/checkbox.js +1 -1
  18. package/dist/checkbox.js.map +1 -1
  19. package/dist/{colour-picker-0sgAha1T.js → colour-picker-C3Ag4uh-.js} +15 -17
  20. package/dist/colour-picker-C3Ag4uh-.js.map +1 -0
  21. package/dist/colour-picker.d.ts +1 -1
  22. package/dist/colour-picker.js +1 -1
  23. package/dist/crumbs.d.ts +5 -2
  24. package/dist/crumbs.d.ts.map +1 -1
  25. package/dist/crumbs.js +45 -20
  26. package/dist/crumbs.js.map +1 -1
  27. package/dist/data-display.d.ts +1 -2
  28. package/dist/data-display.d.ts.map +1 -1
  29. package/dist/data-display.js +25 -23
  30. package/dist/data-display.js.map +1 -1
  31. package/dist/{data-provider-DgI909FP.js → data-provider-35TyWclX.js} +2 -2
  32. package/dist/data-provider-35TyWclX.js.map +1 -0
  33. package/dist/{decorate-DdjvVHS-.js → decorate-VRK8rslU.js} +2 -2
  34. package/dist/{decorate-DdjvVHS-.js.map → decorate-VRK8rslU.js.map} +1 -1
  35. package/dist/{defaults-C3b9OWJD.js → defaults-DcE8RQJc.js} +19 -1
  36. package/dist/defaults-DcE8RQJc.js.map +1 -0
  37. package/dist/editable-label.d.ts +1 -2
  38. package/dist/editable-label.d.ts.map +1 -1
  39. package/dist/editable-label.js +1 -1
  40. package/dist/editable-label.js.map +1 -1
  41. package/dist/{hex-editor-WZrjRtvL.js → hex-editor-Bk6rxhfg.js} +9 -11
  42. package/dist/hex-editor-Bk6rxhfg.js.map +1 -0
  43. package/dist/{hex-editor-CMIxtNgT.d.ts → hex-editor-Caq8vNQk.d.ts} +2 -3
  44. package/dist/hex-editor-Caq8vNQk.d.ts.map +1 -0
  45. package/dist/hex.d.ts +1 -1
  46. package/dist/hex.js +1 -1
  47. package/dist/highlight-B-eQrhhb.js.map +1 -1
  48. package/dist/{icon-Ndo40kNO.js → icon-B22XVlCt.js} +6 -4
  49. package/dist/{icon-Ndo40kNO.js.map → icon-B22XVlCt.js.map} +1 -1
  50. package/dist/{icon-Bd5BiU4b.d.ts → icon-DpA9x2Ve.d.ts} +2 -3
  51. package/dist/icon-DpA9x2Ve.d.ts.map +1 -0
  52. package/dist/icons.d.ts +2 -2
  53. package/dist/icons.js +2 -2
  54. package/dist/{incr-search-CeERfijK.js → incr-search-2pz21xtR.js} +9 -6
  55. package/dist/incr-search-2pz21xtR.js.map +1 -0
  56. package/dist/incr-search.d.ts +1 -1
  57. package/dist/incr-search.js +1 -1
  58. package/dist/{index-CowKi2mo.d.ts → index-BeuCXtiO.d.ts} +3 -4
  59. package/dist/index-BeuCXtiO.d.ts.map +1 -0
  60. package/dist/{index-D87kRcEs.d.ts → index-BtGFYfaK.d.ts} +2 -1
  61. package/dist/index-BtGFYfaK.d.ts.map +1 -0
  62. package/dist/{index-s_IhSnwc.d.ts → index-CoqtMSd9.d.ts} +6 -4
  63. package/dist/index-CoqtMSd9.d.ts.map +1 -0
  64. package/dist/{index-9fGVAg1d.d.ts → index-Cxwxtj6g.d.ts} +3 -3
  65. package/dist/index-Cxwxtj6g.d.ts.map +1 -0
  66. package/dist/index-D_ftz_IT.d.ts +184 -0
  67. package/dist/index-D_ftz_IT.d.ts.map +1 -0
  68. package/dist/{index-_RTQSKHi.d.ts → index-DfEkTGvT.d.ts} +11 -11
  69. package/dist/index-DfEkTGvT.d.ts.map +1 -0
  70. package/dist/index.d.ts +167 -29
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +712 -2970
  73. package/dist/index.js.map +1 -1
  74. package/dist/keyboard-B5xD7Fz-.js +36 -0
  75. package/dist/keyboard-B5xD7Fz-.js.map +1 -0
  76. package/dist/{labelled-input-base-ORb1rGq6.d.ts → labelled-input-base-DiTme1JO.d.ts} +1 -2
  77. package/dist/labelled-input-base-DiTme1JO.d.ts.map +1 -0
  78. package/dist/{labelled-input-base-9K_w3oLj.js → labelled-input-base-hoKGwyKT.js} +2 -2
  79. package/dist/{labelled-input-base-9K_w3oLj.js.map → labelled-input-base-hoKGwyKT.js.map} +1 -1
  80. package/dist/labelled-radial-input.d.ts +5 -4
  81. package/dist/labelled-radial-input.d.ts.map +1 -1
  82. package/dist/labelled-radial-input.js +7 -3
  83. package/dist/labelled-radial-input.js.map +1 -1
  84. package/dist/labelled-range-input.d.ts +1 -1
  85. package/dist/labelled-range-input.js +27 -3
  86. package/dist/labelled-range-input.js.map +1 -1
  87. package/dist/led.d.ts +1 -2
  88. package/dist/led.d.ts.map +1 -1
  89. package/dist/led.js +1 -1
  90. package/dist/led.js.map +1 -1
  91. package/dist/{menu-vjoiHZ13.js → menu-g6yd7ddN.js} +8 -9
  92. package/dist/menu-g6yd7ddN.js.map +1 -0
  93. package/dist/{menu-item-COx-Azgj.d.ts → menu-item-DSyf9f_L.d.ts} +4 -4
  94. package/dist/menu-item-DSyf9f_L.d.ts.map +1 -0
  95. package/dist/{menu-item-BrIuORYD.js → menu-item-NUKwJ4s5.js} +3 -4
  96. package/dist/{menu-item-BrIuORYD.js.map → menu-item-NUKwJ4s5.js.map} +1 -1
  97. package/dist/menu.d.ts +2 -2
  98. package/dist/menu.js +2 -2
  99. package/dist/miller.d.ts +8 -5
  100. package/dist/miller.d.ts.map +1 -1
  101. package/dist/miller.js +22 -24
  102. package/dist/miller.js.map +1 -1
  103. package/dist/narrowed-text.d.ts +7 -3
  104. package/dist/narrowed-text.d.ts.map +1 -1
  105. package/dist/narrowed-text.js +73 -47
  106. package/dist/narrowed-text.js.map +1 -1
  107. package/dist/panel.d.ts +1 -1
  108. package/dist/panel.d.ts.map +1 -1
  109. package/dist/panel.js +2 -2
  110. package/dist/panel.js.map +1 -1
  111. package/dist/plots.d.ts +2 -2
  112. package/dist/plots.js +1 -1
  113. package/dist/polar-pad.d.ts +15 -3
  114. package/dist/polar-pad.d.ts.map +1 -1
  115. package/dist/polar-pad.js +64 -11
  116. package/dist/polar-pad.js.map +1 -1
  117. package/dist/{radial-input-CpQPs1Mp.d.ts → radial-input-BhZeyyP-.d.ts} +6 -4
  118. package/dist/radial-input-BhZeyyP-.d.ts.map +1 -0
  119. package/dist/{radial-input-DZ7eQCcM.js → radial-input-DxCrt_NE.js} +41 -19
  120. package/dist/radial-input-DxCrt_NE.js.map +1 -0
  121. package/dist/radial-input.d.ts +1 -1
  122. package/dist/radial-input.js +1 -1
  123. package/dist/range-input.d.ts +15 -4
  124. package/dist/range-input.d.ts.map +1 -1
  125. package/dist/range-input.js +23 -2
  126. package/dist/range-input.js.map +1 -1
  127. package/dist/range.d.ts +15 -4
  128. package/dist/range.d.ts.map +1 -1
  129. package/dist/range.js +23 -2
  130. package/dist/range.js.map +1 -1
  131. package/dist/selecthorizontal.d.ts +1 -2
  132. package/dist/selecthorizontal.d.ts.map +1 -1
  133. package/dist/selecthorizontal.js +1 -1
  134. package/dist/selecthorizontal.js.map +1 -1
  135. package/dist/slider-input-DFpG23bS.js +1592 -0
  136. package/dist/slider-input-DFpG23bS.js.map +1 -0
  137. package/dist/slider-input-TidGhvXt.d.ts +160 -0
  138. package/dist/slider-input-TidGhvXt.d.ts.map +1 -0
  139. package/dist/slider-input.d.ts +2 -0
  140. package/dist/slider-input.js +2 -0
  141. package/dist/snap-container.d.ts +1 -2
  142. package/dist/snap-container.d.ts.map +1 -1
  143. package/dist/snap-container.js +6 -5
  144. package/dist/snap-container.js.map +1 -1
  145. package/dist/split-layout.d.ts +1 -2
  146. package/dist/split-layout.d.ts.map +1 -1
  147. package/dist/split-layout.js +6 -4
  148. package/dist/split-layout.js.map +1 -1
  149. package/dist/style.css +1 -1
  150. package/dist/swipe.d.ts +12 -2
  151. package/dist/swipe.d.ts.map +1 -1
  152. package/dist/swipe.js +22 -3
  153. package/dist/swipe.js.map +1 -1
  154. package/dist/{tab-list-DLzb-m-I.d.ts → tab-list-BohmbvqF.d.ts} +14 -5
  155. package/dist/tab-list-BohmbvqF.d.ts.map +1 -0
  156. package/dist/tab-list-DhkVhRQc.js +597 -0
  157. package/dist/tab-list-DhkVhRQc.js.map +1 -0
  158. package/dist/tabs.d.ts +2 -2
  159. package/dist/tabs.js +1 -208
  160. package/dist/{tickled-controller-BjYCzGrU.d.ts → tickled-controller-BF2Ea0DE.d.ts} +2 -31
  161. package/dist/tickled-controller-BF2Ea0DE.d.ts.map +1 -0
  162. package/dist/tickled-styles-fNDdqf6l.js.map +1 -1
  163. package/dist/{timeline-2OFtN4Ta.js → timeline-B2fgDL4U.js} +37 -1438
  164. package/dist/timeline-B2fgDL4U.js.map +1 -0
  165. package/dist/timeline.d.ts +1 -1
  166. package/dist/timeline.js +1 -1
  167. package/dist/{tooltip-RGVIwHrZ.d.ts → tooltip-BlvBUQxx.d.ts} +3 -3
  168. package/dist/tooltip-BlvBUQxx.d.ts.map +1 -0
  169. package/dist/{tree-D84RUgFC.js → tree-BnvzhsAw.js} +8 -9
  170. package/dist/tree-BnvzhsAw.js.map +1 -0
  171. package/dist/{tree-component-BaCSPWg1.d.ts → tree-component-DCmmKZ4j.d.ts} +8 -124
  172. package/dist/tree-component-DCmmKZ4j.d.ts.map +1 -0
  173. package/dist/tree.d.ts +10 -4
  174. package/dist/tree.d.ts.map +1 -1
  175. package/dist/tree.js +1 -1
  176. package/dist/types-DO3gt5vg.d.ts +130 -0
  177. package/dist/types-DO3gt5vg.d.ts.map +1 -0
  178. package/dist/wheel-nudge-BlSCe_hD.js +86 -0
  179. package/dist/wheel-nudge-BlSCe_hD.js.map +1 -0
  180. package/dist/wheel-nudge-DgMTRYlh.d.ts +29 -0
  181. package/dist/wheel-nudge-DgMTRYlh.d.ts.map +1 -0
  182. package/dist/{xy-axis-BeWRMmbo.d.ts → xy-axis-5KhVTNoX.d.ts} +30 -7
  183. package/dist/xy-axis-5KhVTNoX.d.ts.map +1 -0
  184. package/dist/xy-axis-CxHC8z5E.js +1849 -0
  185. package/dist/xy-axis-CxHC8z5E.js.map +1 -0
  186. package/dist/xy-pad.d.ts +13 -2
  187. package/dist/xy-pad.d.ts.map +1 -1
  188. package/dist/xy-pad.js +47 -6
  189. package/dist/xy-pad.js.map +1 -1
  190. package/docs-user/README.md +2 -0
  191. package/docs-user/button.md +48 -0
  192. package/docs-user/narrowed-text.md +33 -1
  193. package/docs-user/notifier.md +131 -0
  194. package/docs-user/plots.md +61 -1
  195. package/docs-user/radial-input.md +2 -2
  196. package/docs-user/slider-input.md +673 -0
  197. package/docs-user/tabs.md +12 -14
  198. package/docs-user/user-catalog.md +69 -4
  199. package/docs-user/vertical-list.md +75 -7
  200. package/package.json +23 -20
  201. package/dist/colour-C3MQIjFJ-vRCdr8W-.js +0 -3732
  202. package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +0 -1
  203. package/dist/colour-picker-0sgAha1T.js.map +0 -1
  204. package/dist/data-provider-DgI909FP.js.map +0 -1
  205. package/dist/defaults-C3b9OWJD.js.map +0 -1
  206. package/dist/dist-CA13FvMn.js +0 -980
  207. package/dist/dist-CA13FvMn.js.map +0 -1
  208. package/dist/hex-editor-CMIxtNgT.d.ts.map +0 -1
  209. package/dist/hex-editor-WZrjRtvL.js.map +0 -1
  210. package/dist/icon-Bd5BiU4b.d.ts.map +0 -1
  211. package/dist/incr-search-CeERfijK.js.map +0 -1
  212. package/dist/index-9fGVAg1d.d.ts.map +0 -1
  213. package/dist/index-CowKi2mo.d.ts.map +0 -1
  214. package/dist/index-D87kRcEs.d.ts.map +0 -1
  215. package/dist/index-DgXpCg_G.d.ts +0 -1244
  216. package/dist/index-DgXpCg_G.d.ts.map +0 -1
  217. package/dist/index-_RTQSKHi.d.ts.map +0 -1
  218. package/dist/index-s_IhSnwc.d.ts.map +0 -1
  219. package/dist/keyboard-CimiEskD.js +0 -20
  220. package/dist/keyboard-CimiEskD.js.map +0 -1
  221. package/dist/labelled-input-base-ORb1rGq6.d.ts.map +0 -1
  222. package/dist/menu-item-COx-Azgj.d.ts.map +0 -1
  223. package/dist/menu-vjoiHZ13.js.map +0 -1
  224. package/dist/radial-input-CpQPs1Mp.d.ts.map +0 -1
  225. package/dist/radial-input-DZ7eQCcM.js.map +0 -1
  226. package/dist/tab-list-DLzb-m-I.d.ts.map +0 -1
  227. package/dist/tabs.js.map +0 -1
  228. package/dist/tickled-controller-BjYCzGrU.d.ts.map +0 -1
  229. package/dist/timeline-2OFtN4Ta.js.map +0 -1
  230. package/dist/tooltip-RGVIwHrZ.d.ts.map +0 -1
  231. package/dist/tree-D84RUgFC.js.map +0 -1
  232. package/dist/tree-component-BaCSPWg1.d.ts.map +0 -1
  233. package/dist/types-DSfbtHs3.d.ts +0 -8
  234. package/dist/types-DSfbtHs3.d.ts.map +0 -1
  235. package/dist/xy-axis-BeWRMmbo.d.ts.map +0 -1
  236. package/dist/xy-axis-no0OL_O1.js +0 -7893
  237. package/dist/xy-axis-no0OL_O1.js.map +0 -1
  238. /package/bundle/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
  239. /package/dist/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
@@ -0,0 +1,1849 @@
1
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
2
+ import { t as getIcon } from "./registry-CkW7q09Q.js";
3
+ import "./icons.js";
4
+ import { LitElement, css, html, nothing, svg } from "lit";
5
+ import { property, state } from "lit/decorators.js";
6
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
7
+ import { Colour } from "@ixfx/visual";
8
+ import { clamp, rangeStream } from "@ixfx/numbers";
9
+ import { repeat } from "lit/directives/repeat.js";
10
+ import { Easings } from "@ixfx/modulation";
11
+ import { classMap } from "lit/directives/class-map.js";
12
+ //#region src/plots/colour-map.ts
13
+ const DEFAULT_BASE = `oklch(60% 0.15 140)`;
14
+ const DEFAULT_PALETTE = [
15
+ `green`,
16
+ `yellow`,
17
+ `red`
18
+ ];
19
+ const _scaleCache = /* @__PURE__ */ new Map();
20
+ function getScaleSteps(palette) {
21
+ const key = palette.join(`\x00`);
22
+ if (!_scaleCache.has(key)) _scaleCache.set(key, Colour.scale(palette, { stepsBetween: 99 }));
23
+ return _scaleCache.get(key);
24
+ }
25
+ /** Resolve a CSS custom property to its final value, handling var() references. */
26
+ function resolveCssVariable(el, prop) {
27
+ const style = getComputedStyle(el);
28
+ let value = style.getPropertyValue(prop).trim();
29
+ const varRegex = /var\((--[\w-]+)(?:,\s*(.*))?\)/g;
30
+ for (let i = 0; i < 5 && value.includes(`var(`); i++) value = value.replace(varRegex, (_, varName, fallback) => {
31
+ const resolved = style.getPropertyValue(varName).trim();
32
+ if (resolved) return resolved;
33
+ if (fallback) return fallback.trim();
34
+ return ``;
35
+ });
36
+ return value;
37
+ }
38
+ /** Read the resolved --plot-color CSS variable from an element. */
39
+ function readBaseColour(el) {
40
+ return resolveCssVariable(el, `--plot-color`) || DEFAULT_BASE;
41
+ }
42
+ /** Parse a comma-separated colour string attribute into an array. Falls back to default palette. */
43
+ function parseColourScaleAttr(attr) {
44
+ if (!attr.trim()) return DEFAULT_PALETTE;
45
+ const parsed = attr.split(`,`).map((s) => s.trim()).filter(Boolean);
46
+ return parsed.length >= 2 ? parsed : DEFAULT_PALETTE;
47
+ }
48
+ /** Parse any CSS colour string to HSL scalars (h, s, l all 0..1). */
49
+ function toHslScalar(css) {
50
+ try {
51
+ const hsl = Colour.HslSpace.fromCss(css, { scalar: true });
52
+ return {
53
+ h: hsl.h,
54
+ s: hsl.s,
55
+ l: hsl.l
56
+ };
57
+ } catch {
58
+ return {
59
+ h: .39,
60
+ s: .5,
61
+ l: .55
62
+ };
63
+ }
64
+ }
65
+ /** Apply easing or custom mapping to a 0..1 value. Returns clamped 0..1. */
66
+ function applyMapping(v, config) {
67
+ const clamped = Math.max(0, Math.min(1, v));
68
+ if (config.easingName) try {
69
+ return Math.max(0, Math.min(1, Easings.get(config.easingName)(clamped)));
70
+ } catch {}
71
+ if (config.valueMap) return Math.max(0, Math.min(1, config.valueMap(clamped)));
72
+ return clamped;
73
+ }
74
+ /**
75
+ * Compute the fill colour and opacity for a single data point.
76
+ * @param scaledValue Normalised value in 0..1 (data range for single-axis; age fraction for xy).
77
+ * @param baseColour Resolved base colour string (from --plot-color).
78
+ * @param config Colour map configuration.
79
+ */
80
+ function computeColour(scaledValue, baseColour, config) {
81
+ const mapped = applyMapping(scaledValue, config);
82
+ switch (config.mode) {
83
+ case `fixed`: return {
84
+ fill: baseColour,
85
+ opacity: 1
86
+ };
87
+ case `opacity`: return {
88
+ fill: baseColour,
89
+ opacity: Math.max(.04, mapped)
90
+ };
91
+ case `hue`: {
92
+ const hsl = toHslScalar(baseColour);
93
+ return {
94
+ fill: `hsl(${Math.round(mapped * 360)}deg ${Math.round(hsl.s * 100)}% ${Math.round(hsl.l * 100)}%)`,
95
+ opacity: 1
96
+ };
97
+ }
98
+ case `saturation`: {
99
+ const hsl = toHslScalar(baseColour);
100
+ return {
101
+ fill: `hsl(${Math.round(hsl.h * 360)}deg ${Math.round(mapped * 100)}% ${Math.round(hsl.l * 100)}%)`,
102
+ opacity: 1
103
+ };
104
+ }
105
+ case `lightness`: {
106
+ const hsl = toHslScalar(baseColour);
107
+ return {
108
+ fill: `hsl(${Math.round(hsl.h * 360)}deg ${Math.round(hsl.s * 100)}% ${Math.round(mapped * 100)}%)`,
109
+ opacity: 1
110
+ };
111
+ }
112
+ case `scale`: {
113
+ const steps = getScaleSteps(config.palette.length >= 2 ? config.palette : DEFAULT_PALETTE);
114
+ return {
115
+ fill: steps[Math.min(steps.length - 1, Math.floor(mapped * steps.length))],
116
+ opacity: 1
117
+ };
118
+ }
119
+ default: return {
120
+ fill: baseColour,
121
+ opacity: 1
122
+ };
123
+ }
124
+ }
125
+ /**
126
+ * Apply xy-axis age-style modification on top of a base ColourResult.
127
+ * 'size' is not handled here — the caller adjusts the radius separately.
128
+ */
129
+ function applyAgeModification(base, ageFraction, ageStyle) {
130
+ if (ageStyle === `opacity`) return {
131
+ ...base,
132
+ opacity: base.opacity * Math.max(.08, ageFraction ** .6)
133
+ };
134
+ if (ageStyle === `desaturation`) {
135
+ const hsl = toHslScalar(base.fill);
136
+ const sFaded = hsl.s * ageFraction;
137
+ return {
138
+ fill: `hsl(${Math.round(hsl.h * 360)}deg ${Math.round(sFaded * 100)}% ${Math.round(hsl.l * 100)}%)`,
139
+ opacity: base.opacity
140
+ };
141
+ }
142
+ return base;
143
+ }
144
+ //#endregion
145
+ //#region src/plots/data.ts
146
+ /**
147
+ * A data series
148
+ */
149
+ var PlotDataSeries = class {
150
+ #rangeStream;
151
+ #rangeAuto;
152
+ #rangeManual;
153
+ #options;
154
+ /**
155
+ * Constructor.
156
+ *
157
+ * Default options:
158
+ * - `capacityLimit`: 100
159
+ * - `persistentScaling`: true
160
+ * - `fixedRange`: null
161
+ * @param options
162
+ */
163
+ constructor(options = {}) {
164
+ this.values = [];
165
+ this.#rangeStream = rangeStream();
166
+ this.#rangeAuto = {
167
+ min: 0,
168
+ max: 0
169
+ };
170
+ this.#rangeManual = null;
171
+ this.#options = {
172
+ capacityLimit: 100,
173
+ persistentScaling: true,
174
+ fixedRange: null,
175
+ ...options
176
+ };
177
+ if (this.#options.fixedRange !== null) this.#rangeManual = this.#options.fixedRange;
178
+ }
179
+ /**
180
+ * Sets a manual range, this disabled auto-scaling.
181
+ * Use {@link unsetRange} to re-enable auto-scaling.
182
+ * @param min
183
+ * @param max
184
+ */
185
+ setRange(min, max) {
186
+ this.#rangeManual = {
187
+ min,
188
+ max
189
+ };
190
+ }
191
+ /**
192
+ * Disable the manual range
193
+ */
194
+ unsetRange() {
195
+ this.#rangeManual = null;
196
+ this.#recompute();
197
+ }
198
+ get isAutoRange() {
199
+ return this.#rangeManual === null;
200
+ }
201
+ /**
202
+ * Adds a single value
203
+ * @param valueOrValues Value to add
204
+ */
205
+ add(valueOrValues) {
206
+ if (typeof valueOrValues === `number`) {
207
+ this.values.push(valueOrValues);
208
+ if (this.isAutoRange) this.#rangeAuto = this.#rangeStream.seen(valueOrValues);
209
+ } else {
210
+ this.values.push(...valueOrValues);
211
+ if (this.isAutoRange) for (const v of valueOrValues) this.#rangeAuto = this.#rangeStream.seen(v);
212
+ }
213
+ if (this.values.length > this.#options.capacityLimit) {
214
+ this.values = this.values.slice(this.values.length - this.#options.capacityLimit);
215
+ if (!this.#options.persistentScaling && this.isAutoRange) this.#recompute();
216
+ }
217
+ }
218
+ #recompute() {
219
+ if (!this.#options.persistentScaling) this.#rangeStream = rangeStream();
220
+ for (let i = 0; i < this.values.length; i++) this.#rangeAuto = this.#rangeStream.seen(this.values[i]);
221
+ }
222
+ /**
223
+ * Sets the entire data series at once, replacing any existing data. If the number of values exceeds the `capacityLimit`, only the most recent values up to the limit are kept.
224
+ * @param values
225
+ */
226
+ set(values) {
227
+ if (values.length > this.#options.capacityLimit) values = values.slice(values.length - this.#options.capacityLimit);
228
+ this.values = values;
229
+ this.#recompute();
230
+ }
231
+ /**
232
+ * Returns the manual range if this is in effect, or the automatic
233
+ */
234
+ get range() {
235
+ if (this.#rangeManual) return this.#rangeManual;
236
+ return this.#rangeAuto;
237
+ }
238
+ /**
239
+ * Clear all data and reset range. If `persistentScaling` is _false_, also resets scaling to initial state.
240
+ * @returns _true_ if there was data to clear
241
+ */
242
+ clear() {
243
+ const empty = this.values.length === 0;
244
+ this.values = [];
245
+ if (!this.#options.persistentScaling) this.#recompute();
246
+ return !empty;
247
+ }
248
+ };
249
+ //#endregion
250
+ //#region src/util/is-numeric.ts
251
+ function isNumeric(str) {
252
+ if (typeof str === `number`) return true;
253
+ if (typeof str !== `string`) return false;
254
+ const trimmed = str.trim();
255
+ return trimmed !== `` && !Number.isNaN(Number(trimmed));
256
+ }
257
+ //#endregion
258
+ //#region src/util/tooltip.ts
259
+ var _TooltipElement;
260
+ let _baseStylesInjected = false;
261
+ function ensureBaseTooltipStyles() {
262
+ if (_baseStylesInjected) return;
263
+ _baseStylesInjected = true;
264
+ const style = document.createElement(`style`);
265
+ style.textContent = `
266
+ ixfx-tooltip {
267
+ display: inline-block;
268
+ background: var(--surface-5);
269
+ color: var(--surface-5-text);
270
+ border: var(--border);
271
+ border-radius: var(--radius-l, 6px);
272
+ box-shadow: var(--shadow-m);
273
+ padding: var(--space-s, 4px);
274
+ font-size: 10px;
275
+ font-family: var(--font-family, inherit);
276
+ }
277
+ ixfx-tooltip .tooltip-type-number {
278
+ font-family: var(--font-mono, ui-monospace, monospace);
279
+ }
280
+ `;
281
+ document.head.appendChild(style);
282
+ }
283
+ let TooltipElement = class TooltipElement extends LitElement {
284
+ static {
285
+ _TooltipElement = this;
286
+ }
287
+ constructor(..._args) {
288
+ super(..._args);
289
+ this.valueFormatTooltip = _TooltipElement.defaultFormatter;
290
+ this.orientation = `top`;
291
+ this.anchor = `--tooltip`;
292
+ }
293
+ connectedCallback() {
294
+ super.connectedCallback();
295
+ ensureBaseTooltipStyles();
296
+ }
297
+ static {
298
+ this.numberFormatter = (v) => Math.floor(v).toString();
299
+ }
300
+ static {
301
+ this.defaultFormatter = (v) => {
302
+ if (typeof v === `number`) {
303
+ if (Number.isInteger(v)) return v.toString();
304
+ return v.toFixed(2);
305
+ } else if (typeof v === `string`) return v;
306
+ else return JSON.stringify(v);
307
+ };
308
+ }
309
+ #renderIcon() {
310
+ if (!this.icon) return nothing;
311
+ if (this.icon.startsWith(`<svg`)) return html`<span class="tooltip-icon" style="display:inline-flex;align-items:center;margin-right:0.25em">${unsafeHTML(this.icon)}</span>`;
312
+ if (!getIcon(this.icon)) return nothing;
313
+ return html`<ixfx-icon class="tooltip-icon" style="margin-right:0.25em" name="${this.icon}"></ixfx-icon>`;
314
+ }
315
+ render() {
316
+ const classes = {
317
+ "tooltip": true,
318
+ "tooltip-orientation-top": this.orientation === `top`,
319
+ "tooltip-orientation-bottom": this.orientation === `bottom`,
320
+ "tooltip-orientation-right": this.orientation === `right`,
321
+ "tooltip-orientation-left": this.orientation === `left`,
322
+ "tooltip-type-text": true,
323
+ "tooltip-type-number": false,
324
+ "tooltip-style-bubble": true
325
+ };
326
+ let value = ``;
327
+ switch (typeof this.value) {
328
+ case `number`:
329
+ classes[`tooltip-type-number`] = true;
330
+ value = this.valueFormatTooltip(this.value);
331
+ break;
332
+ case `string`:
333
+ if (isNumeric(this.value)) {
334
+ classes[`tooltip-type-number`] = true;
335
+ value = this.valueFormatTooltip(Number.parseFloat(this.value));
336
+ } else value = this.value;
337
+ break;
338
+ default: value = JSON.stringify(this.value);
339
+ }
340
+ return html`<output style="--anchor-id: ${this.anchor}" class=${classMap(classes)}>${this.#renderIcon()}${value}</output>`;
341
+ }
342
+ createRenderRoot() {
343
+ return this;
344
+ }
345
+ };
346
+ __decorate([property({ type: String })], TooltipElement.prototype, "orientation", void 0);
347
+ __decorate([property({ type: String })], TooltipElement.prototype, "anchor", void 0);
348
+ __decorate([property()], TooltipElement.prototype, "value", void 0);
349
+ __decorate([property({ type: String })], TooltipElement.prototype, "icon", void 0);
350
+ TooltipElement = _TooltipElement = __decorate([safeCustomElement(`ixfx-tooltip`)], TooltipElement);
351
+ //#endregion
352
+ //#region src/plots/svg-utils.ts
353
+ let _tooltipEl = null;
354
+ let _tooltipOwner = null;
355
+ function ensureTooltip() {
356
+ if (_tooltipEl) return _tooltipEl;
357
+ const el = document.createElement(`div`);
358
+ el.style.cssText = `
359
+ position: fixed;
360
+ pointer-events: none;
361
+ z-index: 9999;
362
+ background: var(--surface-5);
363
+ color: var(--surface-5-text);
364
+ border: var(--border);
365
+ border-radius: var(--radius-l, 6px);
366
+ box-shadow: var(--shadow-m);
367
+ padding: 2px 6px;
368
+ font-size: 10px;
369
+ font-family: ui-monospace, monospace;
370
+ opacity: 0;
371
+ transition: opacity 0.1s ease;
372
+ `;
373
+ document.body.appendChild(el);
374
+ _tooltipEl = el;
375
+ return el;
376
+ }
377
+ function showTooltip(owner, clientX, clientY, text) {
378
+ const el = ensureTooltip();
379
+ _tooltipOwner = owner;
380
+ el.textContent = text;
381
+ el.style.whiteSpace = `nowrap`;
382
+ el.style.left = `${clientX + 14}px`;
383
+ el.style.top = `${clientY + 14}px`;
384
+ el.style.opacity = `1`;
385
+ }
386
+ function showTooltipHTML(owner, clientX, clientY, html) {
387
+ const el = ensureTooltip();
388
+ _tooltipOwner = owner;
389
+ el.innerHTML = html;
390
+ el.style.whiteSpace = `normal`;
391
+ el.style.left = `${clientX + 14}px`;
392
+ el.style.top = `${clientY + 14}px`;
393
+ el.style.opacity = `1`;
394
+ }
395
+ function hideTooltip(owner) {
396
+ if (_tooltipOwner !== owner) return;
397
+ const el = ensureTooltip();
398
+ el.style.opacity = `0`;
399
+ _tooltipOwner = null;
400
+ }
401
+ let _bubbleEl = null;
402
+ let _bubbleOwner = null;
403
+ function ensureBubble() {
404
+ if (_bubbleEl) return _bubbleEl;
405
+ const el = document.createElement(`ixfx-tooltip`);
406
+ el.style.cssText = `
407
+ position: fixed;
408
+ z-index: 9999;
409
+ pointer-events: none;
410
+ opacity: 0;
411
+ transition: opacity 0.12s ease;
412
+ `;
413
+ document.body.appendChild(el);
414
+ _bubbleEl = el;
415
+ return el;
416
+ }
417
+ /**
418
+ * Shows/repositions an `ixfx-tooltip` bubble.
419
+ * `mainClient` centers the bubble along the given axis (e.g. a bar's midpoint);
420
+ * `crossClient` places it along the other axis (e.g. an offset from the cursor).
421
+ */
422
+ function showBubbleTooltip(owner, text, mainClient, crossClient, orientation) {
423
+ const el = ensureBubble();
424
+ _bubbleOwner = owner;
425
+ el.value = text;
426
+ if (orientation === `horizontal`) {
427
+ el.style.left = `${mainClient}px`;
428
+ el.style.top = `${crossClient}px`;
429
+ el.style.transform = `translate(-50%, -100%)`;
430
+ } else {
431
+ el.style.left = `${crossClient}px`;
432
+ el.style.top = `${mainClient}px`;
433
+ el.style.transform = `translate(0, -50%)`;
434
+ }
435
+ el.style.opacity = `1`;
436
+ }
437
+ function hideBubbleTooltip(owner) {
438
+ if (_bubbleOwner !== owner) return;
439
+ const el = ensureBubble();
440
+ el.style.opacity = `0`;
441
+ _bubbleOwner = null;
442
+ }
443
+ /** Stroke width proportional to the smaller dimension of the viewport. */
444
+ function adaptiveStroke(width, height) {
445
+ return Math.max(1, Math.min(width, height) / 25);
446
+ }
447
+ /** Dot radius based on available space per data point and viewport height. */
448
+ function adaptiveRadius(width, height, dataLength) {
449
+ if (dataLength === 0) return 3;
450
+ const widthPerPoint = width / dataLength;
451
+ return Math.max(1.5, Math.min(widthPerPoint * .45, height * .2, 8));
452
+ }
453
+ /** Normalize a value from [min, max] to [0, 1]. Returns 0.5 if range is zero. */
454
+ function scaleValue(v, min, max) {
455
+ const range = max - min;
456
+ return range === 0 ? .5 : (v - min) / range;
457
+ }
458
+ //#endregion
459
+ //#region src/plots/multi-axis.ts
460
+ const PLOT_PALETTE = [
461
+ `oklch(60% 0.15 30)`,
462
+ `oklch(60% 0.15 90)`,
463
+ `oklch(60% 0.15 150)`,
464
+ `oklch(60% 0.15 210)`,
465
+ `oklch(60% 0.15 270)`,
466
+ `oklch(60% 0.15 330)`,
467
+ `oklch(60% 0.15 60)`,
468
+ `oklch(60% 0.15 180)`
469
+ ];
470
+ let PlotMultiAxis = class PlotMultiAxis extends LitElement {
471
+ constructor(..._args) {
472
+ super(..._args);
473
+ this.layout = `overlap`;
474
+ this.gridMinWidth = 200;
475
+ this.valueFormat = (v) => v ? v.toFixed(2) : ``;
476
+ this.#instances = /* @__PURE__ */ new Map();
477
+ this.#seriesOrder = [];
478
+ this.#seriesSet = /* @__PURE__ */ new Set();
479
+ this.#pendingStyles = /* @__PURE__ */ new Map();
480
+ }
481
+ #instances;
482
+ #seriesOrder;
483
+ #seriesSet;
484
+ #pendingStyles;
485
+ /**
486
+ * Adds a single data point
487
+ * @param name Series name
488
+ * @param value Value
489
+ * @param witholdRedraw If _false_ (default) plot is automatically redrawn
490
+ */
491
+ addData(name, value, witholdRedraw = false) {
492
+ return this.#addToSeries(name, value, witholdRedraw);
493
+ }
494
+ setData(name, values, witholdRedraw = false) {
495
+ const [el, created] = this.#ensurePlot(name);
496
+ if (!this.#seriesSet.has(name)) {
497
+ this.#seriesSet.add(name);
498
+ this.#seriesOrder.push(name);
499
+ }
500
+ el.set(values, witholdRedraw);
501
+ if (created) this.requestUpdate();
502
+ return el;
503
+ }
504
+ setObjects(data, witholdRedraw = false) {
505
+ const entries = Object.entries(data);
506
+ for (const [name, vals] of entries) this.setData(name, vals, true);
507
+ if (witholdRedraw) return;
508
+ this.redraw();
509
+ }
510
+ /**
511
+ * Calls redraw() on all subplots.
512
+ */
513
+ redraw() {
514
+ for (const el of this.#instances.values()) el.redraw();
515
+ }
516
+ /**
517
+ * Adds a record of data points to the plot. Each key in the record corresponds to a series name, and its value is added as a data point to that series. If a series does not exist for a given key, it will be created.
518
+ * @param data Data to add
519
+ * @param witholdRedraw If _false_ (default) plot is automatically redrawn after adding the data. If _true_, plot is not redrawn automatically, and you need to call `redraw()` manually to update the plot after adding the data.
520
+ */
521
+ addObject(data, witholdRedraw = false) {
522
+ const entries = Object.entries(data);
523
+ for (const [name, val] of entries) this.#addToSeries(name, val, true);
524
+ if (witholdRedraw) return;
525
+ this.redraw();
526
+ }
527
+ /**
528
+ * Styles a series by setting CSS variables on the subplot.
529
+ * @param name Name of series
530
+ * @param styles Styles to apply
531
+ */
532
+ styleSeries(name, styles) {
533
+ const normalised = {};
534
+ for (const [key, val] of Object.entries(styles)) normalised[key.startsWith(`--`) ? key : `--${key}`] = val;
535
+ const el = this.#instances.get(name);
536
+ if (!el) {
537
+ this.#pendingStyles.set(name, normalised);
538
+ return;
539
+ }
540
+ for (const [key, val] of Object.entries(normalised)) el.style.setProperty(key, val);
541
+ }
542
+ /**
543
+ * Clears data from all sub-plots
544
+ * @returns _True_ if there was data to clear
545
+ */
546
+ clear() {
547
+ let cleared = false;
548
+ for (const el of this.#instances.values()) cleared = cleared || el.clear();
549
+ return cleared;
550
+ }
551
+ /**
552
+ * Clears data from a given series
553
+ * @param name Series name
554
+ * @returns _True_ if series was found and had data
555
+ */
556
+ clearSeries(name) {
557
+ const el = this.#instances.get(name);
558
+ if (el) {
559
+ el.clear();
560
+ return true;
561
+ }
562
+ return false;
563
+ }
564
+ /**
565
+ * Returns a copy of the series names
566
+ */
567
+ get series() {
568
+ return [...this.#seriesOrder];
569
+ }
570
+ #addToSeries(name, valueOrValues, withholdRedraw = false) {
571
+ const [el, created] = this.#ensurePlot(name);
572
+ if (!this.#seriesSet.has(name)) {
573
+ this.#seriesSet.add(name);
574
+ this.#seriesOrder.push(name);
575
+ }
576
+ el.add(valueOrValues, withholdRedraw);
577
+ if (created) this.requestUpdate();
578
+ return el;
579
+ }
580
+ #ensurePlot(name) {
581
+ let el = this.#instances.get(name);
582
+ if (el) return [el, false];
583
+ el = document.createElement(`ixfx-plot-single-axis`);
584
+ el.tooltips = false;
585
+ this.#instances.set(name, el);
586
+ const colorIndex = this.#instances.size % PLOT_PALETTE.length;
587
+ el.style.setProperty(`--plot-color`, PLOT_PALETTE[colorIndex]);
588
+ const pending = this.#pendingStyles.get(name);
589
+ if (pending) {
590
+ for (const [key, val] of Object.entries(pending)) el.style.setProperty(key, val);
591
+ this.#pendingStyles.delete(name);
592
+ }
593
+ el.addEventListener(`plot-datapoint-click`, (e) => {
594
+ const ce = e;
595
+ this.dispatchEvent(new CustomEvent(`plot-datapoint-click`, {
596
+ detail: {
597
+ ...ce.detail,
598
+ series: name
599
+ },
600
+ bubbles: true,
601
+ composed: true
602
+ }));
603
+ });
604
+ el.addEventListener(`plot-pointer-move`, (e) => {
605
+ const ce = e;
606
+ this.#onSubPlotPointerMove(ce.detail.clientX, ce.detail.clientY, ce.detail.index);
607
+ });
608
+ el.addEventListener(`plot-pointer-leave`, () => {
609
+ this.#onSubPlotPointerLeave();
610
+ });
611
+ return [el, true];
612
+ }
613
+ #onSubPlotPointerMove(clientX, clientY, index) {
614
+ if (index < 0) {
615
+ hideTooltip(this);
616
+ return;
617
+ }
618
+ const parts = [];
619
+ for (const [name, inst] of this.#instances) {
620
+ const val = inst.getValueAt(index);
621
+ if (val === void 0) continue;
622
+ const color = getComputedStyle(inst).getPropertyValue(`--plot-color`).trim();
623
+ parts.push(`<span style="color:${color}">${this.#escapeHTML(name)}</span> ${this.valueFormat(val)}`);
624
+ }
625
+ if (parts.length === 0) {
626
+ hideTooltip(this);
627
+ return;
628
+ }
629
+ showTooltipHTML(this, clientX, clientY, parts.join(`<br>`));
630
+ }
631
+ #onSubPlotPointerLeave() {
632
+ hideTooltip(this);
633
+ }
634
+ #escapeHTML(s) {
635
+ return s.replace(/&/g, `&amp;`).replace(/</g, `&lt;`).replace(/>/g, `&gt;`).replace(/"/g, `&quot;`);
636
+ }
637
+ render() {
638
+ const gridStyle = this.layout === `grid` ? `--grid-min-width: ${this.gridMinWidth}px` : ``;
639
+ return html`
640
+ <div
641
+ id="container"
642
+ class="layout layout-${this.layout}"
643
+ part="container"
644
+ style=${gridStyle}
645
+ >
646
+ ${repeat(this.#seriesOrder, (name) => name, (name) => this.#ensurePlot(name)[0])}
647
+ </div>
648
+ `;
649
+ }
650
+ static {
651
+ this.styles = css`
652
+ :host {
653
+ display: block;
654
+ width: 100%;
655
+ height: 100%;
656
+ min-height: 40px;
657
+ --plot-color: var(--accent);
658
+ }
659
+
660
+ #container {
661
+ width: 100%;
662
+ height: 100%;
663
+ }
664
+
665
+ #container.layout-overlap {
666
+ display: grid;
667
+ }
668
+ #container.layout-overlap > * {
669
+ grid-area: 1 / 1;
670
+ background: transparent;
671
+ }
672
+
673
+ #container.layout-horizontal {
674
+ display: flex;
675
+ flex-direction: row;
676
+ gap: var(--space-s, 4px);
677
+ }
678
+ #container.layout-horizontal > * {
679
+ flex: 1;
680
+ min-width: 0;
681
+ }
682
+
683
+ #container.layout-vertical {
684
+ display: flex;
685
+ flex-direction: column;
686
+ gap: var(--space-s, 4px);
687
+ }
688
+ #container.layout-vertical > * {
689
+ flex: 1;
690
+ min-height: 0;
691
+ }
692
+
693
+ #container.layout-grid {
694
+ display: grid;
695
+ grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-width, 200px), 1fr));
696
+ gap: var(--space-s, 4px);
697
+ }
698
+ `;
699
+ }
700
+ };
701
+ __decorate([property()], PlotMultiAxis.prototype, "layout", void 0);
702
+ __decorate([property({
703
+ attribute: `grid-min-width`,
704
+ type: Number
705
+ })], PlotMultiAxis.prototype, "gridMinWidth", void 0);
706
+ PlotMultiAxis = __decorate([safeCustomElement(`ixfx-plot-multi-axis`)], PlotMultiAxis);
707
+ //#endregion
708
+ //#region src/plots/plot-histogram.ts
709
+ let PlotHistogram = class PlotHistogram extends LitElement {
710
+ constructor(..._args) {
711
+ super(..._args);
712
+ this.orientation = `horizontal`;
713
+ this.resolution = .1;
714
+ this.capacity = 100;
715
+ this.title = ``;
716
+ this.paused = false;
717
+ this.valueTooltips = `hover`;
718
+ this.tooltipDelay = 400;
719
+ this.valueUpdateInterval = 500;
720
+ this.valueFormat = (valueMin, valueMax, frequency) => `${valueMin.toFixed(2)} - ${valueMax.toFixed(2)}: ${frequency}`;
721
+ this._width = 100;
722
+ this._height = 40;
723
+ this._hoveredIndex = null;
724
+ this._inlineOverlayText = null;
725
+ this.#series = new PlotDataSeries({
726
+ capacityLimit: 100,
727
+ persistentScaling: true
728
+ });
729
+ this.#hasProgrammaticData = false;
730
+ this.#tooltipIndex = null;
731
+ this.#tooltipVisible = false;
732
+ this.#tooltipShowTimer = null;
733
+ this.#tooltipHideTimer = null;
734
+ this.#tooltipPosition = null;
735
+ this.#valueRefreshTimer = null;
736
+ this.#onPointerMove = (e) => {
737
+ if (this.valueTooltips === `none`) return;
738
+ const index = this.#bucketIndexFromPointer(e.clientX, e.clientY);
739
+ const bucketIndex = index >= 0 ? index : null;
740
+ if (this.valueTooltips === `inline`) this.#setInlineIndex(bucketIndex);
741
+ else this.#updateBubbleTooltip(bucketIndex, e.clientX, e.clientY);
742
+ };
743
+ this.#onPointerLeave = () => {
744
+ if (this.valueTooltips === `none`) return;
745
+ if (this.valueTooltips === `inline`) this.#setInlineIndex(null);
746
+ else this.#updateBubbleTooltip(null, 0, 0);
747
+ };
748
+ }
749
+ #series;
750
+ #hasProgrammaticData;
751
+ #parseDeclarativeData(text) {
752
+ if (!text) return [];
753
+ return text.trim().split(/[,\s;]+/).map((t) => Number.parseFloat(t)).filter((n) => !Number.isNaN(n));
754
+ }
755
+ add(v) {
756
+ this.#hasProgrammaticData = true;
757
+ this.#series.add(v);
758
+ if (this.paused) return;
759
+ this.requestUpdate();
760
+ }
761
+ set(values) {
762
+ this.#hasProgrammaticData = true;
763
+ this.#series.set(values);
764
+ if (this.paused) return;
765
+ this.requestUpdate();
766
+ }
767
+ clear() {
768
+ this.#series.clear();
769
+ this.requestUpdate();
770
+ }
771
+ firstUpdated(_changed) {
772
+ this._width = this.clientWidth || 100;
773
+ this._height = this.clientHeight || 40;
774
+ new ResizeObserver((entries) => {
775
+ const e = entries[0];
776
+ this._width = e.contentRect.width;
777
+ this._height = e.contentRect.height;
778
+ }).observe(this);
779
+ if (!this.#hasProgrammaticData) {
780
+ const data = this.#parseDeclarativeData(this.textContent ?? ``);
781
+ if (data.length > 0) this.#series.set(data);
782
+ }
783
+ const declaredTitle = this.getAttribute(`title`);
784
+ if (declaredTitle !== null) {
785
+ this.title = declaredTitle;
786
+ this.removeAttribute(`title`);
787
+ }
788
+ }
789
+ updated(changed) {
790
+ if (changed.has(`capacity`)) {
791
+ const old = [...this.#series.values];
792
+ this.#series = new PlotDataSeries({
793
+ capacityLimit: this.capacity,
794
+ persistentScaling: true
795
+ });
796
+ if (old.length > 0) this.#series.set(old);
797
+ }
798
+ if (changed.has(`valueTooltips`)) this.#resetHoverState();
799
+ }
800
+ disconnectedCallback() {
801
+ super.disconnectedCallback();
802
+ this.#resetHoverState();
803
+ }
804
+ #readCssVar(name) {
805
+ return getComputedStyle(this).getPropertyValue(name).trim();
806
+ }
807
+ #getGapPx() {
808
+ const val = this.#readCssVar(`--plot-histogram-gap`);
809
+ if (val === ``) return 1;
810
+ const parsed = Number.parseFloat(val);
811
+ return Number.isFinite(parsed) ? Math.max(0, parsed) : 1;
812
+ }
813
+ #getBarLengthOverride() {
814
+ const val = this.#readCssVar(`--plot-histogram-bar-length`);
815
+ if (val === `` || val === `auto`) return null;
816
+ const parsed = Number.parseFloat(val);
817
+ return Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : null;
818
+ }
819
+ #emitClick(i, valueMin, valueMax, relativeValue) {
820
+ this.dispatchEvent(new CustomEvent(`plot-bucket-click`, {
821
+ detail: {
822
+ bucketIndex: i,
823
+ valueMin,
824
+ valueMax,
825
+ relativeValue
826
+ },
827
+ bubbles: true,
828
+ composed: true
829
+ }));
830
+ }
831
+ /** Bucket layout + counts, shared by render() and pointer hit-testing. */
832
+ #computeGeometry() {
833
+ const { values, range } = this.#series;
834
+ const w = this._width;
835
+ const h = this._height;
836
+ const isHorizontal = this.orientation === `horizontal`;
837
+ const padding = Math.min(4, (isHorizontal ? h : w) * .05);
838
+ const dataMin = this.min ?? range.min;
839
+ const dataMax = this.max ?? range.max;
840
+ const dataSpan = dataMax - dataMin || 1;
841
+ const bucketCount = Math.max(1, Math.floor(1 / this.resolution));
842
+ const availableMain = (isHorizontal ? w : h) - padding * 2;
843
+ const availableCross = (isHorizontal ? h : w) - padding * 2;
844
+ const buckets = Array.from({ length: bucketCount }).fill(0);
845
+ for (const v of values) {
846
+ const normalized = (v - dataMin) / dataSpan;
847
+ const index = Math.min(bucketCount - 1, Math.max(0, Math.floor(normalized * bucketCount)));
848
+ buckets[index]++;
849
+ }
850
+ return {
851
+ w,
852
+ h,
853
+ isHorizontal,
854
+ padding,
855
+ dataMin,
856
+ dataMax,
857
+ dataSpan,
858
+ bucketCount,
859
+ availableMain,
860
+ availableCross,
861
+ buckets
862
+ };
863
+ }
864
+ /** Per-bucket rect geometry (SVG pixel coords) + value/count, shared by render() and pointer hit-testing. */
865
+ #computeBars() {
866
+ const { isHorizontal, padding, dataMin, dataSpan, bucketCount, availableMain, availableCross, buckets } = this.#computeGeometry();
867
+ const bucketSize = availableMain / bucketCount;
868
+ const gap = this.#getGapPx();
869
+ const barLengthOverride = this.#getBarLengthOverride();
870
+ const maxCount = Math.max(1, ...buckets);
871
+ return buckets.map((count, i) => {
872
+ const relativeValue = count / maxCount;
873
+ const barLen = barLengthOverride !== null ? barLengthOverride * availableCross : relativeValue * availableCross;
874
+ const valueMin = dataMin + i / bucketCount * dataSpan;
875
+ const valueMax = dataMin + (i + 1) / bucketCount * dataSpan;
876
+ if (isHorizontal) {
877
+ const x = padding + i * bucketSize;
878
+ const width = i === bucketCount - 1 ? Math.max(0, padding + availableMain - x) : Math.max(0, bucketSize - gap);
879
+ return {
880
+ count,
881
+ valueMin,
882
+ valueMax,
883
+ relativeValue,
884
+ x,
885
+ y: padding + availableCross - barLen,
886
+ width,
887
+ height: barLen
888
+ };
889
+ } else {
890
+ const y = padding + i * bucketSize;
891
+ const height = i === bucketCount - 1 ? Math.max(0, padding + availableMain - y) : Math.max(0, bucketSize - gap);
892
+ return {
893
+ count,
894
+ valueMin,
895
+ valueMax,
896
+ relativeValue,
897
+ x: padding,
898
+ y,
899
+ width: barLen,
900
+ height
901
+ };
902
+ }
903
+ });
904
+ }
905
+ /** Index of the bar rect under the pointer, or -1 if none (matches click hit-testing). */
906
+ #bucketIndexFromPointer(clientX, clientY) {
907
+ const rect = this.getBoundingClientRect();
908
+ const x = clientX - rect.left;
909
+ const y = clientY - rect.top;
910
+ return this.#computeBars().findIndex((b) => x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height);
911
+ }
912
+ #tooltipIndex;
913
+ #tooltipVisible;
914
+ #tooltipShowTimer;
915
+ #tooltipHideTimer;
916
+ #tooltipPosition;
917
+ #valueRefreshTimer;
918
+ #clearValueRefreshTimer() {
919
+ if (this.#valueRefreshTimer !== null) {
920
+ clearTimeout(this.#valueRefreshTimer);
921
+ this.#valueRefreshTimer = null;
922
+ }
923
+ }
924
+ /** While a bucket stays hovered, re-invoke `tick` at most every `valueUpdateInterval` ms, so a live-streaming bucket's displayed value stays fresh without updating on every single data point. */
925
+ #scheduleValueRefresh(tick) {
926
+ this.#clearValueRefreshTimer();
927
+ this.#valueRefreshTimer = window.setTimeout(() => {
928
+ this.#valueRefreshTimer = null;
929
+ tick();
930
+ this.#scheduleValueRefresh(tick);
931
+ }, this.valueUpdateInterval);
932
+ }
933
+ #clearBubbleTooltip() {
934
+ if (this.#tooltipShowTimer !== null) {
935
+ clearTimeout(this.#tooltipShowTimer);
936
+ this.#tooltipShowTimer = null;
937
+ }
938
+ if (this.#tooltipHideTimer !== null) {
939
+ clearTimeout(this.#tooltipHideTimer);
940
+ this.#tooltipHideTimer = null;
941
+ }
942
+ if (this.#tooltipVisible) {
943
+ this.#tooltipVisible = false;
944
+ hideBubbleTooltip(this);
945
+ }
946
+ this.#tooltipIndex = null;
947
+ this.#tooltipPosition = null;
948
+ }
949
+ #resetHoverState() {
950
+ this.#clearValueRefreshTimer();
951
+ this.#clearBubbleTooltip();
952
+ this._hoveredIndex = null;
953
+ this._inlineOverlayText = null;
954
+ }
955
+ /** Bubble position: centred on the bar along the bucket axis, offset from the cursor on the other axis. */
956
+ #tooltipPositionFor(bar, clientX, clientY) {
957
+ const hostRect = this.getBoundingClientRect();
958
+ const offset = 14;
959
+ if (this.orientation === `horizontal`) return {
960
+ mainClient: hostRect.left + bar.x + bar.width / 2,
961
+ crossClient: clientY - offset
962
+ };
963
+ return {
964
+ mainClient: hostRect.top + bar.y + bar.height / 2,
965
+ crossClient: clientX + offset
966
+ };
967
+ }
968
+ #refreshBubbleText() {
969
+ if (this.#tooltipIndex === null || !this.#tooltipVisible || !this.#tooltipPosition) return;
970
+ const bar = this.#computeBars()[this.#tooltipIndex];
971
+ if (!bar) return;
972
+ const text = this.valueFormat(bar.valueMin, bar.valueMax, bar.count);
973
+ showBubbleTooltip(this, text, this.#tooltipPosition.mainClient, this.#tooltipPosition.crossClient, this.orientation);
974
+ }
975
+ /**
976
+ * Drives the floating bubble tooltip. Only reacts to the hovered bucket *changing*
977
+ * (not every pointermove), so the tooltip doesn't track minor cursor movement.
978
+ * Debounces the initial show and the final hide; switching between buckets while
979
+ * already visible updates instantly. While a bucket stays hovered, its displayed
980
+ * value is refreshed at `valueUpdateInterval`.
981
+ */
982
+ #updateBubbleTooltip(index, clientX, clientY) {
983
+ if (index === this.#tooltipIndex) return;
984
+ this.#tooltipIndex = index;
985
+ this.#clearValueRefreshTimer();
986
+ if (index === null) {
987
+ if (this.#tooltipShowTimer !== null) {
988
+ clearTimeout(this.#tooltipShowTimer);
989
+ this.#tooltipShowTimer = null;
990
+ }
991
+ if (this.#tooltipVisible) this.#tooltipHideTimer = window.setTimeout(() => {
992
+ this.#tooltipHideTimer = null;
993
+ this.#tooltipVisible = false;
994
+ this.#tooltipPosition = null;
995
+ hideBubbleTooltip(this);
996
+ }, this.tooltipDelay);
997
+ return;
998
+ }
999
+ if (this.#tooltipHideTimer !== null) {
1000
+ clearTimeout(this.#tooltipHideTimer);
1001
+ this.#tooltipHideTimer = null;
1002
+ }
1003
+ const bar = this.#computeBars()[index];
1004
+ if (!bar) return;
1005
+ const text = this.valueFormat(bar.valueMin, bar.valueMax, bar.count);
1006
+ const position = this.#tooltipPositionFor(bar, clientX, clientY);
1007
+ this.#tooltipPosition = position;
1008
+ if (this.#tooltipVisible) {
1009
+ showBubbleTooltip(this, text, position.mainClient, position.crossClient, this.orientation);
1010
+ this.#scheduleValueRefresh(() => this.#refreshBubbleText());
1011
+ return;
1012
+ }
1013
+ if (this.#tooltipShowTimer !== null) clearTimeout(this.#tooltipShowTimer);
1014
+ this.#tooltipShowTimer = window.setTimeout(() => {
1015
+ this.#tooltipShowTimer = null;
1016
+ this.#tooltipVisible = true;
1017
+ showBubbleTooltip(this, text, position.mainClient, position.crossClient, this.orientation);
1018
+ this.#scheduleValueRefresh(() => this.#refreshBubbleText());
1019
+ }, this.tooltipDelay);
1020
+ }
1021
+ #formatBarAt(index) {
1022
+ const bar = this.#computeBars()[index];
1023
+ return bar ? this.valueFormat(bar.valueMin, bar.valueMax, bar.count) : null;
1024
+ }
1025
+ /**
1026
+ * Drives the `inline` overlay-text: instant on hover change, throttled while stationary
1027
+ * over a live-changing bucket. `_inlineOverlayText` is cached reactive state rather than
1028
+ * being recomputed fresh in render() on every data change, since add()/set() trigger their
1029
+ * own re-render independent of the throttle timer.
1030
+ */
1031
+ #setInlineIndex(index) {
1032
+ if (index === this._hoveredIndex) return;
1033
+ this._hoveredIndex = index;
1034
+ this.#clearValueRefreshTimer();
1035
+ if (index === null) {
1036
+ this._inlineOverlayText = null;
1037
+ return;
1038
+ }
1039
+ this._inlineOverlayText = this.#formatBarAt(index);
1040
+ this.#scheduleValueRefresh(() => {
1041
+ if (this._hoveredIndex !== null) this._inlineOverlayText = this.#formatBarAt(this._hoveredIndex);
1042
+ });
1043
+ }
1044
+ #onPointerMove;
1045
+ #onPointerLeave;
1046
+ render() {
1047
+ const { w, h, padding } = this.#computeGeometry();
1048
+ const barsContent = this.#computeBars().map((b, i) => svg`
1049
+ <rect class="bar" x=${b.x} y=${b.y} width=${b.width} height=${b.height}
1050
+ style="--value: ${b.relativeValue}"
1051
+ @click=${() => this.#emitClick(i, b.valueMin, b.valueMax, b.relativeValue)} />
1052
+ `);
1053
+ const textColor = `var(--plot-text, var(--surface-2-text, #fff))`;
1054
+ const overlayText = this.valueTooltips === `inline` && this._inlineOverlayText !== null ? this._inlineOverlayText : this.title;
1055
+ const titleContent = overlayText ? svg`<text x=${padding + 2} y=${padding + Math.min(10, h * .6)}
1056
+ class="overlay-text" fill=${textColor}
1057
+ style="opacity: 0.6">${overlayText}</text>` : svg``;
1058
+ return html`
1059
+ <svg width=${w} height=${h} viewBox="0 0 ${w} ${h}"
1060
+ xmlns="http://www.w3.org/2000/svg"
1061
+ @pointermove=${this.#onPointerMove}
1062
+ @pointerleave=${this.#onPointerLeave}>
1063
+ <rect width=${w} height=${h} fill="transparent" />
1064
+ ${barsContent}
1065
+ ${titleContent}
1066
+ </svg>
1067
+ `;
1068
+ }
1069
+ static {
1070
+ this.styles = css`
1071
+ :host {
1072
+ display: inline-block;
1073
+ width: 100%;
1074
+ height: 100%;
1075
+ min-width: 20px;
1076
+ min-height: 20px;
1077
+
1078
+ --plot-histogram-fill: currentColor;
1079
+ --plot-histogram-gap: 1px;
1080
+ --plot-histogram-border-radius: 0;
1081
+ --plot-histogram-bar-length: auto;
1082
+ }
1083
+
1084
+ svg {
1085
+ display: block;
1086
+ cursor: crosshair;
1087
+ overflow: visible;
1088
+ }
1089
+
1090
+ rect.bar {
1091
+ fill: var(--plot-histogram-fill, currentColor);
1092
+ rx: var(--plot-histogram-border-radius, 0);
1093
+ opacity: calc(0.15 + var(--value) * 0.85);
1094
+ }
1095
+
1096
+ .overlay-text {
1097
+ font-family: var(--font-mono, ui-monospace, monospace);
1098
+ font-size: 9px;
1099
+ dominant-baseline: hanging;
1100
+ pointer-events: none;
1101
+ }
1102
+ `;
1103
+ }
1104
+ };
1105
+ __decorate([property({ attribute: `orientation` })], PlotHistogram.prototype, "orientation", void 0);
1106
+ __decorate([property({ type: Number })], PlotHistogram.prototype, "resolution", void 0);
1107
+ __decorate([property({ type: Number })], PlotHistogram.prototype, "min", void 0);
1108
+ __decorate([property({ type: Number })], PlotHistogram.prototype, "max", void 0);
1109
+ __decorate([property({ type: Number })], PlotHistogram.prototype, "capacity", void 0);
1110
+ __decorate([property({ attribute: false })], PlotHistogram.prototype, "title", void 0);
1111
+ __decorate([property({
1112
+ type: Boolean,
1113
+ reflect: true
1114
+ })], PlotHistogram.prototype, "paused", void 0);
1115
+ __decorate([property({ attribute: `value-tooltips` })], PlotHistogram.prototype, "valueTooltips", void 0);
1116
+ __decorate([property({
1117
+ type: Number,
1118
+ attribute: `tooltip-delay`
1119
+ })], PlotHistogram.prototype, "tooltipDelay", void 0);
1120
+ __decorate([property({
1121
+ type: Number,
1122
+ attribute: `value-update-interval`
1123
+ })], PlotHistogram.prototype, "valueUpdateInterval", void 0);
1124
+ __decorate([state()], PlotHistogram.prototype, "_width", void 0);
1125
+ __decorate([state()], PlotHistogram.prototype, "_height", void 0);
1126
+ __decorate([state()], PlotHistogram.prototype, "_hoveredIndex", void 0);
1127
+ __decorate([state()], PlotHistogram.prototype, "_inlineOverlayText", void 0);
1128
+ PlotHistogram = __decorate([safeCustomElement(`ixfx-plot-histogram`)], PlotHistogram);
1129
+ //#endregion
1130
+ //#region src/plots/single-axis.ts
1131
+ function buildLinePath(points) {
1132
+ if (points.length === 0) return ``;
1133
+ let d = `M ${points[0].x},${points[0].y}`;
1134
+ for (let i = 1; i < points.length; i++) d += ` L ${points[i].x},${points[i].y}`;
1135
+ return d;
1136
+ }
1137
+ function renderLine(points, cr, strokeW, round) {
1138
+ return svg`<path
1139
+ d=${buildLinePath(points)}
1140
+ fill="none"
1141
+ stroke=${cr.fill}
1142
+ stroke-width=${strokeW}
1143
+ opacity=${cr.opacity}
1144
+ stroke-linecap=${round ? `round` : `butt`}
1145
+ stroke-linejoin=${round ? `round` : `miter`}
1146
+ />`;
1147
+ }
1148
+ function renderArea(points, cr, bottom, areaColor) {
1149
+ if (points.length < 2) return svg``;
1150
+ const lineD = buildLinePath(points);
1151
+ const fillColor = areaColor || cr.fill;
1152
+ const areaD = `${lineD} L ${points[points.length - 1].x},${bottom} L ${points[0].x},${bottom} Z`;
1153
+ return svg`<path d=${areaD} fill=${fillColor} opacity=${cr.opacity * .2} />`;
1154
+ }
1155
+ function renderCircles(points, crs, radius, ageStyle) {
1156
+ return points.map((p, i) => {
1157
+ const r = ageStyle === `size` ? radius * Math.max(.2, p.ageFraction) : radius;
1158
+ return svg`<circle cx=${p.x} cy=${p.y} r=${r} fill=${crs[i].fill} opacity=${crs[i].opacity} />`;
1159
+ });
1160
+ }
1161
+ function renderSquares(points, crs, size, ageStyle) {
1162
+ return points.map((p, i) => {
1163
+ const s = ageStyle === `size` ? size * Math.max(.2, p.ageFraction) : size;
1164
+ return svg`<rect
1165
+ x=${p.x - s / 2} y=${p.y - s / 2}
1166
+ width=${s} height=${s}
1167
+ fill=${crs[i].fill} opacity=${crs[i].opacity}
1168
+ />`;
1169
+ });
1170
+ }
1171
+ function renderVbars(points, crs, strokeW, bottom, ageStyle) {
1172
+ return points.map((p, i) => {
1173
+ const sw = ageStyle === `size` ? strokeW * Math.max(.2, p.ageFraction) : strokeW;
1174
+ return svg`<line
1175
+ x1=${p.x} y1=${bottom} x2=${p.x} y2=${p.y}
1176
+ stroke=${crs[i].fill} stroke-width=${sw} opacity=${crs[i].opacity}
1177
+ />`;
1178
+ });
1179
+ }
1180
+ function renderHbars(points, crs, strokeW, tickLen, ageStyle) {
1181
+ return points.map((p, i) => {
1182
+ const tl = ageStyle === `size` ? tickLen * Math.max(.2, p.ageFraction) : tickLen;
1183
+ return svg`<line
1184
+ x1=${p.x - tl / 2} y1=${p.y} x2=${p.x + tl / 2} y2=${p.y}
1185
+ stroke=${crs[i].fill} stroke-width=${strokeW} opacity=${crs[i].opacity}
1186
+ />`;
1187
+ });
1188
+ }
1189
+ function renderHistBottom(points, crs, barW, bottom, ageStyle) {
1190
+ return points.map((p, i) => {
1191
+ const w = ageStyle === `size` ? barW * Math.max(.2, p.ageFraction) : barW;
1192
+ return svg`<rect
1193
+ x=${p.x - w / 2} y=${p.y}
1194
+ width=${w} height=${Math.max(1, bottom - p.y)}
1195
+ fill=${crs[i].fill} opacity=${crs[i].opacity}
1196
+ />`;
1197
+ });
1198
+ }
1199
+ function renderHistTop(points, crs, barW, top, ageStyle) {
1200
+ return points.map((p, i) => {
1201
+ const w = ageStyle === `size` ? barW * Math.max(.2, p.ageFraction) : barW;
1202
+ return svg`<rect
1203
+ x=${p.x - w / 2} y=${top}
1204
+ width=${w} height=${Math.max(1, p.y - top)}
1205
+ fill=${crs[i].fill} opacity=${crs[i].opacity}
1206
+ />`;
1207
+ });
1208
+ }
1209
+ function renderHistMiddle(points, crs, barW, mid, ageStyle) {
1210
+ return points.map((p, i) => {
1211
+ const w = ageStyle === `size` ? barW * Math.max(.2, p.ageFraction) : barW;
1212
+ return svg`<rect
1213
+ x=${p.x - w / 2} y=${Math.min(p.y, mid)}
1214
+ width=${w} height=${Math.max(1, Math.abs(p.y - mid))}
1215
+ fill=${crs[i].fill} opacity=${crs[i].opacity}
1216
+ />`;
1217
+ });
1218
+ }
1219
+ let PlotSingleAxis = class PlotSingleAxis extends LitElement {
1220
+ constructor(..._args) {
1221
+ super(..._args);
1222
+ this.drawStyle = `line-sharp`;
1223
+ this.ageStyle = `none`;
1224
+ this.title = ``;
1225
+ this.precise = false;
1226
+ this.tooltips = true;
1227
+ this.capacity = 100;
1228
+ this.rangeMin = NaN;
1229
+ this.rangeMax = NaN;
1230
+ this.persistentScale = true;
1231
+ this.paused = false;
1232
+ this.clickPauses = false;
1233
+ this.shaded = false;
1234
+ this.areaColor = ``;
1235
+ this.colorMode = `fixed`;
1236
+ this.colorEasing = ``;
1237
+ this.colorScale = ``;
1238
+ this.colorValueMap = null;
1239
+ this._width = 100;
1240
+ this._height = 40;
1241
+ this.#series = new PlotDataSeries();
1242
+ this.#hasProgrammaticData = false;
1243
+ this.#lastClientX = null;
1244
+ this.#lastClientY = null;
1245
+ this.valueFormat = (v) => v ? v.toFixed(2) : ``;
1246
+ this.#onPointerMove = (e) => {
1247
+ this.#lastClientX = e.clientX;
1248
+ this.#lastClientY = e.clientY;
1249
+ this.#updateTooltip(e.clientX, e.clientY);
1250
+ const index = this.#getIndexFromX(e.clientX, this.getBoundingClientRect());
1251
+ this.dispatchEvent(new CustomEvent(`plot-pointer-move`, {
1252
+ detail: {
1253
+ clientX: e.clientX,
1254
+ clientY: e.clientY,
1255
+ index
1256
+ },
1257
+ bubbles: true,
1258
+ composed: true
1259
+ }));
1260
+ };
1261
+ this.#onPointerLeave = () => {
1262
+ this.#lastClientX = null;
1263
+ this.#lastClientY = null;
1264
+ hideTooltip(this);
1265
+ this.dispatchEvent(new CustomEvent(`plot-pointer-leave`, {
1266
+ bubbles: true,
1267
+ composed: true
1268
+ }));
1269
+ };
1270
+ this.#onClick = (e) => {
1271
+ if (this.clickPauses) this.paused = !this.paused;
1272
+ const index = this.#getIndexFromX(e.clientX, this.getBoundingClientRect());
1273
+ if (index < 0) return;
1274
+ this.dispatchEvent(new CustomEvent(`plot-datapoint-click`, {
1275
+ detail: {
1276
+ value: this.#series.values[index],
1277
+ index
1278
+ },
1279
+ bubbles: true,
1280
+ composed: true
1281
+ }));
1282
+ };
1283
+ }
1284
+ #series;
1285
+ #hasProgrammaticData;
1286
+ #lastClientX;
1287
+ #lastClientY;
1288
+ #parseDeclarativeData(text) {
1289
+ if (!text) return [];
1290
+ return text.trim().split(/[,\s;]+/).map((t) => Number.parseFloat(t)).filter((n) => !Number.isNaN(n));
1291
+ }
1292
+ /**
1293
+ * Adds a value to the data series.
1294
+ *
1295
+ * Pass `withholdRedraw = true` to prevent automatic redraw after adding the value. In this case, call `redraw()` manually after adding all values to update the plot.
1296
+ *
1297
+ * Use {@link set} to replace the entire data series at once.
1298
+ * @param valueOrValues Single value, or several values
1299
+ * @param withholdRedraw If _false_ (default) plot is automatically redrawn
1300
+ */
1301
+ add(valueOrValues, withholdRedraw = false) {
1302
+ this.#hasProgrammaticData = true;
1303
+ this.#series.add(valueOrValues);
1304
+ if (this.paused) return;
1305
+ if (withholdRedraw) return;
1306
+ this.redraw();
1307
+ }
1308
+ /**
1309
+ * Redraws the plot.
1310
+ * Normally not needed, unless add() is called with withholdRedraw = true, or styleSeries() is used to update series styles.
1311
+ */
1312
+ redraw() {
1313
+ this.requestUpdate();
1314
+ this.#updateTooltipAtLastPos();
1315
+ }
1316
+ /**
1317
+ * Sets the entire data for a series at once, replacing contents.
1318
+ *
1319
+ * Use add() to add values one by one.
1320
+ *
1321
+ * Pass `withholdRedraw = true` to prevent automatic redraw after setting the values. In this case, call `redraw()` manually after setting the values to update the plot.
1322
+ * @param values Values
1323
+ * @param withholdRedraw If _false_ (default) plot is automatically redrawn
1324
+ */
1325
+ set(values, withholdRedraw = false) {
1326
+ this.#hasProgrammaticData = true;
1327
+ this.#series.set(values);
1328
+ if (this.paused) return;
1329
+ if (withholdRedraw) return;
1330
+ this.redraw();
1331
+ }
1332
+ /**
1333
+ * Clears all data and redraws
1334
+ */
1335
+ clear() {
1336
+ const result = this.#series.clear();
1337
+ this.requestUpdate();
1338
+ return result;
1339
+ }
1340
+ firstUpdated(_changed) {
1341
+ this._width = this.clientWidth || 100;
1342
+ this._height = this.clientHeight || 40;
1343
+ new ResizeObserver((entries) => {
1344
+ const e = entries[0];
1345
+ this._width = e.contentRect.width;
1346
+ this._height = e.contentRect.height;
1347
+ }).observe(this);
1348
+ if (!this.#hasProgrammaticData) {
1349
+ const data = this.#parseDeclarativeData(this.textContent ?? ``);
1350
+ if (data.length > 0) this.#series.set(data);
1351
+ }
1352
+ }
1353
+ updated(changed) {
1354
+ if (changed.has(`capacity`) || changed.has(`persistentScale`) || changed.has(`rangeMin`) || changed.has(`rangeMax`)) {
1355
+ const old = [...this.#series.values];
1356
+ const fixedRange = !Number.isNaN(this.rangeMin) && !Number.isNaN(this.rangeMax) ? {
1357
+ min: this.rangeMin,
1358
+ max: this.rangeMax
1359
+ } : null;
1360
+ this.#series = new PlotDataSeries({
1361
+ capacityLimit: this.capacity,
1362
+ persistentScaling: this.persistentScale,
1363
+ fixedRange
1364
+ });
1365
+ if (old.length > 0) this.#series.set(old);
1366
+ }
1367
+ }
1368
+ #getIndexFromX(clientX, rect) {
1369
+ const n = this.#series.values.length;
1370
+ if (n === 0) return -1;
1371
+ const x = clientX - rect.left;
1372
+ if (x < 0 || x > rect.width) return -1;
1373
+ const padding = rect.height < 20 ? 0 : Math.min(4, rect.height * .05);
1374
+ const plotW = rect.width - padding * 2;
1375
+ const index = Math.floor((x - padding) / (plotW / n));
1376
+ return index >= 0 && index < n ? index : -1;
1377
+ }
1378
+ #updateTooltip(clientX, clientY) {
1379
+ if (!this.tooltips) return;
1380
+ const rect = this.getBoundingClientRect();
1381
+ const index = this.#getIndexFromX(clientX, rect);
1382
+ if (index < 0) {
1383
+ hideTooltip(this);
1384
+ return;
1385
+ }
1386
+ showTooltip(this, clientX, clientY, this.valueFormat(this.#series.values[index]));
1387
+ }
1388
+ #updateTooltipAtLastPos() {
1389
+ if (this.#lastClientX === null || this.#lastClientY === null) return;
1390
+ this.#updateTooltip(this.#lastClientX, this.#lastClientY);
1391
+ }
1392
+ #onPointerMove;
1393
+ #onPointerLeave;
1394
+ #onClick;
1395
+ #computePoints(values, min, max, padding, plotW, plotH) {
1396
+ const n = values.length;
1397
+ const widthPerPoint = n > 0 ? plotW / n : plotW;
1398
+ const scaleX = (i) => padding + (i + .5) * widthPerPoint;
1399
+ return values.map((value, index) => {
1400
+ const sv = clamp(scaleValue(value, min, max));
1401
+ const ageFraction = n > 1 ? index / (n - 1) : 1;
1402
+ return {
1403
+ x: scaleX(index),
1404
+ y: padding + (1 - sv) * plotH,
1405
+ value,
1406
+ scaledValue: sv,
1407
+ index,
1408
+ ageFraction
1409
+ };
1410
+ });
1411
+ }
1412
+ get series() {
1413
+ return this.#series;
1414
+ }
1415
+ /**
1416
+ * Get all values in the data series as a readonly array.
1417
+ */
1418
+ get values() {
1419
+ return this.#series.values;
1420
+ }
1421
+ /**
1422
+ * Get length of data series
1423
+ */
1424
+ get dataLength() {
1425
+ return this.#series.values.length;
1426
+ }
1427
+ /**
1428
+ * Get value at specific index of data series
1429
+ * @param index
1430
+ */
1431
+ getValueAt(index) {
1432
+ return this.#series.values[index];
1433
+ }
1434
+ /**
1435
+ * Get value index based on client X position
1436
+ * @param clientX
1437
+ */
1438
+ getIndexFromClientX(clientX) {
1439
+ return this.#getIndexFromX(clientX, this.getBoundingClientRect());
1440
+ }
1441
+ #renderByStyle(points, crs, strokeW, radius, barW, bottom, top, mid) {
1442
+ const lineCr = crs.at(-1) ?? {
1443
+ fill: `currentColor`,
1444
+ opacity: 1
1445
+ };
1446
+ const needsArea = (this.drawStyle === `line-sharp` || this.drawStyle === `line-round`) && this.shaded;
1447
+ const areaColor = this.areaColor || void 0;
1448
+ if (needsArea) return [renderArea(points, lineCr, bottom, areaColor), this.drawStyle === `line-sharp` ? renderLine(points, lineCr, strokeW, false) : renderLine(points, lineCr, strokeW, true)];
1449
+ switch (this.drawStyle) {
1450
+ case `line-sharp`: return [renderLine(points, lineCr, strokeW, false)];
1451
+ case `line-round`: return [renderLine(points, lineCr, strokeW, true)];
1452
+ case `circles`: return renderCircles(points, crs, radius, this.ageStyle);
1453
+ case `squares`: return renderSquares(points, crs, radius * 2, this.ageStyle);
1454
+ case `vbar`: return renderVbars(points, crs, strokeW, bottom, this.ageStyle);
1455
+ case `hbar`: return renderHbars(points, crs, strokeW, barW, this.ageStyle);
1456
+ case `hist-bottom`: return renderHistBottom(points, crs, barW, bottom, this.ageStyle);
1457
+ case `hist-top`: return renderHistTop(points, crs, barW, top, this.ageStyle);
1458
+ case `hist-middle`: return renderHistMiddle(points, crs, barW, mid, this.ageStyle);
1459
+ default: return [];
1460
+ }
1461
+ }
1462
+ render() {
1463
+ const { values, range } = this.#series;
1464
+ const w = this._width;
1465
+ const h = this._height;
1466
+ const padding = h < 20 ? 0 : Math.min(4, h * .05);
1467
+ const plotW = Math.max(1, w - padding * 2);
1468
+ const plotH = Math.max(1, h - padding * 2);
1469
+ const bottom = padding + plotH;
1470
+ const top = padding;
1471
+ const mid = padding + plotH / 2;
1472
+ const strokeW = adaptiveStroke(w, h);
1473
+ const radius = adaptiveRadius(w, h, values.length);
1474
+ const barW = values.length > 0 ? Math.max(1, plotW / values.length * .85) : plotW;
1475
+ const textColor = `var(--plot-text, var(--surface-2-text, #fff))`;
1476
+ const points = values.length > 0 ? this.#computePoints(values, range.min, range.max, padding, plotW, plotH) : [];
1477
+ const baseColour = points.length > 0 ? readBaseColour(this) : ``;
1478
+ const colourConfig = {
1479
+ mode: this.colorMode,
1480
+ palette: parseColourScaleAttr(this.colorScale),
1481
+ easingName: this.colorEasing,
1482
+ valueMap: this.colorValueMap
1483
+ };
1484
+ const isDiscrete = this.drawStyle !== `line-sharp` && this.drawStyle !== `line-round`;
1485
+ const crs = points.map((p) => {
1486
+ const base = computeColour(p.scaledValue, baseColour, colourConfig);
1487
+ return isDiscrete ? applyAgeModification(base, p.ageFraction, this.ageStyle) : base;
1488
+ });
1489
+ const dataContent = points.length > 0 ? this.#renderByStyle(points, crs, strokeW, radius, barW, bottom, top, mid) : [];
1490
+ const titleContent = this.title ? svg`<text
1491
+ x=${padding + 2} y=${top + Math.min(10, h * .6)}
1492
+ class="overlay-text"
1493
+ fill=${textColor}
1494
+ style="opacity: 0.6"
1495
+ >${this.title}</text>` : svg``;
1496
+ const latestValue = values.length > 0 ? values.at(-1) : null;
1497
+ const preciseContent = this.precise && latestValue !== null ? svg`<text
1498
+ x=${w - padding - 2} y=${top + Math.min(10, h * .6)}
1499
+ class="overlay-text"
1500
+ text-anchor="end"
1501
+ fill=${textColor}
1502
+ style="opacity: 0.8"
1503
+ >${this.valueFormat(latestValue)}</text>` : svg``;
1504
+ return html`
1505
+ <svg
1506
+ width=${w} height=${h}
1507
+ viewBox="0 0 ${w} ${h}"
1508
+ xmlns="http://www.w3.org/2000/svg"
1509
+ @pointermove=${this.#onPointerMove}
1510
+ @pointerleave=${this.#onPointerLeave}
1511
+ @click=${this.#onClick}
1512
+ >
1513
+ <rect width=${w} height=${h} fill="transparent" />
1514
+ ${dataContent.map((c) => c)}
1515
+ ${titleContent}
1516
+ ${preciseContent}
1517
+ </svg>
1518
+ `;
1519
+ }
1520
+ static {
1521
+ this.styles = css`
1522
+ :host {
1523
+ display: inline-block;
1524
+ width: 100%;
1525
+ height: 100%;
1526
+ min-width: 20px;
1527
+ min-height: 4px;
1528
+ }
1529
+
1530
+ svg {
1531
+ display: block;
1532
+ cursor: crosshair;
1533
+ overflow: visible;
1534
+ }
1535
+
1536
+ .overlay-text {
1537
+ font-family: var(--font-mono, ui-monospace, monospace);
1538
+ font-size: 9px;
1539
+ dominant-baseline: hanging;
1540
+ pointer-events: none;
1541
+ }
1542
+ `;
1543
+ }
1544
+ };
1545
+ __decorate([property({ attribute: `draw-style` })], PlotSingleAxis.prototype, "drawStyle", void 0);
1546
+ __decorate([property({ attribute: `age-style` })], PlotSingleAxis.prototype, "ageStyle", void 0);
1547
+ __decorate([property()], PlotSingleAxis.prototype, "title", void 0);
1548
+ __decorate([property({ type: Boolean })], PlotSingleAxis.prototype, "precise", void 0);
1549
+ __decorate([property({ type: Boolean })], PlotSingleAxis.prototype, "tooltips", void 0);
1550
+ __decorate([property({ type: Number })], PlotSingleAxis.prototype, "capacity", void 0);
1551
+ __decorate([property({ type: Number })], PlotSingleAxis.prototype, "rangeMin", void 0);
1552
+ __decorate([property({ type: Number })], PlotSingleAxis.prototype, "rangeMax", void 0);
1553
+ __decorate([property({
1554
+ type: Boolean,
1555
+ attribute: `persistent-scale`
1556
+ })], PlotSingleAxis.prototype, "persistentScale", void 0);
1557
+ __decorate([property({
1558
+ type: Boolean,
1559
+ reflect: true
1560
+ })], PlotSingleAxis.prototype, "paused", void 0);
1561
+ __decorate([property({
1562
+ type: Boolean,
1563
+ attribute: `click-pauses`
1564
+ })], PlotSingleAxis.prototype, "clickPauses", void 0);
1565
+ __decorate([property({
1566
+ type: Boolean,
1567
+ attribute: `shaded`
1568
+ })], PlotSingleAxis.prototype, "shaded", void 0);
1569
+ __decorate([property({ attribute: `area-color` })], PlotSingleAxis.prototype, "areaColor", void 0);
1570
+ __decorate([property({ attribute: `color-mode` })], PlotSingleAxis.prototype, "colorMode", void 0);
1571
+ __decorate([property({ attribute: `color-easing` })], PlotSingleAxis.prototype, "colorEasing", void 0);
1572
+ __decorate([property({ attribute: `color-scale` })], PlotSingleAxis.prototype, "colorScale", void 0);
1573
+ __decorate([state()], PlotSingleAxis.prototype, "_width", void 0);
1574
+ __decorate([state()], PlotSingleAxis.prototype, "_height", void 0);
1575
+ PlotSingleAxis = __decorate([safeCustomElement(`ixfx-plot-single-axis`)], PlotSingleAxis);
1576
+ //#endregion
1577
+ //#region src/plots/xy-axis.ts
1578
+ let PlotXyAxis = class PlotXyAxis extends LitElement {
1579
+ constructor(..._args) {
1580
+ super(..._args);
1581
+ this.drawStyle = `circles`;
1582
+ this.ageStyle = `none`;
1583
+ this.title = ``;
1584
+ this.precise = false;
1585
+ this.tooltips = true;
1586
+ this.capacity = 100;
1587
+ this.persistentScale = true;
1588
+ this.paused = false;
1589
+ this.clickPauses = false;
1590
+ this.colorMode = `fixed`;
1591
+ this.colorEasing = ``;
1592
+ this.colorScale = ``;
1593
+ this.colorValueMap = null;
1594
+ this._width = 100;
1595
+ this._height = 100;
1596
+ this.#xSeries = new PlotDataSeries({
1597
+ capacityLimit: 100,
1598
+ persistentScaling: true
1599
+ });
1600
+ this.#ySeries = new PlotDataSeries({
1601
+ capacityLimit: 100,
1602
+ persistentScaling: true
1603
+ });
1604
+ this.valueFormat = (x, y) => `${x.toFixed(2)}, ${y.toFixed(2)}`;
1605
+ this.#lastPoints = [];
1606
+ this.#lastClientX = null;
1607
+ this.#lastClientY = null;
1608
+ this.#onPointerMove = (e) => {
1609
+ this.#lastClientX = e.clientX;
1610
+ this.#lastClientY = e.clientY;
1611
+ this.#updateTooltip(e.clientX, e.clientY);
1612
+ };
1613
+ this.#onPointerLeave = () => {
1614
+ this.#lastClientX = null;
1615
+ this.#lastClientY = null;
1616
+ hideTooltip(this);
1617
+ };
1618
+ this.#onClick = (e) => {
1619
+ if (this.clickPauses) this.paused = !this.paused;
1620
+ const rect = e.currentTarget.getBoundingClientRect();
1621
+ const nearest = this.#findNearestPoint(e.clientX, e.clientY, this.#lastPoints, rect);
1622
+ if (!nearest) return;
1623
+ this.dispatchEvent(new CustomEvent(`plot-datapoint-click`, {
1624
+ detail: {
1625
+ x: nearest.xVal,
1626
+ y: nearest.yVal,
1627
+ index: nearest.index
1628
+ },
1629
+ bubbles: true,
1630
+ composed: true
1631
+ }));
1632
+ };
1633
+ }
1634
+ #xSeries;
1635
+ #ySeries;
1636
+ add(x, y) {
1637
+ this.#xSeries.add(x);
1638
+ this.#ySeries.add(y);
1639
+ if (this.paused) return;
1640
+ this.requestUpdate();
1641
+ this.#updateTooltipAtLastPos();
1642
+ }
1643
+ setXY(xs, ys) {
1644
+ this.#xSeries.set(xs);
1645
+ this.#ySeries.set(ys);
1646
+ if (this.paused) return;
1647
+ this.requestUpdate();
1648
+ this.#updateTooltipAtLastPos();
1649
+ }
1650
+ clear() {
1651
+ this.#xSeries.clear();
1652
+ this.#ySeries.clear();
1653
+ this.requestUpdate();
1654
+ }
1655
+ firstUpdated(_changed) {
1656
+ this._width = this.clientWidth || 100;
1657
+ this._height = this.clientHeight || 100;
1658
+ new ResizeObserver((entries) => {
1659
+ const e = entries[0];
1660
+ this._width = e.contentRect.width;
1661
+ this._height = e.contentRect.height;
1662
+ }).observe(this);
1663
+ }
1664
+ updated(changed) {
1665
+ if (changed.has(`capacity`) || changed.has(`persistentScale`)) {
1666
+ const opts = {
1667
+ capacityLimit: this.capacity,
1668
+ persistentScaling: this.persistentScale
1669
+ };
1670
+ const oldX = [...this.#xSeries.values];
1671
+ const oldY = [...this.#ySeries.values];
1672
+ this.#xSeries = new PlotDataSeries(opts);
1673
+ this.#ySeries = new PlotDataSeries(opts);
1674
+ if (oldX.length > 0) {
1675
+ this.#xSeries.set(oldX);
1676
+ this.#ySeries.set(oldY);
1677
+ }
1678
+ }
1679
+ }
1680
+ #computePoints(padding, plotW, plotH) {
1681
+ const xs = this.#xSeries.values;
1682
+ const ys = this.#ySeries.values;
1683
+ const n = Math.min(xs.length, ys.length);
1684
+ if (n === 0) return [];
1685
+ const { min: xMin, max: xMax } = this.#xSeries.range;
1686
+ const { min: yMin, max: yMax } = this.#ySeries.range;
1687
+ return Array.from({ length: n }, (_, i) => ({
1688
+ x: padding + scaleValue(xs[i], xMin, xMax) * plotW,
1689
+ y: padding + (1 - scaleValue(ys[i], yMin, yMax)) * plotH,
1690
+ xVal: xs[i],
1691
+ yVal: ys[i],
1692
+ index: i,
1693
+ ageFraction: n > 1 ? i / (n - 1) : 1
1694
+ }));
1695
+ }
1696
+ /** Compute the final ColourResult for each xy point: base colour → age modification. */
1697
+ #computeColours(points, baseColour) {
1698
+ const config = {
1699
+ mode: this.colorMode,
1700
+ palette: parseColourScaleAttr(this.colorScale),
1701
+ easingName: this.colorEasing,
1702
+ valueMap: this.colorValueMap
1703
+ };
1704
+ return points.map((p) => {
1705
+ return applyAgeModification(computeColour(p.ageFraction, baseColour, config), p.ageFraction, this.ageStyle);
1706
+ });
1707
+ }
1708
+ #findNearestPoint(clientX, clientY, points, rect) {
1709
+ const x = clientX - rect.left;
1710
+ const y = clientY - rect.top;
1711
+ let best = null;
1712
+ let bestDist = Infinity;
1713
+ for (const p of points) {
1714
+ const dx = p.x - x;
1715
+ const dy = p.y - y;
1716
+ const d = dx * dx + dy * dy;
1717
+ if (d < bestDist) {
1718
+ bestDist = d;
1719
+ best = p;
1720
+ }
1721
+ }
1722
+ return best && Math.sqrt(bestDist) < 30 ? best : null;
1723
+ }
1724
+ #lastPoints;
1725
+ #lastClientX;
1726
+ #lastClientY;
1727
+ #updateTooltip(clientX, clientY) {
1728
+ if (!this.tooltips) return;
1729
+ const rect = this.getBoundingClientRect();
1730
+ const nearest = this.#findNearestPoint(clientX, clientY, this.#lastPoints, rect);
1731
+ if (!nearest) {
1732
+ hideTooltip(this);
1733
+ return;
1734
+ }
1735
+ showTooltip(this, clientX, clientY, this.valueFormat(nearest.xVal, nearest.yVal));
1736
+ }
1737
+ #updateTooltipAtLastPos() {
1738
+ if (this.#lastClientX === null || this.#lastClientY === null) return;
1739
+ this.#updateTooltip(this.#lastClientX, this.#lastClientY);
1740
+ }
1741
+ #onPointerMove;
1742
+ #onPointerLeave;
1743
+ #onClick;
1744
+ render() {
1745
+ const w = this._width;
1746
+ const h = this._height;
1747
+ const padding = Math.min(6, Math.min(w, h) * .06);
1748
+ const plotW = Math.max(1, w - padding * 2);
1749
+ const plotH = Math.max(1, h - padding * 2);
1750
+ const points = this.#computePoints(padding, plotW, plotH);
1751
+ this.#lastPoints = points;
1752
+ const baseColour = points.length > 0 ? readBaseColour(this) : ``;
1753
+ const colours = this.#computeColours(points, baseColour);
1754
+ const radius = adaptiveRadius(w, h, points.length);
1755
+ const textColor = `var(--plot-text, var(--surface-2-text, #fff))`;
1756
+ const dataContent = points.map((p, i) => {
1757
+ const cr = colours[i];
1758
+ const sizeFactor = this.ageStyle === `size` ? Math.max(.2, p.ageFraction) : 1;
1759
+ if (this.drawStyle === `circles`) {
1760
+ const r = radius * sizeFactor;
1761
+ return svg`<circle cx=${p.x} cy=${p.y} r=${r} fill=${cr.fill} opacity=${cr.opacity} />`;
1762
+ }
1763
+ const s = radius * 2 * sizeFactor;
1764
+ return svg`<rect x=${p.x - s / 2} y=${p.y - s / 2} width=${s} height=${s} fill=${cr.fill} opacity=${cr.opacity} />`;
1765
+ });
1766
+ const titleContent = this.title ? svg`<text
1767
+ x=${padding + 2} y=${padding + 2}
1768
+ class="overlay-text"
1769
+ fill=${textColor}
1770
+ style="opacity: 0.6"
1771
+ >${this.title}</text>` : svg``;
1772
+ const n = Math.min(this.#xSeries.values.length, this.#ySeries.values.length);
1773
+ const latestX = n > 0 ? this.#xSeries.values[n - 1] : null;
1774
+ const latestY = n > 0 ? this.#ySeries.values[n - 1] : null;
1775
+ const preciseContent = this.precise && latestX !== null && latestY !== null ? svg`<text
1776
+ x=${w - padding - 2} y=${padding + 2}
1777
+ class="overlay-text"
1778
+ text-anchor="end"
1779
+ fill=${textColor}
1780
+ style="opacity: 0.8"
1781
+ >${this.valueFormat(latestX, latestY)}</text>` : svg``;
1782
+ return html`
1783
+ <svg
1784
+ width=${w} height=${h}
1785
+ viewBox="0 0 ${w} ${h}"
1786
+ xmlns="http://www.w3.org/2000/svg"
1787
+ @pointermove=${this.#onPointerMove}
1788
+ @pointerleave=${this.#onPointerLeave}
1789
+ @click=${this.#onClick}
1790
+ >
1791
+ <rect width=${w} height=${h} fill="transparent" />
1792
+ ${dataContent}
1793
+ ${titleContent}
1794
+ ${preciseContent}
1795
+ </svg>
1796
+ `;
1797
+ }
1798
+ static {
1799
+ this.styles = css`
1800
+ :host {
1801
+ display: block;
1802
+ width: 100%;
1803
+ height: 100%;
1804
+ min-width: 40px;
1805
+ min-height: 40px;
1806
+ }
1807
+
1808
+ svg {
1809
+ display: block;
1810
+ cursor: crosshair;
1811
+ }
1812
+
1813
+ .overlay-text {
1814
+ font-family: var(--font-mono, ui-monospace, monospace);
1815
+ font-size: 9px;
1816
+ dominant-baseline: hanging;
1817
+ pointer-events: none;
1818
+ }
1819
+ `;
1820
+ }
1821
+ };
1822
+ __decorate([property({ attribute: `draw-style` })], PlotXyAxis.prototype, "drawStyle", void 0);
1823
+ __decorate([property({ attribute: `age-style` })], PlotXyAxis.prototype, "ageStyle", void 0);
1824
+ __decorate([property()], PlotXyAxis.prototype, "title", void 0);
1825
+ __decorate([property({ type: Boolean })], PlotXyAxis.prototype, "precise", void 0);
1826
+ __decorate([property({ type: Boolean })], PlotXyAxis.prototype, "tooltips", void 0);
1827
+ __decorate([property({ type: Number })], PlotXyAxis.prototype, "capacity", void 0);
1828
+ __decorate([property({
1829
+ type: Boolean,
1830
+ attribute: `persistent-scale`
1831
+ })], PlotXyAxis.prototype, "persistentScale", void 0);
1832
+ __decorate([property({
1833
+ type: Boolean,
1834
+ reflect: true
1835
+ })], PlotXyAxis.prototype, "paused", void 0);
1836
+ __decorate([property({
1837
+ type: Boolean,
1838
+ attribute: `click-pauses`
1839
+ })], PlotXyAxis.prototype, "clickPauses", void 0);
1840
+ __decorate([property({ attribute: `color-mode` })], PlotXyAxis.prototype, "colorMode", void 0);
1841
+ __decorate([property({ attribute: `color-easing` })], PlotXyAxis.prototype, "colorEasing", void 0);
1842
+ __decorate([property({ attribute: `color-scale` })], PlotXyAxis.prototype, "colorScale", void 0);
1843
+ __decorate([state()], PlotXyAxis.prototype, "_width", void 0);
1844
+ __decorate([state()], PlotXyAxis.prototype, "_height", void 0);
1845
+ PlotXyAxis = __decorate([safeCustomElement(`ixfx-plot-xy-axis`)], PlotXyAxis);
1846
+ //#endregion
1847
+ export { TooltipElement as a, computeColour as c, PlotMultiAxis as i, parseColourScaleAttr as l, PlotSingleAxis as n, PlotDataSeries as o, PlotHistogram as r, applyAgeModification as s, PlotXyAxis as t, readBaseColour as u };
1848
+
1849
+ //# sourceMappingURL=xy-axis-CxHC8z5E.js.map