@expressivecss/expressive 0.11.0 → 0.13.1

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 (265) hide show
  1. package/CHANGELOG.md +392 -7
  2. package/README.md +37 -1
  3. package/dist/css/expressive.css +5440 -234
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  8. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  9. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  10. package/dist/js/chunks/{chunk-AYYYWUJ4.mjs → chunk-3CLXLPYS.mjs} +17 -11
  11. package/dist/js/chunks/chunk-3CLXLPYS.mjs.map +7 -0
  12. package/dist/js/chunks/chunk-3DHK5CZU.mjs +105 -0
  13. package/dist/js/chunks/chunk-3DHK5CZU.mjs.map +7 -0
  14. package/dist/js/chunks/{chunk-IMUPRRI6.mjs → chunk-3VOK6J7U.mjs} +3 -3
  15. package/dist/js/chunks/chunk-4PWAPV6F.mjs +7 -0
  16. package/dist/js/chunks/chunk-4PWAPV6F.mjs.map +7 -0
  17. package/dist/js/chunks/{chunk-XFJ452AZ.mjs → chunk-55IA2XUC.mjs} +5 -6
  18. package/dist/js/chunks/{chunk-XFJ452AZ.mjs.map → chunk-55IA2XUC.mjs.map} +2 -2
  19. package/dist/js/chunks/{chunk-XDB3G6RL.mjs → chunk-6N2XAEFK.mjs} +15 -4
  20. package/dist/js/chunks/{chunk-XDB3G6RL.mjs.map → chunk-6N2XAEFK.mjs.map} +2 -2
  21. package/dist/js/chunks/{chunk-MISGHDUU.mjs → chunk-6OMRGML6.mjs} +9 -7
  22. package/dist/js/chunks/{chunk-MISGHDUU.mjs.map → chunk-6OMRGML6.mjs.map} +2 -2
  23. package/dist/js/chunks/{chunk-A3BJA7V4.mjs → chunk-6WLSDZ4O.mjs} +2 -2
  24. package/dist/js/chunks/chunk-CCWDV5CR.mjs +292 -0
  25. package/dist/js/chunks/chunk-CCWDV5CR.mjs.map +7 -0
  26. package/dist/js/chunks/{chunk-JODJAGFS.mjs → chunk-CKQP6P76.mjs} +31 -8
  27. package/dist/js/chunks/chunk-CKQP6P76.mjs.map +7 -0
  28. package/dist/js/chunks/{chunk-KR44BSYQ.mjs → chunk-DLWYKFEI.mjs} +3 -3
  29. package/dist/js/chunks/chunk-DTKR54I6.mjs +270 -0
  30. package/dist/js/chunks/chunk-DTKR54I6.mjs.map +7 -0
  31. package/dist/js/chunks/{chunk-SLFNPY5M.mjs → chunk-EC63CHXU.mjs} +2 -2
  32. package/dist/js/chunks/chunk-GVGT6MIU.mjs +47 -0
  33. package/dist/js/chunks/chunk-GVGT6MIU.mjs.map +7 -0
  34. package/dist/js/chunks/{chunk-LZVBJ33S.mjs → chunk-GZWPXXY6.mjs} +15 -8
  35. package/dist/js/chunks/chunk-GZWPXXY6.mjs.map +7 -0
  36. package/dist/js/chunks/chunk-HKT2S7VM.mjs +48 -0
  37. package/dist/js/chunks/chunk-HKT2S7VM.mjs.map +7 -0
  38. package/dist/js/chunks/{chunk-D4UZ4HFN.mjs → chunk-IFXVDDDH.mjs} +23 -11
  39. package/dist/js/chunks/chunk-IFXVDDDH.mjs.map +7 -0
  40. package/dist/js/chunks/chunk-K4CJCRGE.mjs +262 -0
  41. package/dist/js/chunks/chunk-K4CJCRGE.mjs.map +7 -0
  42. package/dist/js/chunks/chunk-LNCCYGPC.mjs +163 -0
  43. package/dist/js/chunks/chunk-LNCCYGPC.mjs.map +7 -0
  44. package/dist/js/chunks/{chunk-QSQ24FYT.mjs → chunk-LTWPGFBR.mjs} +2 -2
  45. package/dist/js/chunks/{chunk-QSQ24FYT.mjs.map → chunk-LTWPGFBR.mjs.map} +2 -2
  46. package/dist/js/chunks/{chunk-I5KR22BY.mjs → chunk-LWBQIX5J.mjs} +15 -15
  47. package/dist/js/chunks/chunk-LWBQIX5J.mjs.map +7 -0
  48. package/dist/js/chunks/{chunk-GY2Z5CJ2.mjs → chunk-NNYP33NB.mjs} +67 -32
  49. package/dist/js/chunks/chunk-NNYP33NB.mjs.map +7 -0
  50. package/dist/js/chunks/chunk-NQLC3YMW.mjs +1 -0
  51. package/dist/js/chunks/chunk-O5XPWXIQ.mjs +406 -0
  52. package/dist/js/chunks/chunk-O5XPWXIQ.mjs.map +7 -0
  53. package/dist/js/chunks/chunk-P3G3QHII.mjs +166 -0
  54. package/dist/js/chunks/chunk-P3G3QHII.mjs.map +7 -0
  55. package/dist/js/chunks/{chunk-VAS6JQ6P.mjs → chunk-PIJ2TAR3.mjs} +3 -3
  56. package/dist/js/chunks/{chunk-74N6U5JJ.mjs → chunk-PLYXLSH3.mjs} +38 -37
  57. package/dist/js/chunks/chunk-PLYXLSH3.mjs.map +7 -0
  58. package/dist/js/chunks/{chunk-PIIAEYAJ.mjs → chunk-QDAL4IYN.mjs} +2 -2
  59. package/dist/js/chunks/chunk-QKWPQK5N.mjs +179 -0
  60. package/dist/js/chunks/chunk-QKWPQK5N.mjs.map +7 -0
  61. package/dist/js/chunks/{chunk-FRB7TOCS.mjs → chunk-QV2JAWIZ.mjs} +2 -2
  62. package/dist/js/chunks/{chunk-N3UHGN3N.mjs → chunk-QVVEWBOF.mjs} +3 -3
  63. package/dist/js/chunks/chunk-RHRQKTTX.mjs +139 -0
  64. package/dist/js/chunks/chunk-RHRQKTTX.mjs.map +7 -0
  65. package/dist/js/chunks/chunk-SPETGK25.mjs +59 -0
  66. package/dist/js/chunks/chunk-SPETGK25.mjs.map +7 -0
  67. package/dist/js/chunks/{chunk-TK6PX2GC.mjs → chunk-U2OTHOTI.mjs} +131 -19
  68. package/dist/js/chunks/chunk-U2OTHOTI.mjs.map +7 -0
  69. package/dist/js/chunks/{chunk-TYM6U5DY.mjs → chunk-UAEAQBRC.mjs} +2 -2
  70. package/dist/js/chunks/{chunk-7UXHSYIA.mjs → chunk-UBLRRJ7N.mjs} +3 -3
  71. package/dist/js/chunks/{chunk-EP3CVKB5.mjs → chunk-UEZUJQ66.mjs} +8 -5
  72. package/dist/js/chunks/chunk-UEZUJQ66.mjs.map +7 -0
  73. package/dist/js/chunks/{chunk-JNPIWFJT.mjs → chunk-UNEMYWEW.mjs} +3 -3
  74. package/dist/js/chunks/chunk-VTLBW7QS.mjs +124 -0
  75. package/dist/js/chunks/chunk-VTLBW7QS.mjs.map +7 -0
  76. package/dist/js/chunks/{chunk-JKNPQQWY.mjs → chunk-WJXDBPXQ.mjs} +28 -12
  77. package/dist/js/chunks/chunk-WJXDBPXQ.mjs.map +7 -0
  78. package/dist/js/chunks/{chunk-CDW6WIRT.mjs → chunk-XGHORT7J.mjs} +18 -11
  79. package/dist/js/chunks/chunk-XGHORT7J.mjs.map +7 -0
  80. package/dist/js/chunks/{chunk-7GKXUG4B.mjs → chunk-XH6BEEMY.mjs} +78 -25
  81. package/dist/js/chunks/chunk-XH6BEEMY.mjs.map +7 -0
  82. package/dist/js/chunks/{chunk-XO5WX3TK.mjs → chunk-YDCS5S3J.mjs} +2 -2
  83. package/dist/js/expressive.cjs +2540 -233
  84. package/dist/js/expressive.cjs.map +4 -4
  85. package/dist/js/expressive.js +2540 -233
  86. package/dist/js/expressive.js.map +4 -4
  87. package/dist/js/expressive.min.js +10 -10
  88. package/dist/js/expressive.min.js.map +4 -4
  89. package/dist/js/expressive.mjs +86 -38
  90. package/dist/js/expressive.mjs.map +1 -1
  91. package/dist/js/internal/behaviors/bottomSheets.mjs +3 -3
  92. package/dist/js/internal/behaviors/dialogs.mjs +2 -2
  93. package/dist/js/internal/behaviors/forms.mjs +2 -2
  94. package/dist/js/internal/behaviors/sheetDrag.mjs +2 -2
  95. package/dist/js/internal/behaviors/sideSheets.mjs +3 -3
  96. package/dist/js/internal/components/appBar.mjs +2 -2
  97. package/dist/js/internal/components/autocomplete.mjs +4 -4
  98. package/dist/js/internal/components/barChart.mjs +10 -0
  99. package/dist/js/internal/components/barChart.mjs.map +7 -0
  100. package/dist/js/internal/components/buttonGroup.mjs +2 -2
  101. package/dist/js/internal/components/buttons.mjs +3 -3
  102. package/dist/js/internal/components/cards.mjs +3 -3
  103. package/dist/js/internal/components/carousel.mjs +3 -3
  104. package/dist/js/internal/components/characterCounter.mjs +2 -2
  105. package/dist/js/internal/components/chips.mjs +5 -5
  106. package/dist/js/internal/components/columnChart.mjs +9 -0
  107. package/dist/js/internal/components/columnChart.mjs.map +7 -0
  108. package/dist/js/internal/components/commandPalette.mjs +9 -0
  109. package/dist/js/internal/components/commandPalette.mjs.map +7 -0
  110. package/dist/js/internal/components/datepicker.mjs +6 -6
  111. package/dist/js/internal/components/expandingCard.mjs +3 -3
  112. package/dist/js/internal/components/heatmapChart.mjs +9 -0
  113. package/dist/js/internal/components/heatmapChart.mjs.map +7 -0
  114. package/dist/js/internal/components/index.mjs +79 -31
  115. package/dist/js/internal/components/lightbox.mjs +3 -3
  116. package/dist/js/internal/components/lineChart.mjs +12 -0
  117. package/dist/js/internal/components/lineChart.mjs.map +7 -0
  118. package/dist/js/internal/components/menu.mjs +3 -3
  119. package/dist/js/internal/components/messageScroller.mjs +9 -0
  120. package/dist/js/internal/components/messageScroller.mjs.map +7 -0
  121. package/dist/js/internal/components/mixedChart.mjs +10 -0
  122. package/dist/js/internal/components/mixedChart.mjs.map +7 -0
  123. package/dist/js/internal/components/navigationRail.mjs +2 -2
  124. package/dist/js/internal/components/pieChart.mjs +9 -0
  125. package/dist/js/internal/components/pieChart.mjs.map +7 -0
  126. package/dist/js/internal/components/questionnaire.mjs +9 -0
  127. package/dist/js/internal/components/questionnaire.mjs.map +7 -0
  128. package/dist/js/internal/components/radarChart.mjs +9 -0
  129. package/dist/js/internal/components/radarChart.mjs.map +7 -0
  130. package/dist/js/internal/components/radialChart.mjs +9 -0
  131. package/dist/js/internal/components/radialChart.mjs.map +7 -0
  132. package/dist/js/internal/components/registry.mjs +37 -25
  133. package/dist/js/internal/components/richTextEditor.mjs +9 -0
  134. package/dist/js/internal/components/richTextEditor.mjs.map +7 -0
  135. package/dist/js/internal/components/scrollspy.mjs +2 -2
  136. package/dist/js/internal/components/select.mjs +4 -4
  137. package/dist/js/internal/components/slider.mjs +3 -3
  138. package/dist/js/internal/components/snackbar.mjs +2 -2
  139. package/dist/js/internal/components/tabs.mjs +4 -4
  140. package/dist/js/internal/components/timepicker.mjs +4 -4
  141. package/dist/js/internal/components/tooltip.mjs +3 -3
  142. package/dist/js/internal/core/component.mjs +1 -1
  143. package/dist/js/internal/core/utils.mjs +1 -1
  144. package/dist/js/internal/plugins/dockedDisplayPlugin.mjs +2 -2
  145. package/dist/js/modular.mjs +86 -38
  146. package/dist/types/behaviors/forms.d.ts +22 -0
  147. package/dist/types/components/appBar.d.ts +1 -1
  148. package/dist/types/components/barChart.d.ts +24 -0
  149. package/dist/types/components/columnChart.d.ts +21 -0
  150. package/dist/types/components/commandPalette.d.ts +96 -0
  151. package/dist/types/components/datepicker.d.ts +5 -2
  152. package/dist/types/components/heatmapChart.d.ts +49 -0
  153. package/dist/types/components/index.d.ts +12 -0
  154. package/dist/types/components/lineChart.d.ts +134 -0
  155. package/dist/types/components/menu.d.ts +32 -0
  156. package/dist/types/components/messageScroller.d.ts +111 -0
  157. package/dist/types/components/mixedChart.d.ts +25 -0
  158. package/dist/types/components/pieChart.d.ts +40 -0
  159. package/dist/types/components/questionnaire.d.ts +96 -0
  160. package/dist/types/components/radarChart.d.ts +39 -0
  161. package/dist/types/components/radialChart.d.ts +45 -0
  162. package/dist/types/components/registry.d.ts +44 -0
  163. package/dist/types/components/richTextEditor.d.ts +71 -0
  164. package/dist/types/components/slider.d.ts +1 -0
  165. package/dist/types/components/snackbar.d.ts +6 -0
  166. package/dist/types/components/timepicker.d.ts +4 -1
  167. package/dist/types/components/tooltip.d.ts +2 -1
  168. package/dist/types/modular.d.ts +10 -1
  169. package/package.json +4 -1
  170. package/src/sass/README.md +3 -6
  171. package/src/sass/abstracts/_index.scss +1 -1
  172. package/src/sass/abstracts/_mixins.scss +226 -0
  173. package/src/sass/abstracts/_variables.scss +14 -0
  174. package/src/sass/base/_typography.scss +45 -0
  175. package/src/sass/components/_accordion.scss +161 -0
  176. package/src/sass/components/_aura.scss +119 -0
  177. package/src/sass/components/_avatar.scss +86 -0
  178. package/src/sass/components/_bar-chart.scss +285 -0
  179. package/src/sass/components/_bottom-sheet.scss +1 -0
  180. package/src/sass/components/_button-groups.scss +13 -3
  181. package/src/sass/components/_cards.scss +16 -13
  182. package/src/sass/components/_carousel.scss +9 -7
  183. package/src/sass/components/_column-chart.scss +134 -0
  184. package/src/sass/components/_command-palette.scss +140 -0
  185. package/src/sass/components/_countdown.scss +89 -0
  186. package/src/sass/components/_data-table.scss +188 -0
  187. package/src/sass/components/_dialog.scss +1 -0
  188. package/src/sass/components/_empty-state.scss +78 -0
  189. package/src/sass/components/_expanding-card.scss +3 -0
  190. package/src/sass/components/_heatmap-chart.scss +156 -0
  191. package/src/sass/components/_index.scss +27 -0
  192. package/src/sass/components/_kpi.scss +162 -0
  193. package/src/sass/components/_line-chart.scss +102 -0
  194. package/src/sass/components/_marquee.scss +111 -0
  195. package/src/sass/components/_menu.scss +10 -0
  196. package/src/sass/components/_message-scroller.scss +66 -0
  197. package/src/sass/components/_message.scss +202 -0
  198. package/src/sass/components/_mixed-chart.scss +157 -0
  199. package/src/sass/components/_navbar.scss +11 -6
  200. package/src/sass/components/_navigation-bar.scss +1 -0
  201. package/src/sass/components/_navigation-rail.scss +9 -0
  202. package/src/sass/components/_page-footer.scss +0 -11
  203. package/src/sass/components/_pie-chart.scss +203 -0
  204. package/src/sass/components/_popover.scss +112 -0
  205. package/src/sass/components/_progress.scss +25 -6
  206. package/src/sass/components/_questionnaire.scss +144 -0
  207. package/src/sass/components/_radar-chart.scss +205 -0
  208. package/src/sass/components/_radial-chart.scss +174 -0
  209. package/src/sass/components/_rich-text-editor.scss +99 -0
  210. package/src/sass/components/_search.scss +1 -0
  211. package/src/sass/components/_side-sheet.scss +1 -0
  212. package/src/sass/components/_skeleton.scss +81 -0
  213. package/src/sass/components/_snackbar.scss +2 -1
  214. package/src/sass/components/_stat.scss +86 -0
  215. package/src/sass/components/_stepper.scss +190 -0
  216. package/src/sass/components/_timeline.scss +114 -0
  217. package/src/sass/components/_timepicker.scss +2 -2
  218. package/src/sass/components/_toolbar.scss +2 -1
  219. package/src/sass/components/_tooltip.scss +2 -0
  220. package/src/sass/components/_tree.scss +139 -0
  221. package/src/sass/components/forms/_checkboxes.scss +2 -2
  222. package/src/sass/components/forms/_drop-zone.scss +128 -0
  223. package/src/sass/components/forms/_index.scss +2 -0
  224. package/src/sass/components/forms/_input-fields.scss +166 -15
  225. package/src/sass/components/forms/_radio-buttons.scss +2 -2
  226. package/src/sass/components/forms/_rating.scss +165 -0
  227. package/src/sass/components/forms/_slider.scss +51 -29
  228. package/src/sass/components/forms/_switches.scss +3 -3
  229. package/src/sass/tokens/_reference.scss +3 -15
  230. package/src/sass/utilities/_helpers.scss +14 -0
  231. package/src/sass/utilities/_index.scss +2 -0
  232. package/src/sass/utilities/_scroll-area.scss +35 -0
  233. package/src/sass/utilities/_shapes.scss +164 -0
  234. package/src/sass/utilities/_spacing.scss +29 -23
  235. package/src/sass/utilities/_visibility.scss +3 -2
  236. package/dist/js/chunks/chunk-74N6U5JJ.mjs.map +0 -7
  237. package/dist/js/chunks/chunk-7GKXUG4B.mjs.map +0 -7
  238. package/dist/js/chunks/chunk-AYYYWUJ4.mjs.map +0 -7
  239. package/dist/js/chunks/chunk-CDW6WIRT.mjs.map +0 -7
  240. package/dist/js/chunks/chunk-D4UZ4HFN.mjs.map +0 -7
  241. package/dist/js/chunks/chunk-EP3CVKB5.mjs.map +0 -7
  242. package/dist/js/chunks/chunk-GY2Z5CJ2.mjs.map +0 -7
  243. package/dist/js/chunks/chunk-I5KR22BY.mjs.map +0 -7
  244. package/dist/js/chunks/chunk-JKNPQQWY.mjs.map +0 -7
  245. package/dist/js/chunks/chunk-JODJAGFS.mjs.map +0 -7
  246. package/dist/js/chunks/chunk-JR24UB4X.mjs +0 -100
  247. package/dist/js/chunks/chunk-JR24UB4X.mjs.map +0 -7
  248. package/dist/js/chunks/chunk-LZVBJ33S.mjs.map +0 -7
  249. package/dist/js/chunks/chunk-PMBPAFAS.mjs +0 -7
  250. package/dist/js/chunks/chunk-PMBPAFAS.mjs.map +0 -7
  251. package/dist/js/chunks/chunk-TK6PX2GC.mjs.map +0 -7
  252. package/dist/js/chunks/chunk-WWPAPHMA.mjs +0 -1
  253. /package/dist/js/chunks/{chunk-IMUPRRI6.mjs.map → chunk-3VOK6J7U.mjs.map} +0 -0
  254. /package/dist/js/chunks/{chunk-A3BJA7V4.mjs.map → chunk-6WLSDZ4O.mjs.map} +0 -0
  255. /package/dist/js/chunks/{chunk-KR44BSYQ.mjs.map → chunk-DLWYKFEI.mjs.map} +0 -0
  256. /package/dist/js/chunks/{chunk-SLFNPY5M.mjs.map → chunk-EC63CHXU.mjs.map} +0 -0
  257. /package/dist/js/chunks/{chunk-WWPAPHMA.mjs.map → chunk-NQLC3YMW.mjs.map} +0 -0
  258. /package/dist/js/chunks/{chunk-VAS6JQ6P.mjs.map → chunk-PIJ2TAR3.mjs.map} +0 -0
  259. /package/dist/js/chunks/{chunk-PIIAEYAJ.mjs.map → chunk-QDAL4IYN.mjs.map} +0 -0
  260. /package/dist/js/chunks/{chunk-FRB7TOCS.mjs.map → chunk-QV2JAWIZ.mjs.map} +0 -0
  261. /package/dist/js/chunks/{chunk-N3UHGN3N.mjs.map → chunk-QVVEWBOF.mjs.map} +0 -0
  262. /package/dist/js/chunks/{chunk-TYM6U5DY.mjs.map → chunk-UAEAQBRC.mjs.map} +0 -0
  263. /package/dist/js/chunks/{chunk-7UXHSYIA.mjs.map → chunk-UBLRRJ7N.mjs.map} +0 -0
  264. /package/dist/js/chunks/{chunk-JNPIWFJT.mjs.map → chunk-UNEMYWEW.mjs.map} +0 -0
  265. /package/dist/js/chunks/{chunk-XO5WX3TK.mjs.map → chunk-YDCS5S3J.mjs.map} +0 -0
@@ -0,0 +1,406 @@
1
+ import {
2
+ Component
3
+ } from "./chunk-LTWPGFBR.mjs";
4
+
5
+ // src/ts/components/lineChart.ts
6
+ var _defaults = {
7
+ min: null,
8
+ max: null
9
+ };
10
+ var SVG = "http://www.w3.org/2000/svg";
11
+ var COLORS = 4;
12
+ function svgNode(name, attributes = {}) {
13
+ const element = document.createElementNS(SVG, name);
14
+ for (const key in attributes) element.setAttribute(key, attributes[key]);
15
+ return element;
16
+ }
17
+ var _gradientId = 0;
18
+ function cellValue(cell) {
19
+ if (!cell) return null;
20
+ const raw = cell.dataset.value ?? cell.textContent;
21
+ const text = raw.replace(/\u2212/g, "-").replace(/[\s%\p{Sc}]/gu, "");
22
+ if (!/^[+-]?(\d{1,3}(,\d{3})+|\d+)?(\.\d+)?(e[+-]?\d+)?$/i.test(text) || !/\d/.test(text)) return null;
23
+ return parseFloat(text.replace(/,/g, ""));
24
+ }
25
+ function monotonePath(points) {
26
+ const n = points.length;
27
+ if (n === 0) return "";
28
+ if (n === 1) return `M${points[0][0]},${points[0][1]}h0`;
29
+ const slopes = [];
30
+ for (let i = 0; i < n - 1; i++) {
31
+ slopes.push((points[i + 1][1] - points[i][1]) / (points[i + 1][0] - points[i][0]));
32
+ }
33
+ const tangents = points.map((_, i) => {
34
+ if (i === 0) return slopes[0];
35
+ if (i === n - 1) return slopes[n - 2];
36
+ const [a, b] = [slopes[i - 1], slopes[i]];
37
+ return a * b <= 0 ? 0 : 2 / (1 / a + 1 / b);
38
+ });
39
+ let d = `M${points[0][0]},${points[0][1]}`;
40
+ for (let i = 0; i < n - 1; i++) {
41
+ const [[x0, y0], [x1, y1]] = [points[i], points[i + 1]];
42
+ const h = (x1 - x0) / 3;
43
+ d += `C${x0 + h},${y0 + tangents[i] * h} ${x1 - h},${y1 - tangents[i + 1] * h} ${x1},${y1}`;
44
+ }
45
+ return d;
46
+ }
47
+ var LineChart = class extends Component {
48
+ constructor(el, options) {
49
+ super(el, options, new.target);
50
+ /** The values' x positions and series, read from the table. */
51
+ this.labels = [];
52
+ this.series = [];
53
+ /** The highlighted row, or -1. */
54
+ this.activeIndex = -1;
55
+ this._generated = [];
56
+ /** The table's header cells and body rows, as read. */
57
+ this._header = [];
58
+ this._rows = [];
59
+ /**
60
+ * Whether the rows run right to left: a horizontal row axis in a
61
+ * right-to-left context, like the labels. Bar chart rows keep their order.
62
+ */
63
+ this._reversed = false;
64
+ this._onPointerMove = (e) => {
65
+ this.show(this._indexAt(e));
66
+ };
67
+ this._onFocus = () => {
68
+ if (this.activeIndex < 0 && this._plot.matches(":focus-visible")) this.show(0);
69
+ };
70
+ this._onLeave = (e) => {
71
+ if (e.pointerType === "touch" || this._plot.matches(":focus-visible")) return;
72
+ this.show(-1);
73
+ };
74
+ /** The browser took the touch to scroll the page. */
75
+ this._onCancel = () => {
76
+ this.show(-1);
77
+ };
78
+ this._onKeyDown = (e) => {
79
+ const next = this._keyTarget(e.key);
80
+ if (next === void 0) return;
81
+ e.preventDefault();
82
+ this.show(next);
83
+ };
84
+ this.el[this._key] = this;
85
+ this.options = {
86
+ ...new.target.defaults,
87
+ ...options
88
+ };
89
+ this._read();
90
+ if (!this.labels.length || !this.series.length) return;
91
+ this._reversed = !this._horizontal && getComputedStyle(this.el).direction === "rtl";
92
+ this._render();
93
+ if (!this.el.classList.contains("sparkline")) {
94
+ this._plot.tabIndex = 0;
95
+ this._plot.addEventListener("pointerdown", this._onPointerMove);
96
+ this._plot.addEventListener("pointermove", this._onPointerMove);
97
+ this._plot.addEventListener("pointerleave", this._onLeave);
98
+ this._plot.addEventListener("pointercancel", this._onCancel);
99
+ this._plot.addEventListener("keydown", this._onKeyDown);
100
+ this._plot.addEventListener("focus", this._onFocus);
101
+ this._plot.addEventListener("blur", this._onLeave);
102
+ }
103
+ }
104
+ /** The class prefix of the generated parts; ColumnChart reuses this class. */
105
+ get _type() {
106
+ return "line-chart";
107
+ }
108
+ /** Whether the y axis always includes zero, as columns grow from it. */
109
+ get _fromZero() {
110
+ return false;
111
+ }
112
+ /**
113
+ * Whether the rows run down the plot and the values across it, as in a
114
+ * bar chart. The rows then keep their order in a right-to-left context.
115
+ */
116
+ get _horizontal() {
117
+ return false;
118
+ }
119
+ /**
120
+ * Whether the rows go round a circle, as in a pie or radar chart. The
121
+ * arrows then step round it, wrapping from the last row to the first.
122
+ */
123
+ get _circular() {
124
+ return false;
125
+ }
126
+ /** Whether the cursor marks each series with a point. */
127
+ get _points() {
128
+ return true;
129
+ }
130
+ get _key() {
131
+ return "Expressive_LineChart";
132
+ }
133
+ static get defaults() {
134
+ return _defaults;
135
+ }
136
+ static init(els, options = {}) {
137
+ return super.init(els, options, this);
138
+ }
139
+ static getInstance(el) {
140
+ return el["Expressive_LineChart"];
141
+ }
142
+ destroy() {
143
+ this._generated.forEach((node) => node.remove());
144
+ this._generated = [];
145
+ this.el[this._key] = void 0;
146
+ }
147
+ /**
148
+ * Highlights one row: moves the cursor and fills the tooltip.
149
+ * Pass -1 to hide them. Does nothing for a chart without data.
150
+ */
151
+ show(index) {
152
+ if (!this._tooltip || index === this.activeIndex) return;
153
+ this.activeIndex = index;
154
+ const hidden = index < 0 || index >= this.labels.length;
155
+ this._cursor.hidden = this._tooltip.hidden = hidden;
156
+ if (hidden) return;
157
+ this._cursor.replaceChildren();
158
+ const title = document.createElement("strong");
159
+ title.textContent = this.labels[index];
160
+ this._tooltip.replaceChildren(title);
161
+ this.series.forEach((series, i) => {
162
+ const value = series.values[index];
163
+ if (value === null) return;
164
+ const color = String(i % COLORS + 1);
165
+ const top = series.tops[index];
166
+ if (top !== null && this._points) {
167
+ const dot = document.createElement("span");
168
+ dot.className = series.className;
169
+ dot.dataset.series = color;
170
+ dot.style.top = `${series.y(top)}%`;
171
+ this._cursor.append(dot);
172
+ }
173
+ const name = document.createElement("span");
174
+ name.dataset.series = color;
175
+ name.textContent = series.name;
176
+ const text = document.createElement("span");
177
+ text.textContent = series.text[index];
178
+ this._tooltip.append(name, text);
179
+ });
180
+ this._place(index);
181
+ }
182
+ /**
183
+ * Moves the filled cursor and tooltip to a row: beside its band, with the
184
+ * tooltip opening back over the plot past the middle.
185
+ */
186
+ _place(index) {
187
+ const x = this._x(index);
188
+ const side = this._horizontal ? "top" : "left";
189
+ this._cursor.style[side] = this._tooltip.style[side] = `${x}%`;
190
+ this._tooltip.classList.toggle("end", x > 50);
191
+ }
192
+ _read() {
193
+ const table = this.el.querySelector(":scope > table");
194
+ if (!table) return;
195
+ const headerRow = table.tHead?.rows[0] ?? table.rows[0];
196
+ const header = Array.from(headerRow?.cells ?? []);
197
+ const rows = Array.from(table.tBodies).flatMap((body) => Array.from(body.rows)).filter((row) => row !== headerRow);
198
+ [this._header, this._rows] = [header, rows];
199
+ this.labels = rows.map((row) => row.cells[0]?.textContent.trim() ?? "");
200
+ this.series = header.slice(1).map((cell, i) => {
201
+ const values = rows.map((row) => cellValue(row.cells[i + 1]));
202
+ return {
203
+ name: cell.textContent.trim(),
204
+ className: cell.className,
205
+ text: rows.map((row) => row.cells[i + 1]?.textContent.trim() ?? ""),
206
+ values,
207
+ tops: values,
208
+ y: void 0
209
+ };
210
+ });
211
+ const stacked = this.el.classList.contains("stacked");
212
+ if (stacked) {
213
+ const totals = this.labels.map(() => 0);
214
+ for (const series of this.series) {
215
+ series.base = [...totals];
216
+ series.tops = series.values.map((value, i) => totals[i] = value === null || totals[i] === null ? null : totals[i] + value);
217
+ }
218
+ }
219
+ if (this.series.every((series) => series.tops.every((value) => value === null))) this.series = [];
220
+ const option = (name) => this._number(name) ?? this.options[name];
221
+ this._y = this._scale(this.series, option("min"), option("max"));
222
+ }
223
+ /** A number from one of the chart's data attributes, or null. */
224
+ _number(name) {
225
+ const value = parseFloat(this.el.dataset[name] ?? "");
226
+ return Number.isFinite(value) ? value : null;
227
+ }
228
+ /**
229
+ * Fits a y scale to the series' tops, between `min` and `max` where they
230
+ * are set, and gives it to each series.
231
+ */
232
+ _scale(series, min, max, fromZero = this._fromZero) {
233
+ const stacked = this.el.classList.contains("stacked");
234
+ let [low, high] = [Infinity, -Infinity];
235
+ for (const { tops } of series) {
236
+ for (const value of tops) {
237
+ if (value === null) continue;
238
+ low = Math.min(low, value);
239
+ high = Math.max(high, value);
240
+ }
241
+ }
242
+ if (low > high) low = high = 0;
243
+ if (fromZero) {
244
+ const [bottom, top] = [Math.min(0, low), Math.max(0, high)];
245
+ const room = (top - (min ?? bottom)) * 0.1 || 1;
246
+ min ?? (min = bottom < 0 ? bottom - room : 0);
247
+ max ?? (max = top + room);
248
+ } else {
249
+ const pad = (high - low) * 0.1 || 1;
250
+ min ?? (min = stacked ? Math.min(0, low) : low - pad);
251
+ max ?? (max = high + (stacked ? (high - min) * 0.1 || 1 : pad));
252
+ }
253
+ const y = (value) => 100 - (value - min) / (max - min || 1) * 100;
254
+ for (const each of series) each.y = y;
255
+ return y;
256
+ }
257
+ /** Each row sits in the middle of an equal band, beside its label. */
258
+ _x(index) {
259
+ const x = (index + 0.5) / this.labels.length * 100;
260
+ return this._reversed ? 100 - x : x;
261
+ }
262
+ _render() {
263
+ const type = this._type;
264
+ this._plot = document.createElement("div");
265
+ this._plot.className = `${type}-plot`;
266
+ const caption = this.el.querySelector(":scope > figcaption")?.textContent.trim();
267
+ this._plot.setAttribute("role", "group");
268
+ this._plot.setAttribute("aria-roledescription", type.replace(/-/g, " "));
269
+ if (caption) this._plot.setAttribute("aria-label", caption);
270
+ this._cursor = document.createElement("div");
271
+ this._cursor.className = `${type}-cursor`;
272
+ this._cursor.hidden = true;
273
+ this._tooltip = document.createElement("div");
274
+ this._tooltip.className = `${type}-tooltip`;
275
+ this._tooltip.hidden = true;
276
+ this._tooltip.setAttribute("aria-live", "polite");
277
+ this._plot.append(this._draw(), this._cursor, this._tooltip);
278
+ this._generated = [this._plot, ...this._guides()];
279
+ this.el.append(...this._generated);
280
+ }
281
+ /**
282
+ * The <tfoot> row as a total to show over the plot, its value over its
283
+ * label, or null without one.
284
+ */
285
+ _total(className) {
286
+ const footer = this.el.querySelector(":scope > table")?.tFoot?.rows[0];
287
+ if (!footer) return null;
288
+ const total = document.createElement("div");
289
+ total.className = className;
290
+ const [label, value] = [document.createElement("span"), document.createElement("strong")];
291
+ label.textContent = footer.cells[0]?.textContent.trim() ?? "";
292
+ value.textContent = footer.cells[1]?.textContent.trim() ?? "";
293
+ total.append(value, label);
294
+ return total;
295
+ }
296
+ /** The parts below the plot: the row labels and the legend. */
297
+ _guides() {
298
+ const labels = document.createElement("ol");
299
+ labels.className = `${this._type}-labels`;
300
+ labels.setAttribute("aria-hidden", "true");
301
+ for (const text of this.labels) {
302
+ const li = document.createElement("li");
303
+ li.textContent = text;
304
+ labels.append(li);
305
+ }
306
+ return [labels, ...this._legend()];
307
+ }
308
+ /** The series legend, for more than one series. */
309
+ _legend() {
310
+ if (this.series.length < 2) return [];
311
+ const legend = document.createElement("ul");
312
+ legend.className = `${this._type}-legend`;
313
+ legend.setAttribute("aria-hidden", "true");
314
+ legend.append(...this.series.map((series, i) => {
315
+ const li = document.createElement("li");
316
+ li.dataset.series = String(i % COLORS + 1);
317
+ li.textContent = series.name;
318
+ return li;
319
+ }));
320
+ return [legend];
321
+ }
322
+ /** Draws the series, which fill the plot. */
323
+ _draw(drawn = this.series) {
324
+ const svg = document.createElementNS(SVG, "svg");
325
+ svg.setAttribute("viewBox", "0 0 100 100");
326
+ svg.setAttribute("preserveAspectRatio", "none");
327
+ svg.setAttribute("aria-hidden", "true");
328
+ svg.setAttribute("focusable", "false");
329
+ for (const series of drawn) {
330
+ const group = document.createElementNS(SVG, "g");
331
+ group.setAttribute("class", `${this._type}-series ${series.className}`.trim());
332
+ group.dataset.series = String(this.series.indexOf(series) % COLORS + 1);
333
+ const runs = [[]];
334
+ series.tops.forEach((value, index) => {
335
+ if (value === null) runs.push([]);
336
+ else runs[runs.length - 1].push(index);
337
+ });
338
+ const filled = runs.filter((run) => run.length);
339
+ const points = (run, values) => run.map((index) => [this._x(index), series.y(values[index])]);
340
+ const tops = filled.map((run) => monotonePath(points(run, series.tops)));
341
+ const id = `line-chart-gradient-${++_gradientId}`;
342
+ const gradient = document.createElementNS(SVG, "linearGradient");
343
+ gradient.id = id;
344
+ gradient.setAttribute("x2", "0");
345
+ gradient.setAttribute("y2", "1");
346
+ gradient.append(document.createElementNS(SVG, "stop"), document.createElementNS(SVG, "stop"));
347
+ gradient.lastElementChild.setAttribute("offset", "1");
348
+ const area = document.createElementNS(SVG, "path");
349
+ area.setAttribute("class", `${this._type}-area`);
350
+ area.setAttribute("fill", `url(#${id})`);
351
+ area.setAttribute("d", filled.map((run, r) => {
352
+ if (!series.base) return `${tops[r]}V100H${this._x(run[0])}Z`;
353
+ const base = points(run, series.base).map(([x, y]) => [x, Math.min(100, y)]);
354
+ return `${tops[r]}L${monotonePath(base.reverse()).slice(1)}Z`;
355
+ }).join(""));
356
+ const line = document.createElementNS(SVG, "path");
357
+ line.setAttribute("class", `${this._type}-line`);
358
+ line.setAttribute("d", tops.join(""));
359
+ group.append(gradient, area, line);
360
+ svg.append(group);
361
+ }
362
+ return svg;
363
+ }
364
+ /** The row under the pointer, or -1. */
365
+ _indexAt(e) {
366
+ const box = this._plot.getBoundingClientRect();
367
+ const fraction = this._horizontal ? (e.clientY - box.top) / box.height : (e.clientX - box.left) / box.width;
368
+ const index = Math.floor((this._reversed ? 1 - fraction : fraction) * this.labels.length);
369
+ return Math.min(this.labels.length - 1, Math.max(0, index));
370
+ }
371
+ /**
372
+ * For a circular chart, the pointer's position from the plot's middle: a
373
+ * fraction of a turn clockwise from the top, and its distance as a share
374
+ * of the plot's width.
375
+ */
376
+ _polarAt(e) {
377
+ const box = this._plot.getBoundingClientRect();
378
+ const [x, y] = [e.clientX - box.left - box.width / 2, e.clientY - box.top - box.height / 2];
379
+ return [(Math.atan2(x, -y) / (2 * Math.PI) + 1) % 1, Math.hypot(x, y) / box.width];
380
+ }
381
+ /** The keys that move to the next row and to the one before. */
382
+ get _arrows() {
383
+ if (this._circular) return [["ArrowRight", "ArrowDown"], ["ArrowLeft", "ArrowUp"]];
384
+ if (this._horizontal) return [["ArrowDown"], ["ArrowUp"]];
385
+ return this._reversed ? [["ArrowLeft"], ["ArrowRight"]] : [["ArrowRight"], ["ArrowLeft"]];
386
+ }
387
+ /** The index a key moves to, or undefined for a key the chart ignores. */
388
+ _keyTarget(key) {
389
+ const last = this.labels.length - 1;
390
+ const [forward, back] = this._arrows;
391
+ const step = (by) => {
392
+ if (!this._circular) return Math.min(last, Math.max(0, this.activeIndex + by));
393
+ if (this.activeIndex < 0) return by > 0 ? 0 : last;
394
+ return (this.activeIndex + by + last + 1) % (last + 1);
395
+ };
396
+ if (key === "Escape") return this.activeIndex < 0 ? void 0 : -1;
397
+ return forward.includes(key) ? step(1) : back.includes(key) ? step(-1) : { Home: 0, End: last }[key];
398
+ }
399
+ };
400
+
401
+ export {
402
+ COLORS,
403
+ svgNode,
404
+ LineChart
405
+ };
406
+ //# sourceMappingURL=chunk-O5XPWXIQ.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ts/components/lineChart.ts"],
4
+ "sourcesContent": ["import { Component, BaseOptions, InitElements, InitElement } from '../core/component';\n\nexport interface LineChartOptions extends BaseOptions {\n /**\n * Lowest value on the y axis. Defaults to the data minimum less 10% of\n * the range; a stacked chart starts at zero or its lowest total, and a\n * column chart always includes zero. Overridden by `data-min` on the chart.\n */\n min: number | null;\n /**\n * Highest value on the y axis. Defaults to the data maximum plus 10% of\n * the range. Overridden by `data-max` on the chart.\n */\n max: number | null;\n}\n\nconst _defaults: LineChartOptions = {\n min: null,\n max: null\n};\n\nconst SVG = 'http://www.w3.org/2000/svg';\nexport const COLORS = 4;\n\n/** An SVG element with its attributes. */\nexport function svgNode(name: string, attributes: Record<string, string> = {}): SVGElement {\n const element = document.createElementNS(SVG, name);\n for (const key in attributes) element.setAttribute(key, attributes[key]);\n return element;\n}\nlet _gradientId = 0;\n\nexport interface Series {\n name: string;\n /** Header cell classes, such as `dashed`. */\n className: string;\n /** Cell text, shown in the tooltip as the author formatted it. */\n text: string[];\n values: (number | null)[];\n /**\n * Where each value is drawn: the value, or in a stacked chart the running\n * total, which is unknown (null) above a gap.\n */\n tops: (number | null)[];\n /** In a stacked chart, the total of the series below, which this area fills down to. */\n base?: (number | null)[];\n /** The series' y scale: a value's distance down the plot, in percent. */\n y: (value: number) => number;\n}\n\n/**\n * Reads a cell as a number. `data-value` wins over the text, which may carry\n * currency or percent signs, grouping commas and a typographic minus around\n * one number. Anything else, such as \"(1,200)\" or \"3\u20135\", is a gap rather\n * than a guess.\n */\nfunction cellValue(cell: Element | undefined): number | null {\n if (!cell) return null;\n const raw = (cell as HTMLElement).dataset.value ?? cell.textContent;\n const text = raw.replace(/\\u2212/g, '-').replace(/[\\s%\\p{Sc}]/gu, '');\n if (!/^[+-]?(\\d{1,3}(,\\d{3})+|\\d+)?(\\.\\d+)?(e[+-]?\\d+)?$/i.test(text) || !/\\d/.test(text)) return null;\n return parseFloat(text.replace(/,/g, ''));\n}\n\n/**\n * A monotone cubic through the points (Fritsch-Butland tangents), so the\n * curve never overshoots a data point. Bezier curves keep their shape under\n * the plot's non-uniform scaling, so this can be drawn in a 0-100 box.\n */\nfunction monotonePath(points: [number, number][]): string {\n const n = points.length;\n if (n === 0) return '';\n if (n === 1) return `M${points[0][0]},${points[0][1]}h0`;\n const slopes: number[] = [];\n for (let i = 0; i < n - 1; i++) {\n slopes.push((points[i + 1][1] - points[i][1]) / (points[i + 1][0] - points[i][0]));\n }\n const tangents = points.map((_, i) => {\n if (i === 0) return slopes[0];\n if (i === n - 1) return slopes[n - 2];\n const [a, b] = [slopes[i - 1], slopes[i]];\n return a * b <= 0 ? 0 : 2 / (1 / a + 1 / b);\n });\n let d = `M${points[0][0]},${points[0][1]}`;\n for (let i = 0; i < n - 1; i++) {\n const [[x0, y0], [x1, y1]] = [points[i], points[i + 1]];\n const h = (x1 - x0) / 3;\n d += `C${x0 + h},${y0 + tangents[i] * h} ${x1 - h},${y1 - tangents[i + 1] * h} ${x1},${y1}`;\n }\n return d;\n}\n\n/**\n * Line chart. The data is the chart's <table>; this class draws it as an SVG\n * with x labels, a legend for more than one series, and a cursor and tooltip\n * that follow the pointer or the arrow keys. Styling is CSS.\n *\n * Re-run `LineChart.init(el)` after changing the table.\n */\nexport class LineChart extends Component<LineChartOptions> {\n /** The values' x positions and series, read from the table. */\n labels: string[] = [];\n series: Series[] = [];\n /** The highlighted row, or -1. */\n activeIndex = -1;\n private _generated: Element[] = [];\n /** The table's header cells and body rows, as read. */\n protected _header: HTMLTableCellElement[] = [];\n protected _rows: HTMLTableRowElement[] = [];\n protected _plot: HTMLElement;\n protected _cursor: HTMLElement;\n protected _tooltip: HTMLElement;\n protected _y: (value: number) => number;\n /**\n * Whether the rows run right to left: a horizontal row axis in a\n * right-to-left context, like the labels. Bar chart rows keep their order.\n */\n protected _reversed = false;\n\n /** The class prefix of the generated parts; ColumnChart reuses this class. */\n protected get _type() {\n return 'line-chart';\n }\n\n /** Whether the y axis always includes zero, as columns grow from it. */\n protected get _fromZero() {\n return false;\n }\n\n /**\n * Whether the rows run down the plot and the values across it, as in a\n * bar chart. The rows then keep their order in a right-to-left context.\n */\n protected get _horizontal() {\n return false;\n }\n\n /**\n * Whether the rows go round a circle, as in a pie or radar chart. The\n * arrows then step round it, wrapping from the last row to the first.\n */\n protected get _circular() {\n return false;\n }\n\n /** Whether the cursor marks each series with a point. */\n protected get _points() {\n return true;\n }\n\n protected get _key() {\n return 'Expressive_LineChart';\n }\n\n constructor(el: HTMLElement, options: Partial<LineChartOptions>) {\n super(el, options, new.target);\n this.el[this._key] = this;\n\n this.options = {\n ...new.target.defaults,\n ...options\n };\n\n this._read();\n if (!this.labels.length || !this.series.length) return;\n this._reversed = !this._horizontal && getComputedStyle(this.el).direction === 'rtl';\n this._render();\n if (!this.el.classList.contains('sparkline')) {\n this._plot.tabIndex = 0;\n // A tap has no move before it, so the press picks the row too.\n this._plot.addEventListener('pointerdown', this._onPointerMove);\n this._plot.addEventListener('pointermove', this._onPointerMove);\n this._plot.addEventListener('pointerleave', this._onLeave);\n this._plot.addEventListener('pointercancel', this._onCancel);\n this._plot.addEventListener('keydown', this._onKeyDown);\n this._plot.addEventListener('focus', this._onFocus);\n this._plot.addEventListener('blur', this._onLeave);\n }\n }\n\n static get defaults(): LineChartOptions {\n return _defaults;\n }\n\n static init(el: HTMLElement, options?: Partial<LineChartOptions>): LineChart;\n static init(els: InitElements<InitElement>, options?: Partial<LineChartOptions>): LineChart[];\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<LineChartOptions> = {}\n ): LineChart | LineChart[] {\n return super.init(els, options, this);\n }\n\n static getInstance(el: HTMLElement): LineChart {\n return el['Expressive_LineChart'];\n }\n\n destroy() {\n // The listeners live on the plot, which goes with the generated nodes.\n this._generated.forEach((node) => node.remove());\n this._generated = [];\n this.el[this._key] = undefined;\n }\n\n /**\n * Highlights one row: moves the cursor and fills the tooltip.\n * Pass -1 to hide them. Does nothing for a chart without data.\n */\n show(index: number) {\n // Skipping a repeat also keeps the live tooltip from re-announcing.\n if (!this._tooltip || index === this.activeIndex) return;\n this.activeIndex = index;\n const hidden = index < 0 || index >= this.labels.length;\n this._cursor.hidden = this._tooltip.hidden = hidden;\n if (hidden) return;\n\n this._cursor.replaceChildren();\n const title = document.createElement('strong');\n title.textContent = this.labels[index];\n this._tooltip.replaceChildren(title);\n\n this.series.forEach((series, i) => {\n const value = series.values[index];\n if (value === null) return;\n const color = String((i % COLORS) + 1);\n const top = series.tops[index];\n if (top !== null && this._points) {\n const dot = document.createElement('span');\n dot.className = series.className;\n dot.dataset.series = color;\n dot.style.top = `${series.y(top)}%`;\n this._cursor.append(dot);\n }\n const name = document.createElement('span');\n name.dataset.series = color;\n name.textContent = series.name;\n const text = document.createElement('span');\n text.textContent = series.text[index];\n this._tooltip.append(name, text);\n });\n this._place(index);\n }\n\n /**\n * Moves the filled cursor and tooltip to a row: beside its band, with the\n * tooltip opening back over the plot past the middle.\n */\n protected _place(index: number) {\n const x = this._x(index);\n const side = this._horizontal ? 'top' : 'left';\n this._cursor.style[side] = this._tooltip.style[side] = `${x}%`;\n this._tooltip.classList.toggle('end', x > 50);\n }\n\n private _read() {\n const table = this.el.querySelector<HTMLTableElement>(':scope > table');\n if (!table) return;\n // Without a <thead> the parser puts the header row in the first <tbody>.\n const headerRow = table.tHead?.rows[0] ?? table.rows[0];\n const header = Array.from(headerRow?.cells ?? []);\n const rows = Array.from(table.tBodies)\n .flatMap((body) => Array.from(body.rows))\n .filter((row) => row !== headerRow);\n [this._header, this._rows] = [header, rows];\n this.labels = rows.map((row) => row.cells[0]?.textContent.trim() ?? '');\n this.series = header.slice(1).map((cell, i) => {\n const values = rows.map((row) => cellValue(row.cells[i + 1]));\n return {\n name: cell.textContent.trim(),\n className: cell.className,\n text: rows.map((row) => row.cells[i + 1]?.textContent.trim() ?? ''),\n values,\n tops: values,\n y: undefined\n };\n });\n const stacked = this.el.classList.contains('stacked');\n if (stacked) {\n // A gap leaves the total unknown, so the series above break there too.\n // ponytail: negative values overlap the band below; diverging stacks if needed.\n const totals: (number | null)[] = this.labels.map(() => 0);\n for (const series of this.series) {\n series.base = [...totals];\n series.tops = series.values.map((value, i) =>\n (totals[i] = value === null || totals[i] === null ? null : totals[i] + value));\n }\n }\n\n if (this.series.every((series) => series.tops.every((value) => value === null))) this.series = [];\n const option = (name: 'min' | 'max') => this._number(name) ?? this.options[name];\n this._y = this._scale(this.series, option('min'), option('max'));\n }\n\n /** A number from one of the chart's data attributes, or null. */\n protected _number(name: string) {\n const value = parseFloat(this.el.dataset[name] ?? '');\n return Number.isFinite(value) ? value : null;\n }\n\n /**\n * Fits a y scale to the series' tops, between `min` and `max` where they\n * are set, and gives it to each series.\n */\n protected _scale(series: Series[], min: number | null, max: number | null, fromZero = this._fromZero) {\n const stacked = this.el.classList.contains('stacked');\n let [low, high] = [Infinity, -Infinity];\n for (const { tops } of series) {\n for (const value of tops) {\n if (value === null) continue;\n low = Math.min(low, value);\n high = Math.max(high, value);\n }\n }\n if (low > high) low = high = 0;\n if (fromZero) {\n // Zero is on the scale, with 10% of the span to spare on each side\n // that has data.\n const [bottom, top] = [Math.min(0, low), Math.max(0, high)];\n const room = (top - (min ?? bottom)) * 0.1 || 1;\n min ??= bottom < 0 ? bottom - room : 0;\n max ??= top + room;\n } else {\n const pad = (high - low) * 0.1 || 1;\n // Stacked areas are read against zero, with the headroom of that span.\n min ??= stacked ? Math.min(0, low) : low - pad;\n max ??= high + (stacked ? (high - min) * 0.1 || 1 : pad);\n }\n const y = (value: number) => 100 - ((value - min) / (max - min || 1)) * 100;\n for (const each of series) each.y = y;\n return y;\n }\n\n /** Each row sits in the middle of an equal band, beside its label. */\n private _x(index: number) {\n const x = ((index + 0.5) / this.labels.length) * 100;\n return this._reversed ? 100 - x : x;\n }\n\n private _render() {\n const type = this._type;\n this._plot = document.createElement('div');\n this._plot.className = `${type}-plot`;\n const caption = this.el.querySelector(':scope > figcaption')?.textContent.trim();\n this._plot.setAttribute('role', 'group');\n this._plot.setAttribute('aria-roledescription', type.replace(/-/g, ' '));\n if (caption) this._plot.setAttribute('aria-label', caption);\n\n this._cursor = document.createElement('div');\n this._cursor.className = `${type}-cursor`;\n this._cursor.hidden = true;\n this._tooltip = document.createElement('div');\n this._tooltip.className = `${type}-tooltip`;\n this._tooltip.hidden = true;\n this._tooltip.setAttribute('aria-live', 'polite');\n this._plot.append(this._draw(), this._cursor, this._tooltip);\n\n this._generated = [this._plot, ...this._guides()];\n this.el.append(...this._generated);\n }\n\n /**\n * The <tfoot> row as a total to show over the plot, its value over its\n * label, or null without one.\n */\n protected _total(className: string): HTMLElement | null {\n const footer = this.el.querySelector<HTMLTableElement>(':scope > table')?.tFoot?.rows[0];\n if (!footer) return null;\n const total = document.createElement('div');\n total.className = className;\n const [label, value] = [document.createElement('span'), document.createElement('strong')];\n label.textContent = footer.cells[0]?.textContent.trim() ?? '';\n value.textContent = footer.cells[1]?.textContent.trim() ?? '';\n total.append(value, label);\n return total;\n }\n\n /** The parts below the plot: the row labels and the legend. */\n protected _guides(): Element[] {\n const labels = document.createElement('ol');\n labels.className = `${this._type}-labels`;\n labels.setAttribute('aria-hidden', 'true');\n // A loop rather than a spread: a spread of every row overflows the stack.\n for (const text of this.labels) {\n const li = document.createElement('li');\n li.textContent = text;\n labels.append(li);\n }\n return [labels, ...this._legend()];\n }\n\n /** The series legend, for more than one series. */\n protected _legend(): Element[] {\n if (this.series.length < 2) return [];\n const legend = document.createElement('ul');\n legend.className = `${this._type}-legend`;\n legend.setAttribute('aria-hidden', 'true');\n legend.append(...this.series.map((series, i) => {\n const li = document.createElement('li');\n li.dataset.series = String((i % COLORS) + 1);\n li.textContent = series.name;\n return li;\n }));\n return [legend];\n }\n\n /** Draws the series, which fill the plot. */\n protected _draw(drawn = this.series): Element {\n const svg = document.createElementNS(SVG, 'svg');\n svg.setAttribute('viewBox', '0 0 100 100');\n svg.setAttribute('preserveAspectRatio', 'none');\n svg.setAttribute('aria-hidden', 'true');\n svg.setAttribute('focusable', 'false');\n\n for (const series of drawn) {\n const group = document.createElementNS(SVG, 'g');\n group.setAttribute('class', `${this._type}-series ${series.className}`.trim());\n group.dataset.series = String((this.series.indexOf(series) % COLORS) + 1);\n\n // A gap in the data breaks the line into runs of row indexes.\n const runs: number[][] = [[]];\n series.tops.forEach((value, index) => {\n if (value === null) runs.push([]);\n else runs[runs.length - 1].push(index);\n });\n const filled = runs.filter((run) => run.length);\n const points = (run: number[], values: (number | null)[]) =>\n run.map((index): [number, number] => [this._x(index), series.y(values[index]!)]);\n const tops = filled.map((run) => monotonePath(points(run, series.tops)));\n\n const id = `line-chart-gradient-${++_gradientId}`;\n const gradient = document.createElementNS(SVG, 'linearGradient');\n gradient.id = id;\n gradient.setAttribute('x2', '0');\n gradient.setAttribute('y2', '1');\n gradient.append(document.createElementNS(SVG, 'stop'), document.createElementNS(SVG, 'stop'));\n gradient.lastElementChild.setAttribute('offset', '1');\n\n const area = document.createElementNS(SVG, 'path');\n area.setAttribute('class', `${this._type}-area`);\n area.setAttribute('fill', `url(#${id})`);\n // A stacked area runs back along the series below; the reversed curve\n // is the same curve. Its base stops at the plot bottom when the y axis\n // starts above zero.\n area.setAttribute('d', filled\n .map((run, r) => {\n if (!series.base) return `${tops[r]}V100H${this._x(run[0])}Z`;\n const base = points(run, series.base).map(([x, y]): [number, number] => [x, Math.min(100, y)]);\n return `${tops[r]}L${monotonePath(base.reverse()).slice(1)}Z`;\n })\n .join(''));\n\n const line = document.createElementNS(SVG, 'path');\n line.setAttribute('class', `${this._type}-line`);\n line.setAttribute('d', tops.join(''));\n\n group.append(gradient, area, line);\n svg.append(group);\n }\n return svg;\n }\n\n private _onPointerMove = (e: PointerEvent) => {\n this.show(this._indexAt(e));\n };\n\n /** The row under the pointer, or -1. */\n protected _indexAt(e: PointerEvent) {\n const box = this._plot.getBoundingClientRect();\n const fraction = this._horizontal\n ? (e.clientY - box.top) / box.height\n : (e.clientX - box.left) / box.width;\n const index = Math.floor((this._reversed ? 1 - fraction : fraction) * this.labels.length);\n return Math.min(this.labels.length - 1, Math.max(0, index));\n }\n\n /**\n * For a circular chart, the pointer's position from the plot's middle: a\n * fraction of a turn clockwise from the top, and its distance as a share\n * of the plot's width.\n */\n protected _polarAt(e: PointerEvent): [turn: number, distance: number] {\n const box = this._plot.getBoundingClientRect();\n const [x, y] = [e.clientX - box.left - box.width / 2, e.clientY - box.top - box.height / 2];\n return [(Math.atan2(x, -y) / (2 * Math.PI) + 1) % 1, Math.hypot(x, y) / box.width];\n }\n\n /** The keys that move to the next row and to the one before. */\n protected get _arrows(): [string[], string[]] {\n // Round a circle both pairs work. The rows run clockwise in RTL too, so\n // right still moves the way the top row turns.\n if (this._circular) return [['ArrowRight', 'ArrowDown'], ['ArrowLeft', 'ArrowUp']];\n // Arrows move the cursor the way they point, so they swap in RTL.\n if (this._horizontal) return [['ArrowDown'], ['ArrowUp']];\n return this._reversed ? [['ArrowLeft'], ['ArrowRight']] : [['ArrowRight'], ['ArrowLeft']];\n }\n\n private _onFocus = () => {\n // Only keyboard focus picks the first row; a press off the marks, which\n // focuses the plot too, leaves the tooltip hidden.\n if (this.activeIndex < 0 && this._plot.matches(':focus-visible')) this.show(0);\n };\n\n private _onLeave = (e: Event) => {\n // A touch leaves as it lifts; the tapped row stays until the plot blurs.\n if ((e as PointerEvent).pointerType === 'touch' || this._plot.matches(':focus-visible')) return;\n this.show(-1);\n };\n\n /** The browser took the touch to scroll the page. */\n private _onCancel = () => {\n this.show(-1);\n };\n\n /** The index a key moves to, or undefined for a key the chart ignores. */\n protected _keyTarget(key: string): number | undefined {\n const last = this.labels.length - 1;\n const [forward, back] = this._arrows;\n const step = (by: number) => {\n if (!this._circular) return Math.min(last, Math.max(0, this.activeIndex + by));\n // Round a circle the ends meet; from no row, back starts at the last.\n if (this.activeIndex < 0) return by > 0 ? 0 : last;\n return (this.activeIndex + by + last + 1) % (last + 1);\n };\n // With nothing shown, Escape is left to a dialog or sheet around the chart.\n if (key === 'Escape') return this.activeIndex < 0 ? undefined : -1;\n return forward.includes(key) ? step(1)\n : back.includes(key) ? step(-1)\n : { Home: 0, End: last }[key];\n }\n\n private _onKeyDown = (e: KeyboardEvent) => {\n const next = this._keyTarget(e.key);\n if (next === undefined) return;\n e.preventDefault();\n this.show(next);\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,166 @@
1
+ import {
2
+ LineChart,
3
+ svgNode
4
+ } from "./chunk-O5XPWXIQ.mjs";
5
+
6
+ // src/ts/components/pieChart.ts
7
+ var COLORS = 6;
8
+ var RADIUS = 46;
9
+ var GROWN = 1.06;
10
+ var LABEL_SHARE = 0.05;
11
+ var _maskId = 0;
12
+ var PieChart = class extends LineChart {
13
+ get _type() {
14
+ return "pie-chart";
15
+ }
16
+ get _key() {
17
+ return "Expressive_PieChart";
18
+ }
19
+ get _points() {
20
+ return false;
21
+ }
22
+ get _circular() {
23
+ return true;
24
+ }
25
+ static init(els, options = {}) {
26
+ return super.init(els, options);
27
+ }
28
+ static getInstance(el) {
29
+ return el["Expressive_PieChart"];
30
+ }
31
+ show(index) {
32
+ if (!this._tooltip || index === this.activeIndex) return;
33
+ super.show(index);
34
+ this._slices.forEach((slice, i) => slice?.classList.toggle("active", i === index));
35
+ if (this._tooltip.hidden) return;
36
+ const color = this._colors[index];
37
+ this._tooltip.querySelectorAll("[data-series]").forEach((span) => {
38
+ if (color) span.dataset.series = color;
39
+ else delete span.dataset.series;
40
+ });
41
+ const share = this._share(index);
42
+ const value = this._tooltip.querySelector("span:nth-of-type(2)");
43
+ if (share && value) value.textContent += ` (${share})`;
44
+ }
45
+ /** CSS places the tooltip on the slice's middle from --turn. */
46
+ _place(index) {
47
+ const middle = (this._turns[index][0] + this._turns[index][1]) / 2;
48
+ this._tooltip.style.setProperty("--turn", String(middle));
49
+ this._tooltip.classList.toggle("end", middle < 0.5);
50
+ }
51
+ /** The row's share of the whole as a percentage, or '' for none. */
52
+ _share(index) {
53
+ const [start, end] = this._turns[index];
54
+ if (end <= start) return "";
55
+ const share = end - start;
56
+ const options = { style: "percent", maximumFractionDigits: share < 0.1 ? 1 : 0 };
57
+ try {
58
+ return share.toLocaleString(this.el.closest("[lang]")?.lang || void 0, options);
59
+ } catch {
60
+ return share.toLocaleString(void 0, options);
61
+ }
62
+ }
63
+ /** The legend: every row with its color, its cell and its share. */
64
+ _guides() {
65
+ const legend = document.createElement("ul");
66
+ legend.className = "pie-chart-legend";
67
+ legend.setAttribute("aria-hidden", "true");
68
+ const text = this.series[0].text;
69
+ legend.append(...this.labels.map((label, i) => {
70
+ const li = document.createElement("li");
71
+ if (this._colors[i]) li.dataset.series = this._colors[i];
72
+ const [name, value, share] = [label, text[i], this._share(i)].map((content) => {
73
+ const span = document.createElement("span");
74
+ span.textContent = content;
75
+ return span;
76
+ });
77
+ li.append(name, value, share);
78
+ return li;
79
+ }));
80
+ return [legend];
81
+ }
82
+ /** The row under the pointer by its angle from the middle, or -1 off the slices. */
83
+ _indexAt(e) {
84
+ const [turn, share] = this._polarAt(e);
85
+ const distance = share * 100;
86
+ const hole = this.el.classList.contains("donut") ? parseFloat(getComputedStyle(this.el).getPropertyValue("--md-comp-pie-chart-hole")) || 0 : 0;
87
+ if (distance < RADIUS * hole) return -1;
88
+ const index = this._turns.findIndex(([start, end]) => start <= turn && turn < end);
89
+ return distance > RADIUS * (index === this.activeIndex ? GROWN : 1) ? -1 : index;
90
+ }
91
+ /**
92
+ * An SVG of slices in a -50 to 50 box, under a mask that cuts the gaps
93
+ * between them and the donut's hole, which CSS sizes. The values and the
94
+ * total are HTML over it.
95
+ */
96
+ _draw() {
97
+ const values = this.series[0].values.map((value) => Math.max(0, value ?? 0));
98
+ const total = values.reduce((sum, value) => sum + value, 0);
99
+ let turn = 0;
100
+ this._turns = values.map((value) => [turn, turn += total ? value / total : 0]);
101
+ const drawn = this._turns.filter(([start, end]) => end > start).length;
102
+ let slice = 0;
103
+ this._colors = this._turns.map(([start, end]) => {
104
+ if (end <= start) return void 0;
105
+ const index = slice++;
106
+ return String(index === drawn - 1 && index > 0 && index % COLORS === 0 ? 2 : index % COLORS + 1);
107
+ });
108
+ const at = (t, r = RADIUS) => {
109
+ const angle = t * 2 * Math.PI;
110
+ return `${(r * Math.sin(angle)).toFixed(3)},${(-r * Math.cos(angle)).toFixed(3)}`;
111
+ };
112
+ const pie = document.createElement("div");
113
+ pie.className = "pie-chart-pie";
114
+ pie.setAttribute("aria-hidden", "true");
115
+ const svg = svgNode("svg", { viewBox: "-50 -50 100 100", focusable: "false" });
116
+ const id = `pie-chart-mask-${++_maskId}`;
117
+ const area = { x: "-50", y: "-50", width: "100", height: "100" };
118
+ const mask = svgNode("mask", { id, maskUnits: "userSpaceOnUse", ...area });
119
+ const edges = this._turns.filter(([start, end]) => end > start);
120
+ mask.append(
121
+ svgNode("rect", { ...area, fill: "white" }),
122
+ svgNode("path", {
123
+ class: "pie-chart-gaps",
124
+ stroke: "black",
125
+ d: edges.length > 1 ? edges.map(([start]) => `M0,0L${at(start, 50)}`).join("") : ""
126
+ }),
127
+ svgNode("circle", { class: "pie-chart-hole", r: String(RADIUS), fill: "black" })
128
+ );
129
+ const slices = svgNode("g", { mask: `url(#${id})` });
130
+ const arc = `A${RADIUS},${RADIUS} 0`;
131
+ this._slices = this._turns.map(([start, end], i) => {
132
+ if (end <= start) return null;
133
+ const slice2 = svgNode("path", {
134
+ class: "pie-chart-slice",
135
+ "data-series": this._colors[i],
136
+ // A whole circle is two half arcs; one arc cannot end where it starts.
137
+ d: end - start >= 1 ? `M0,${-RADIUS}${arc} 1 1 0,${RADIUS}${arc} 1 1 0,${-RADIUS}Z` : `M0,0L${at(start)}${arc} ${end - start > 0.5 ? 1 : 0} 1 ${at(end)}Z`
138
+ });
139
+ slices.append(slice2);
140
+ return slice2;
141
+ });
142
+ svg.append(mask, slices);
143
+ pie.append(svg);
144
+ if (this.el.classList.contains("values")) {
145
+ const labels = document.createElement("div");
146
+ labels.className = "pie-chart-values";
147
+ this._turns.forEach(([start, end], i) => {
148
+ if (end - start < LABEL_SHARE) return;
149
+ const label = document.createElement("span");
150
+ label.dataset.series = this._colors[i];
151
+ label.style.setProperty("--turn", String((start + end) / 2));
152
+ label.textContent = this._share(i);
153
+ labels.append(label);
154
+ });
155
+ pie.append(labels);
156
+ }
157
+ const middle = this._total("pie-chart-total");
158
+ if (middle) pie.append(middle);
159
+ return pie;
160
+ }
161
+ };
162
+
163
+ export {
164
+ PieChart
165
+ };
166
+ //# sourceMappingURL=chunk-P3G3QHII.mjs.map