@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ts/behaviors/forms.ts"],
4
+ "sourcesContent": ["import { Utils } from '../core/utils';\n\nexport class Forms {\n /**\n * Checks if the label has validation and apply\n * the correct class and styles\n * @param textfield\n */\n static validateField(textfield: HTMLInputElement) {\n if (!textfield) {\n console.error('No text field element found');\n return;\n }\n\n const hasLength = textfield.getAttribute('data-length') !== null;\n const lenAttr = parseInt(textfield.getAttribute('data-length'));\n const len = textfield.value.length;\n\n if (\n len === 0 &&\n textfield.validity.badInput === false &&\n !textfield.required &&\n textfield.classList.contains('validate')\n ) {\n textfield.classList.remove('invalid');\n } else if (textfield.classList.contains('validate')) {\n // Check for character counter attributes\n if (\n (textfield.validity.valid && hasLength && len <= lenAttr) ||\n (textfield.validity.valid && !hasLength)\n ) {\n textfield.classList.remove('invalid');\n } else {\n textfield.classList.add('invalid');\n }\n }\n }\n\n /**\n * Resizes the given TextArea after updating the\n * value content dynamically.\n * @param e EventTarget\n */\n static textareaAutoResize(e: EventTarget) {\n const textarea = e as HTMLTextAreaElement;\n if (!textarea) return;\n\n // Measure the textarea against itself: collapse it to its natural height,\n // then read the content height back. `.expressive-textarea` is\n // `overflow-y: hidden`, so scrollHeight is the full content height and\n // offsetHeight - clientHeight is exactly the borders (no scrollbar to\n // account for) - which border-box sizing needs added back.\n //\n // This replaces a mirror div that the value was copied into via\n // `innerHTML = innerHTML.replace(...)` - a full serialize and reparse of\n // the text on every keystroke, plus getComputedStyle and seven style\n // writes, run twice per keystroke because it was bound to keydown and\n // keyup both.\n if (!textarea.hasAttribute('original-height')) {\n textarea.setAttribute('original-height', textarea.getBoundingClientRect().height.toString());\n }\n const originalHeight = parseInt(textarea.getAttribute('original-height'));\n if (isNaN(originalHeight)) return;\n\n textarea.style.height = 'auto';\n // Three reads after a single write: one layout flush for the whole resize.\n const borders = textarea.offsetHeight - textarea.clientHeight;\n const contentHeight = textarea.scrollHeight + borders;\n textarea.style.height = Math.max(originalHeight, contentHeight) + 'px';\n }\n\n static Init() {\n Utils.onDocumentReady(() => {\n document.addEventListener('change', (e: KeyboardEvent) => {\n const target = <HTMLInputElement>e.target;\n if (target instanceof HTMLInputElement && target.type === 'file') {\n const zone = target.closest('.drop-zone');\n if (zone) Forms.listDropZoneFiles(zone, target);\n }\n if (target instanceof HTMLInputElement) {\n // The sibling-label loop that used to live here compared\n // `tagName == 'label'` (tagName is uppercase) so it never matched,\n // and no rule in the Sass styles `label.active` anyway - the\n // floating label is driven by CSS, not by a class from here.\n Forms.validateField(target);\n }\n });\n\n document.addEventListener('keyup', (e: KeyboardEvent) => {\n const target = <HTMLInputElement>e.target;\n // Radio and Checkbox focus class\n if (target instanceof HTMLInputElement && ['radio', 'checkbox'].includes(target.type)) {\n // TAB, check if tabbing to radio or checkbox.\n if (e.key === Utils.keys.TAB) {\n target.classList.add('tabbed');\n target.addEventListener('blur', () => target.classList.remove('tabbed'), {\n once: true\n });\n }\n }\n });\n\n document.addEventListener('click', Forms.togglePassword);\n // Capture, so the password is hidden again before any submit handler\n // reads the form or the browser offers to save it.\n document.addEventListener('submit', Forms.resetFormExtras, true);\n document.addEventListener('reset', Forms.resetFormExtras, true);\n for (const type of ['dragenter', 'dragover', 'dragleave', 'drop']) {\n document.addEventListener(type, Forms.markDropZone);\n }\n\n document.querySelectorAll('.expressive-textarea').forEach((textArea: HTMLTextAreaElement) => {\n Forms.InitTextarea(textArea);\n });\n\n // File Input Path\n document\n .querySelectorAll('.file-field input[type=\"file\"]')\n .forEach((fileInput: HTMLInputElement) => {\n Forms.InitFileInputPath(fileInput);\n });\n });\n }\n\n /**\n * A .password-toggle button shows or hides the password in its field. The\n * button's own name stays put (\"Show password\"); aria-pressed carries the\n * state, so there is no generated text to translate.\n */\n static togglePassword(e: MouseEvent) {\n const button = (<Element>e.target).closest?.('.password-toggle');\n const input = button?.closest('.field')?.querySelector<HTMLInputElement>(\n 'input[type=\"password\"], input[data-password]'\n );\n if (!input) return;\n const show = input.type === 'password';\n // Marked so the input is still found once it has become type=\"text\".\n input.toggleAttribute('data-password', show);\n input.type = show ? 'text' : 'password';\n button.setAttribute('aria-pressed', String(show));\n }\n\n /**\n * A revealed password goes back to type=\"password\" when its form is\n * submitted or reset, so it is never sent or autofilled as plain text and\n * a reset field does not stay revealed. A reset form also drops the file\n * lists it would otherwise leave showing: reset fires no change event.\n */\n static resetFormExtras(e: Event) {\n const form = e.target;\n if (!(form instanceof HTMLFormElement)) return;\n form.querySelectorAll<HTMLInputElement>('input[data-password]').forEach((input) => {\n input.removeAttribute('data-password');\n input.type = 'password';\n input.closest('.field')?.querySelector('.password-toggle')?.setAttribute('aria-pressed', 'false');\n });\n if (e.type === 'reset') {\n form.querySelectorAll('.drop-zone-files').forEach((list) => list.replaceChildren());\n form.querySelectorAll('.drop-zone.dragover').forEach((zone) => zone.classList.remove('dragover'));\n }\n }\n\n /**\n * The native file input covers its .drop-zone, so the browser already takes\n * a dropped file. This only shows the drag: the input is the topmost\n * element, so every drag event over the zone targets it and leaving it is\n * leaving the zone.\n */\n static markDropZone(e: DragEvent) {\n const input = <Element>e.target;\n if (!(input instanceof HTMLInputElement) || input.type !== 'file') return;\n const zone = input.closest('.drop-zone');\n if (!zone) return;\n const over = e.type === 'dragenter' || e.type === 'dragover';\n zone.classList.toggle('dragover', over && !input.disabled);\n }\n\n /** Lists the chosen files, name and size, in the zone's .drop-zone-files. */\n static listDropZoneFiles(zone: Element, input: HTMLInputElement) {\n const list = zone.querySelector('.drop-zone-files');\n if (!list) return;\n const size = (bytes: number) => {\n const [value, unit] =\n bytes >= 1e6 ? [bytes / 1e6, 'megabyte'] : bytes >= 1e3 ? [bytes / 1e3, 'kilobyte'] : [bytes, 'byte'];\n return new Intl.NumberFormat(undefined, { style: 'unit', unit, maximumFractionDigits: 1 }).format(value);\n };\n list.replaceChildren(\n ...Array.from(input.files ?? [], (file) => {\n const item = document.createElement('li');\n const name = document.createElement('span');\n const meta = document.createElement('span');\n name.textContent = file.name;\n meta.textContent = size(file.size);\n item.append(name, meta);\n return item;\n })\n );\n }\n\n static InitTextarea(textarea: HTMLTextAreaElement) {\n // Save Data in Element\n textarea.setAttribute('original-height', textarea.getBoundingClientRect().height.toString());\n Forms.textareaAutoResize(textarea);\n // `input` rather than keydown + keyup: it fires once per actual value\n // change and also covers paste, drag-drop and IME composition, which the\n // key events missed.\n textarea.addEventListener('input', (e) => Forms.textareaAutoResize(e.target));\n }\n\n static InitFileInputPath(fileInput: HTMLInputElement) {\n fileInput.addEventListener('change', () => {\n const fileField = fileInput.closest('.file-field');\n const pathInput = <HTMLInputElement>fileField.querySelector('input.file-path');\n const files = fileInput.files;\n const filenames = [];\n for (let i = 0; i < files.length; i++) {\n filenames.push(files[i].name);\n }\n pathInput.value = filenames.join(', ');\n pathInput.dispatchEvent(\n new Event('change', { bubbles: true, cancelable: true, composed: true })\n );\n });\n }\n}\n"],
5
+ "mappings": ";;;;;AAEO,IAAM,QAAN,MAAM,OAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,OAAO,cAAc,WAA6B;AAChD,QAAI,CAAC,WAAW;AACd,cAAQ,MAAM,6BAA6B;AAC3C;AAAA,IACF;AAEA,UAAM,YAAY,UAAU,aAAa,aAAa,MAAM;AAC5D,UAAM,UAAU,SAAS,UAAU,aAAa,aAAa,CAAC;AAC9D,UAAM,MAAM,UAAU,MAAM;AAE5B,QACE,QAAQ,KACR,UAAU,SAAS,aAAa,SAChC,CAAC,UAAU,YACX,UAAU,UAAU,SAAS,UAAU,GACvC;AACA,gBAAU,UAAU,OAAO,SAAS;AAAA,IACtC,WAAW,UAAU,UAAU,SAAS,UAAU,GAAG;AAEnD,UACG,UAAU,SAAS,SAAS,aAAa,OAAO,WAChD,UAAU,SAAS,SAAS,CAAC,WAC9B;AACA,kBAAU,UAAU,OAAO,SAAS;AAAA,MACtC,OAAO;AACL,kBAAU,UAAU,IAAI,SAAS;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,mBAAmB,GAAgB;AACxC,UAAM,WAAW;AACjB,QAAI,CAAC,SAAU;AAaf,QAAI,CAAC,SAAS,aAAa,iBAAiB,GAAG;AAC7C,eAAS,aAAa,mBAAmB,SAAS,sBAAsB,EAAE,OAAO,SAAS,CAAC;AAAA,IAC7F;AACA,UAAM,iBAAiB,SAAS,SAAS,aAAa,iBAAiB,CAAC;AACxE,QAAI,MAAM,cAAc,EAAG;AAE3B,aAAS,MAAM,SAAS;AAExB,UAAM,UAAU,SAAS,eAAe,SAAS;AACjD,UAAM,gBAAgB,SAAS,eAAe;AAC9C,aAAS,MAAM,SAAS,KAAK,IAAI,gBAAgB,aAAa,IAAI;AAAA,EACpE;AAAA,EAEA,OAAO,OAAO;AACZ,UAAM,gBAAgB,MAAM;AAC1B,eAAS,iBAAiB,UAAU,CAAC,MAAqB;AACxD,cAAM,SAA2B,EAAE;AACnC,YAAI,kBAAkB,oBAAoB,OAAO,SAAS,QAAQ;AAChE,gBAAM,OAAO,OAAO,QAAQ,YAAY;AACxC,cAAI,KAAM,QAAM,kBAAkB,MAAM,MAAM;AAAA,QAChD;AACA,YAAI,kBAAkB,kBAAkB;AAKtC,iBAAM,cAAc,MAAM;AAAA,QAC5B;AAAA,MACF,CAAC;AAED,eAAS,iBAAiB,SAAS,CAAC,MAAqB;AACvD,cAAM,SAA2B,EAAE;AAEnC,YAAI,kBAAkB,oBAAoB,CAAC,SAAS,UAAU,EAAE,SAAS,OAAO,IAAI,GAAG;AAErF,cAAI,EAAE,QAAQ,MAAM,KAAK,KAAK;AAC5B,mBAAO,UAAU,IAAI,QAAQ;AAC7B,mBAAO,iBAAiB,QAAQ,MAAM,OAAO,UAAU,OAAO,QAAQ,GAAG;AAAA,cACvE,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAED,eAAS,iBAAiB,SAAS,OAAM,cAAc;AAGvD,eAAS,iBAAiB,UAAU,OAAM,iBAAiB,IAAI;AAC/D,eAAS,iBAAiB,SAAS,OAAM,iBAAiB,IAAI;AAC9D,iBAAW,QAAQ,CAAC,aAAa,YAAY,aAAa,MAAM,GAAG;AACjE,iBAAS,iBAAiB,MAAM,OAAM,YAAY;AAAA,MACpD;AAEA,eAAS,iBAAiB,sBAAsB,EAAE,QAAQ,CAAC,aAAkC;AAC3F,eAAM,aAAa,QAAQ;AAAA,MAC7B,CAAC;AAGD,eACG,iBAAiB,gCAAgC,EACjD,QAAQ,CAAC,cAAgC;AACxC,eAAM,kBAAkB,SAAS;AAAA,MACnC,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,eAAe,GAAe;AACnC,UAAM,SAAmB,EAAE,OAAQ,UAAU,kBAAkB;AAC/D,UAAM,QAAQ,QAAQ,QAAQ,QAAQ,GAAG;AAAA,MACvC;AAAA,IACF;AACA,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,MAAM,SAAS;AAE5B,UAAM,gBAAgB,iBAAiB,IAAI;AAC3C,UAAM,OAAO,OAAO,SAAS;AAC7B,WAAO,aAAa,gBAAgB,OAAO,IAAI,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,gBAAgB,GAAU;AAC/B,UAAM,OAAO,EAAE;AACf,QAAI,EAAE,gBAAgB,iBAAkB;AACxC,SAAK,iBAAmC,sBAAsB,EAAE,QAAQ,CAAC,UAAU;AACjF,YAAM,gBAAgB,eAAe;AACrC,YAAM,OAAO;AACb,YAAM,QAAQ,QAAQ,GAAG,cAAc,kBAAkB,GAAG,aAAa,gBAAgB,OAAO;AAAA,IAClG,CAAC;AACD,QAAI,EAAE,SAAS,SAAS;AACtB,WAAK,iBAAiB,kBAAkB,EAAE,QAAQ,CAAC,SAAS,KAAK,gBAAgB,CAAC;AAClF,WAAK,iBAAiB,qBAAqB,EAAE,QAAQ,CAAC,SAAS,KAAK,UAAU,OAAO,UAAU,CAAC;AAAA,IAClG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,aAAa,GAAc;AAChC,UAAM,QAAiB,EAAE;AACzB,QAAI,EAAE,iBAAiB,qBAAqB,MAAM,SAAS,OAAQ;AACnE,UAAM,OAAO,MAAM,QAAQ,YAAY;AACvC,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,EAAE,SAAS,eAAe,EAAE,SAAS;AAClD,SAAK,UAAU,OAAO,YAAY,QAAQ,CAAC,MAAM,QAAQ;AAAA,EAC3D;AAAA;AAAA,EAGA,OAAO,kBAAkB,MAAe,OAAyB;AAC/D,UAAM,OAAO,KAAK,cAAc,kBAAkB;AAClD,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,CAAC,UAAkB;AAC9B,YAAM,CAAC,OAAO,IAAI,IAChB,SAAS,MAAM,CAAC,QAAQ,KAAK,UAAU,IAAI,SAAS,MAAM,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,OAAO,MAAM;AACtG,aAAO,IAAI,KAAK,aAAa,QAAW,EAAE,OAAO,QAAQ,MAAM,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK;AAAA,IACzG;AACA,SAAK;AAAA,MACH,GAAG,MAAM,KAAK,MAAM,SAAS,CAAC,GAAG,CAAC,SAAS;AACzC,cAAM,OAAO,SAAS,cAAc,IAAI;AACxC,cAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,cAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,aAAK,cAAc,KAAK;AACxB,aAAK,cAAc,KAAK,KAAK,IAAI;AACjC,aAAK,OAAO,MAAM,IAAI;AACtB,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,OAAO,aAAa,UAA+B;AAEjD,aAAS,aAAa,mBAAmB,SAAS,sBAAsB,EAAE,OAAO,SAAS,CAAC;AAC3F,WAAM,mBAAmB,QAAQ;AAIjC,aAAS,iBAAiB,SAAS,CAAC,MAAM,OAAM,mBAAmB,EAAE,MAAM,CAAC;AAAA,EAC9E;AAAA,EAEA,OAAO,kBAAkB,WAA6B;AACpD,cAAU,iBAAiB,UAAU,MAAM;AACzC,YAAM,YAAY,UAAU,QAAQ,aAAa;AACjD,YAAM,YAA8B,UAAU,cAAc,iBAAiB;AAC7E,YAAM,QAAQ,UAAU;AACxB,YAAM,YAAY,CAAC;AACnB,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,kBAAU,KAAK,MAAM,CAAC,EAAE,IAAI;AAAA,MAC9B;AACA,gBAAU,QAAQ,UAAU,KAAK,IAAI;AACrC,gBAAU;AAAA,QACR,IAAI,MAAM,UAAU,EAAE,SAAS,MAAM,YAAY,MAAM,UAAU,KAAK,CAAC;AAAA,MACzE;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Component
3
- } from "./chunk-QSQ24FYT.mjs";
3
+ } from "./chunk-LTWPGFBR.mjs";
4
4
 
5
5
  // src/ts/components/scrollspy.ts
6
6
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
@@ -132,4 +132,4 @@ var ScrollSpy = _ScrollSpy;
132
132
  export {
133
133
  ScrollSpy
134
134
  };
135
- //# sourceMappingURL=chunk-FRB7TOCS.mjs.map
135
+ //# sourceMappingURL=chunk-QV2JAWIZ.mjs.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  Utils
3
- } from "./chunk-MISGHDUU.mjs";
3
+ } from "./chunk-6OMRGML6.mjs";
4
4
  import {
5
5
  Component
6
- } from "./chunk-QSQ24FYT.mjs";
6
+ } from "./chunk-LTWPGFBR.mjs";
7
7
 
8
8
  // src/ts/components/expandingCard.ts
9
9
  var _defaults = {
@@ -172,4 +172,4 @@ export {
172
172
  EXPANDING_CARD_SELECTOR,
173
173
  ExpandingCard
174
174
  };
175
- //# sourceMappingURL=chunk-N3UHGN3N.mjs.map
175
+ //# sourceMappingURL=chunk-QVVEWBOF.mjs.map
@@ -0,0 +1,139 @@
1
+ import {
2
+ COLORS,
3
+ LineChart,
4
+ svgNode
5
+ } from "./chunk-O5XPWXIQ.mjs";
6
+
7
+ // src/ts/components/radialChart.ts
8
+ var RADIUS = 46;
9
+ var FILL = 0.8;
10
+ var RadialChart = class extends LineChart {
11
+ get _type() {
12
+ return "radial-chart";
13
+ }
14
+ get _key() {
15
+ return "Expressive_RadialChart";
16
+ }
17
+ get _points() {
18
+ return false;
19
+ }
20
+ get _circular() {
21
+ return true;
22
+ }
23
+ get _gauge() {
24
+ return this.el.classList.contains("gauge");
25
+ }
26
+ static init(els, options = {}) {
27
+ return super.init(els, options);
28
+ }
29
+ static getInstance(el) {
30
+ return el["Expressive_RadialChart"];
31
+ }
32
+ show(index) {
33
+ if (!this._tooltip || index === this.activeIndex) return;
34
+ super.show(index);
35
+ this._rings.forEach((ring, i) => ring.classList.toggle("active", i === index));
36
+ if (this._tooltip.hidden) return;
37
+ const color = this._color(index);
38
+ this._tooltip.querySelectorAll("[data-series]").forEach((span) => {
39
+ span.dataset.series = color;
40
+ });
41
+ }
42
+ /** The tooltip sits where the ring's fill ends. */
43
+ _place(index) {
44
+ const turn = this._turn(this._fractions[index]);
45
+ const r = this._radii[index];
46
+ const [x, y] = [r * Math.sin(turn * 2 * Math.PI), -r * Math.cos(turn * 2 * Math.PI)];
47
+ this._tooltip.style.left = `${50 + x}%`;
48
+ this._tooltip.style.top = `${(50 + y) / (this._gauge ? 50 : 100) * 100}%`;
49
+ this._tooltip.classList.toggle("end", x > 0);
50
+ }
51
+ _color(index) {
52
+ return String(index % COLORS + 1);
53
+ }
54
+ /** A fraction of the ring as a turn clockwise from the top. */
55
+ _turn(fraction) {
56
+ return this._gauge ? fraction / 2 - 0.25 : fraction;
57
+ }
58
+ /** For more than one row, a legend of every row with its color and cell. */
59
+ _guides() {
60
+ if (this.labels.length < 2) return [];
61
+ const legend = document.createElement("ul");
62
+ legend.className = "radial-chart-legend";
63
+ legend.setAttribute("aria-hidden", "true");
64
+ const text = this.series[0].text;
65
+ legend.append(...this.labels.map((label, i) => {
66
+ const li = document.createElement("li");
67
+ li.dataset.series = this._color(i);
68
+ const [name, value] = [label, text[i]].map((content) => {
69
+ const span = document.createElement("span");
70
+ span.textContent = content;
71
+ return span;
72
+ });
73
+ li.append(name, value);
74
+ return li;
75
+ }));
76
+ return [legend];
77
+ }
78
+ /** The ring under the pointer by its distance from the middle, or -1. */
79
+ _indexAt(e) {
80
+ const box = this._plot.getBoundingClientRect();
81
+ const [x, y] = [e.clientX - box.left - box.width / 2, e.clientY - box.top - box.width / 2];
82
+ if (this._gauge && y > 0) return -1;
83
+ const distance = Math.hypot(x, y) / box.width * 100;
84
+ const step = this._radii.length > 1 ? this._radii[0] - this._radii[1] : this._width;
85
+ return this._radii.findIndex((r) => Math.abs(distance - r) <= step / 2);
86
+ }
87
+ /**
88
+ * An SVG of rings in a -50 to 50 box, the top half for a gauge. Each row
89
+ * is a track circle and a fill circle whose dash is its share; CSS draws
90
+ * them from --radial-chart-dash. The total is HTML over it.
91
+ */
92
+ _draw() {
93
+ const style = getComputedStyle(this.el);
94
+ const token = (name, fallback) => {
95
+ const value = parseFloat(style.getPropertyValue(`--md-comp-radial-chart-${name}`));
96
+ return Number.isFinite(value) ? value : fallback;
97
+ };
98
+ const count = this.labels.length;
99
+ let [width, gap] = [Math.max(0, token("thickness", 8)), Math.max(0, token("gap", 2))];
100
+ const scale = Math.min(1, RADIUS * FILL / (count * width + (count - 1) * gap || 1));
101
+ [width, gap] = [width * scale, gap * scale];
102
+ this._width = width;
103
+ this._radii = this.labels.map((_, i) => RADIUS - width / 2 - i * (width + gap));
104
+ const max = [parseFloat(this.el.dataset.max ?? ""), this.options.max ?? NaN, 100].find((value) => value > 0);
105
+ this._fractions = this.series[0].values.map((value) => Math.min(1, Math.max(0, (value ?? 0) / max)));
106
+ const gauge = this._gauge;
107
+ const svg = svgNode("svg", { viewBox: gauge ? "-50 -50 100 50" : "-50 -50 100 100", focusable: "false" });
108
+ const transform = gauge ? "rotate(180)" : "rotate(-90)";
109
+ const span = gauge ? 0.5 : 1;
110
+ this._rings = this._radii.map((r, i) => {
111
+ const length = 2 * Math.PI * r;
112
+ const circle = (name, fraction, capped) => {
113
+ const ring2 = svgNode("circle", { class: name, r: r.toFixed(3), transform, "stroke-width": width.toFixed(3) });
114
+ const dash = Math.max(1e-3, fraction * span * length - (capped ? width : 0));
115
+ ring2.style.setProperty("--radial-chart-dash", `${dash.toFixed(3)} ${length.toFixed(3)}`);
116
+ if (capped) ring2.style.setProperty("--radial-chart-offset", (-width / 2).toFixed(3));
117
+ return ring2;
118
+ };
119
+ const ring = circle("radial-chart-ring", this._fractions[i], true);
120
+ ring.dataset.series = this._color(i);
121
+ if (!this._fractions[i]) ring.classList.add("empty");
122
+ svg.append(circle("radial-chart-track", 1, gauge), ring);
123
+ return ring;
124
+ });
125
+ const rings = document.createElement("div");
126
+ rings.className = "radial-chart-rings";
127
+ rings.setAttribute("aria-hidden", "true");
128
+ rings.style.setProperty("--radial-chart-middle", `${(this._radii[count - 1] - this._width / 2) * 2}%`);
129
+ rings.append(svg);
130
+ const total = this._total("radial-chart-total");
131
+ if (total) rings.append(total);
132
+ return rings;
133
+ }
134
+ };
135
+
136
+ export {
137
+ RadialChart
138
+ };
139
+ //# sourceMappingURL=chunk-RHRQKTTX.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ts/components/radialChart.ts"],
4
+ "sourcesContent": ["import { InitElements, InitElement } from '../core/component';\nimport { COLORS, LineChart, LineChartOptions, svgNode as node } from './lineChart';\n\nexport type RadialChartOptions = LineChartOptions;\n\n/** The outer ring's outer edge in a 100-unit box. */\nconst RADIUS = 46;\n/** The rings never fill more of the radius than this, leaving a middle. */\nconst FILL = 0.8;\n\n/**\n * Radial chart. It reads its <table> like LineChart, but draws each row of\n * the first value column as a ring, outermost first, filled clockwise from\n * the top as a share of `data-max` above zero (100 by default). With `.gauge` the rings\n * are half circles over the middle. A <tfoot> row shows in the middle, and\n * a legend lists the rows when there is more than one. Further columns show\n * in the tooltip.\n *\n * Re-run `RadialChart.init(el)` after changing the table or the ring tokens.\n */\nexport class RadialChart extends LineChart {\n // No initializers: LineChart's constructor draws before they would run.\n /** Each ring's middle radius, and their shared width. */\n private _radii: number[];\n private _width: number;\n /** Each row's share of the maximum, from 0 to 1. */\n private _fractions: number[];\n private _rings: SVGCircleElement[];\n\n protected get _type() {\n return 'radial-chart';\n }\n\n protected get _key() {\n return 'Expressive_RadialChart';\n }\n\n protected get _points() {\n return false;\n }\n\n protected get _circular() {\n return true;\n }\n\n private get _gauge() {\n return this.el.classList.contains('gauge');\n }\n\n static init(el: HTMLElement, options?: Partial<RadialChartOptions>): RadialChart;\n static init(els: InitElements<InitElement>, options?: Partial<RadialChartOptions>): RadialChart[];\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<RadialChartOptions> = {}\n ): RadialChart | RadialChart[] {\n return super.init(els as HTMLElement, options) as RadialChart | RadialChart[];\n }\n\n static getInstance(el: HTMLElement): RadialChart {\n return el['Expressive_RadialChart'];\n }\n\n show(index: number) {\n if (!this._tooltip || index === this.activeIndex) return;\n super.show(index);\n this._rings.forEach((ring, i) => ring.classList.toggle('active', i === index));\n // LineChart hides the tooltip for -1 and for an index past the last row.\n if (this._tooltip.hidden) return;\n const color = this._color(index);\n this._tooltip.querySelectorAll<HTMLElement>('[data-series]').forEach((span) => {\n span.dataset.series = color;\n });\n }\n\n /** The tooltip sits where the ring's fill ends. */\n protected _place(index: number) {\n const turn = this._turn(this._fractions[index]);\n const r = this._radii[index];\n const [x, y] = [r * Math.sin(turn * 2 * Math.PI), -r * Math.cos(turn * 2 * Math.PI)];\n this._tooltip.style.left = `${50 + x}%`;\n this._tooltip.style.top = `${((50 + y) / (this._gauge ? 50 : 100)) * 100}%`;\n // On the right half it opens leftwards, over the rings.\n this._tooltip.classList.toggle('end', x > 0);\n }\n\n private _color(index: number) {\n return String((index % COLORS) + 1);\n }\n\n /** A fraction of the ring as a turn clockwise from the top. */\n private _turn(fraction: number) {\n return this._gauge ? fraction / 2 - 0.25 : fraction;\n }\n\n /** For more than one row, a legend of every row with its color and cell. */\n protected _guides(): Element[] {\n if (this.labels.length < 2) return [];\n const legend = document.createElement('ul');\n legend.className = 'radial-chart-legend';\n legend.setAttribute('aria-hidden', 'true');\n const text = this.series[0].text;\n legend.append(...this.labels.map((label, i) => {\n const li = document.createElement('li');\n li.dataset.series = this._color(i);\n const [name, value] = [label, text[i]].map((content) => {\n const span = document.createElement('span');\n span.textContent = content;\n return span;\n });\n li.append(name, value);\n return li;\n }));\n return [legend];\n }\n\n /** The ring under the pointer by its distance from the middle, or -1. */\n protected _indexAt(e: PointerEvent) {\n const box = this._plot.getBoundingClientRect();\n // The middle is half the width down in both shapes.\n const [x, y] = [e.clientX - box.left - box.width / 2, e.clientY - box.top - box.width / 2];\n if (this._gauge && y > 0) return -1;\n // The distance in the SVG's 100-unit box; a gap belongs to a ring beside it.\n const distance = (Math.hypot(x, y) / box.width) * 100;\n const step = this._radii.length > 1 ? this._radii[0] - this._radii[1] : this._width;\n return this._radii.findIndex((r) => Math.abs(distance - r) <= step / 2);\n }\n\n /**\n * An SVG of rings in a -50 to 50 box, the top half for a gauge. Each row\n * is a track circle and a fill circle whose dash is its share; CSS draws\n * them from --radial-chart-dash. The total is HTML over it.\n */\n protected _draw(): Element {\n const style = getComputedStyle(this.el);\n const token = (name: string, fallback: number) => {\n const value = parseFloat(style.getPropertyValue(`--md-comp-radial-chart-${name}`));\n return Number.isFinite(value) ? value : fallback;\n };\n const count = this.labels.length;\n let [width, gap] = [Math.max(0, token('thickness', 8)), Math.max(0, token('gap', 2))];\n // Too many rings to leave a middle thin, with their gaps, to fit.\n // ponytail: past about 18 rings they are hairlines; that many values want a bar chart.\n const scale = Math.min(1, (RADIUS * FILL) / (count * width + (count - 1) * gap || 1));\n [width, gap] = [width * scale, gap * scale];\n this._width = width;\n this._radii = this.labels.map((_, i) => RADIUS - width / 2 - i * (width + gap));\n // A maximum of zero or less means nothing, so it falls back.\n const max = [parseFloat(this.el.dataset.max ?? ''), this.options.max ?? NaN, 100].find((value) => value > 0)!;\n this._fractions = this.series[0].values.map((value) => Math.min(1, Math.max(0, (value ?? 0) / max)));\n\n const gauge = this._gauge;\n const svg = node('svg', { viewBox: gauge ? '-50 -50 100 50' : '-50 -50 100 100', focusable: 'false' });\n // A circle starts at three o'clock; turn it to the top, or to nine for a gauge.\n const transform = gauge ? 'rotate(180)' : 'rotate(-90)';\n const span = gauge ? 0.5 : 1;\n this._rings = this._radii.map((r, i) => {\n const length = 2 * Math.PI * r;\n // Round caps reach half the width past each end of a dash, so a capped\n // dash is one width shorter and starts half a width in. One shorter\n // than the width is a dot at the start.\n const circle = (name: string, fraction: number, capped: boolean) => {\n const ring = node('circle', { class: name, r: r.toFixed(3), transform, 'stroke-width': width.toFixed(3) });\n const dash = Math.max(0.001, fraction * span * length - (capped ? width : 0));\n ring.style.setProperty('--radial-chart-dash', `${dash.toFixed(3)} ${length.toFixed(3)}`);\n if (capped) ring.style.setProperty('--radial-chart-offset', (-width / 2).toFixed(3));\n return ring;\n };\n const ring = circle('radial-chart-ring', this._fractions[i], true) as SVGCircleElement;\n ring.dataset.series = this._color(i);\n // An empty ring would still draw its round cap.\n if (!this._fractions[i]) ring.classList.add('empty');\n // Only a gauge's track has ends to round.\n svg.append(circle('radial-chart-track', 1, gauge), ring);\n return ring;\n });\n\n const rings = document.createElement('div');\n rings.className = 'radial-chart-rings';\n rings.setAttribute('aria-hidden', 'true');\n rings.style.setProperty('--radial-chart-middle', `${(this._radii[count - 1] - this._width / 2) * 2}%`);\n rings.append(svg);\n\n const total = this._total('radial-chart-total');\n if (total) rings.append(total);\n return rings;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ring"]
7
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ COLORS,
3
+ LineChart
4
+ } from "./chunk-O5XPWXIQ.mjs";
5
+
6
+ // src/ts/components/columnChart.ts
7
+ var ColumnChart = class extends LineChart {
8
+ get _type() {
9
+ return "column-chart";
10
+ }
11
+ get _key() {
12
+ return "Expressive_ColumnChart";
13
+ }
14
+ get _fromZero() {
15
+ return true;
16
+ }
17
+ get _points() {
18
+ return false;
19
+ }
20
+ static init(els, options = {}) {
21
+ return super.init(els, options);
22
+ }
23
+ static getInstance(el) {
24
+ return el["Expressive_ColumnChart"];
25
+ }
26
+ _draw(drawn = this.series) {
27
+ const columns = document.createElement("div");
28
+ columns.className = "column-chart-columns";
29
+ columns.setAttribute("aria-hidden", "true");
30
+ const clamp = (value, y = drawn[0]?.y ?? this._y) => Math.min(100, Math.max(0, y(value)));
31
+ columns.style.setProperty("--zero", `${clamp(0)}%`);
32
+ this._plot.style.setProperty("--rows", String(this.labels.length));
33
+ this.labels.forEach((_, index) => {
34
+ const row = document.createElement("div");
35
+ drawn.forEach((series) => {
36
+ const to = series.tops[index];
37
+ const from = series.base?.[index] ?? 0;
38
+ if (series.base && (to === null || from === null)) return;
39
+ const column = document.createElement("span");
40
+ if (to !== null) {
41
+ column.className = series.className;
42
+ column.dataset.series = String(this.series.indexOf(series) % COLORS + 1);
43
+ column.classList.toggle("negative", !series.base && to < from);
44
+ column.style.setProperty("--top", `${clamp(Math.max(from, to), series.y)}%`);
45
+ column.style.setProperty("--bottom", `${100 - clamp(Math.min(from, to), series.y)}%`);
46
+ if (this._horizontal) column.dataset.text = series.text[index];
47
+ }
48
+ row.append(column);
49
+ });
50
+ columns.append(row);
51
+ });
52
+ return columns;
53
+ }
54
+ };
55
+
56
+ export {
57
+ ColumnChart
58
+ };
59
+ //# sourceMappingURL=chunk-SPETGK25.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ts/components/columnChart.ts"],
4
+ "sourcesContent": ["import { InitElements, InitElement } from '../core/component';\nimport { COLORS, LineChart, LineChartOptions } from './lineChart';\n\nexport type ColumnChartOptions = LineChartOptions;\n\n/**\n * Column chart. It reads its <table>, scales, labels and tooltip like\n * LineChart, but draws each row as a group of columns from zero, or one\n * column of segments with `stacked`. Columns are HTML clipped to their\n * value, so their rounded corners keep their shape at any size.\n *\n * Re-run `ColumnChart.init(el)` after changing the table.\n */\nexport class ColumnChart extends LineChart {\n protected get _type() {\n return 'column-chart';\n }\n\n protected get _key() {\n return 'Expressive_ColumnChart';\n }\n\n protected get _fromZero() {\n return true;\n }\n\n protected get _points() {\n return false;\n }\n\n static init(el: HTMLElement, options?: Partial<ColumnChartOptions>): ColumnChart;\n static init(els: InitElements<InitElement>, options?: Partial<ColumnChartOptions>): ColumnChart[];\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<ColumnChartOptions> = {}\n ): ColumnChart | ColumnChart[] {\n return super.init(els as HTMLElement, options) as ColumnChart | ColumnChart[];\n }\n\n static getInstance(el: HTMLElement): ColumnChart {\n return el['Expressive_ColumnChart'];\n }\n\n protected _draw(drawn = this.series): Element {\n const columns = document.createElement('div');\n columns.className = 'column-chart-columns';\n columns.setAttribute('aria-hidden', 'true');\n const clamp = (value: number, y = drawn[0]?.y ?? this._y) => Math.min(100, Math.max(0, y(value)));\n // Columns grow from zero, or from the plot edge when zero is off the scale.\n columns.style.setProperty('--zero', `${clamp(0)}%`);\n this._plot.style.setProperty('--rows', String(this.labels.length));\n\n this.labels.forEach((_, index) => {\n const row = document.createElement('div');\n drawn.forEach((series) => {\n // A stacked segment above a gap has no known base, so it is left out.\n const to = series.tops[index];\n const from = series.base?.[index] ?? 0;\n if (series.base && (to === null || from === null)) return;\n const column = document.createElement('span');\n // A gap in a grouped row keeps its empty slot, so the series stay aligned.\n if (to !== null) {\n column.className = series.className;\n column.dataset.series = String((this.series.indexOf(series) % COLORS) + 1);\n // A stacked segment never hangs from zero, so it keeps the top rounding.\n column.classList.toggle('negative', !series.base && to < from);\n column.style.setProperty('--top', `${clamp(Math.max(from, to), series.y)}%`);\n column.style.setProperty('--bottom', `${100 - clamp(Math.min(from, to), series.y)}%`);\n // The cell text, which a bar chart with .values writes along its bar.\n if (this._horizontal) column.dataset.text = series.text[index];\n }\n row.append(column);\n });\n columns.append(row);\n });\n return columns;\n }\n}\n"],
5
+ "mappings": ";;;;;;AAaO,IAAM,cAAN,cAA0B,UAAU;AAAA,EACzC,IAAc,QAAQ;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,IAAc,OAAO;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,IAAc,YAAY;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,IAAc,UAAU;AACtB,WAAO;AAAA,EACT;AAAA,EAIA,OAAO,KACL,KACA,UAAuC,CAAC,GACX;AAC7B,WAAO,MAAM,KAAK,KAAoB,OAAO;AAAA,EAC/C;AAAA,EAEA,OAAO,YAAY,IAA8B;AAC/C,WAAO,GAAG,wBAAwB;AAAA,EACpC;AAAA,EAEU,MAAM,QAAQ,KAAK,QAAiB;AAC5C,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,YAAQ,aAAa,eAAe,MAAM;AAC1C,UAAM,QAAQ,CAAC,OAAe,IAAI,MAAM,CAAC,GAAG,KAAK,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;AAEhG,YAAQ,MAAM,YAAY,UAAU,GAAG,MAAM,CAAC,CAAC,GAAG;AAClD,SAAK,MAAM,MAAM,YAAY,UAAU,OAAO,KAAK,OAAO,MAAM,CAAC;AAEjE,SAAK,OAAO,QAAQ,CAAC,GAAG,UAAU;AAChC,YAAM,MAAM,SAAS,cAAc,KAAK;AACxC,YAAM,QAAQ,CAAC,WAAW;AAExB,cAAM,KAAK,OAAO,KAAK,KAAK;AAC5B,cAAM,OAAO,OAAO,OAAO,KAAK,KAAK;AACrC,YAAI,OAAO,SAAS,OAAO,QAAQ,SAAS,MAAO;AACnD,cAAM,SAAS,SAAS,cAAc,MAAM;AAE5C,YAAI,OAAO,MAAM;AACf,iBAAO,YAAY,OAAO;AAC1B,iBAAO,QAAQ,SAAS,OAAQ,KAAK,OAAO,QAAQ,MAAM,IAAI,SAAU,CAAC;AAEzE,iBAAO,UAAU,OAAO,YAAY,CAAC,OAAO,QAAQ,KAAK,IAAI;AAC7D,iBAAO,MAAM,YAAY,SAAS,GAAG,MAAM,KAAK,IAAI,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG;AAC3E,iBAAO,MAAM,YAAY,YAAY,GAAG,MAAM,MAAM,KAAK,IAAI,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG;AAEpF,cAAI,KAAK,YAAa,QAAO,QAAQ,OAAO,OAAO,KAAK,KAAK;AAAA,QAC/D;AACA,YAAI,OAAO,MAAM;AAAA,MACnB,CAAC;AACD,cAAQ,OAAO,GAAG;AAAA,IACpB,CAAC;AACD,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  Utils
3
- } from "./chunk-MISGHDUU.mjs";
3
+ } from "./chunk-6OMRGML6.mjs";
4
4
  import {
5
5
  Component
6
- } from "./chunk-QSQ24FYT.mjs";
6
+ } from "./chunk-LTWPGFBR.mjs";
7
7
 
8
8
  // src/ts/components/menu.ts
9
9
  var MIN_SURFACE_WIDTH = 112;
@@ -16,6 +16,7 @@ var _defaults = {
16
16
  coverTrigger: false,
17
17
  closeOnClick: true,
18
18
  hover: false,
19
+ contextMenu: false,
19
20
  inDuration: 150,
20
21
  outDuration: 250,
21
22
  onOpenStart: null,
@@ -30,6 +31,41 @@ var _Menu = class _Menu extends Component {
30
31
  super(el, options, _Menu);
31
32
  this._pendingTimers = /* @__PURE__ */ new Set();
32
33
  this._transition = 0;
34
+ this._menuClickTargets = /* @__PURE__ */ new WeakMap();
35
+ /**
36
+ * Viewport point the open context menu belongs to. Null outside a
37
+ * right-click, so a keyboard or programmatic open uses the region's corner
38
+ * rather than an old pointer position.
39
+ */
40
+ this._contextPoint = null;
41
+ /** What had focus when a context menu opened; focus goes back there. */
42
+ this._contextReturn = null;
43
+ this._handleContextMenu = (e) => {
44
+ if (e.defaultPrevented) {
45
+ this.close();
46
+ return;
47
+ }
48
+ const path = e.composedPath();
49
+ if (_Menu._menus.some((menu) => menu !== this && menu.isOpen && path.includes(menu.menuEl))) return;
50
+ e.preventDefault();
51
+ if (!this.isOpen) {
52
+ const root = this.el.getRootNode();
53
+ this._contextReturn = root.activeElement instanceof HTMLElement ? root.activeElement : null;
54
+ }
55
+ const rect = this.el.getBoundingClientRect();
56
+ const fromPointer = (e.clientX !== 0 || e.clientY !== 0) && e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
57
+ this._contextPoint = fromPointer ? { x: e.clientX, y: e.clientY } : null;
58
+ if (this.isOpen) this.recalculateDimensions();
59
+ else this.open();
60
+ };
61
+ this._handleDocumentContextMenu = (e) => {
62
+ const path = e.composedPath();
63
+ if (path.includes(this.menuEl)) {
64
+ e.preventDefault();
65
+ return;
66
+ }
67
+ if (!path.includes(this.el)) this.close();
68
+ };
33
69
  this._handleClick = (e) => {
34
70
  e.preventDefault();
35
71
  if (this.isOpen) {
@@ -58,11 +94,15 @@ var _Menu = class _Menu extends Component {
58
94
  }
59
95
  };
60
96
  this._handleDocumentClick = (e) => {
61
- const target = e.target;
97
+ const path = e.composedPath();
98
+ const menuTarget = this._menuClickTargets.get(e);
99
+ this._menuClickTargets.delete(e);
100
+ const target = menuTarget ?? (path[0] || e.target);
101
+ const inside = !!menuTarget || path.includes(this.menuEl) || this.menuEl?.contains(target);
62
102
  if (this._isSubmenuTriggerClick(target)) return;
63
- if (this.options.closeOnClick && this.menuEl?.contains(target) && !this.isTouchMoving) {
103
+ if (this.options.closeOnClick && inside && !this.isTouchMoving) {
64
104
  this.close();
65
- } else if (!this.menuEl?.contains(target)) {
105
+ } else if (!inside) {
66
106
  this._schedule(() => {
67
107
  if (this.isOpen) {
68
108
  this.close();
@@ -72,6 +112,7 @@ var _Menu = class _Menu extends Component {
72
112
  this.isTouchMoving = false;
73
113
  };
74
114
  this._handleTriggerKeydown = (e) => {
115
+ if (this.options.contextMenu) return;
75
116
  const opensMenu = e.key === SPACE || e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ARROW_UP;
76
117
  if (opensMenu && !this.isOpen) {
77
118
  e.preventDefault();
@@ -80,12 +121,13 @@ var _Menu = class _Menu extends Component {
80
121
  };
81
122
  this._handleDocumentTouchmove = (e) => {
82
123
  const target = e.target;
83
- if (this.menuEl?.contains(target)) {
124
+ if (e.composedPath().includes(this.menuEl) || this.menuEl?.contains(target)) {
84
125
  this.isTouchMoving = true;
85
126
  }
86
127
  };
87
128
  this._handleMenuClick = (e) => {
88
129
  const target = e.target;
130
+ this._menuClickTargets.set(e, target);
89
131
  const li = target.closest("li");
90
132
  if (li?.matches('.disabled, [aria-disabled="true"]')) {
91
133
  e.preventDefault();
@@ -229,7 +271,7 @@ var _Menu = class _Menu extends Component {
229
271
  this._placeMenu();
230
272
  this._schedule(() => this._setupTemporaryEventHandlers(), 0);
231
273
  this._animateIn();
232
- this.el.ariaExpanded = "true";
274
+ this._setExpanded(true);
233
275
  };
234
276
  /**
235
277
  * Close menu.
@@ -251,10 +293,13 @@ var _Menu = class _Menu extends Component {
251
293
  if (transition !== this._transition) return;
252
294
  this._animateOut();
253
295
  this._removeTemporaryEventHandlers();
254
- this.el.ariaExpanded = "false";
255
- if (this.options.autoFocus && (root.activeElement === focused || !root.activeElement || root.activeElement === document.body)) {
256
- this.el.focus();
296
+ this._setExpanded(false);
297
+ if (this.options.autoFocus && this.menuEl.contains(focused) && (root.activeElement === focused || this.el.ownerDocument.activeElement === this.el.ownerDocument.body)) {
298
+ const back = this._contextReturn;
299
+ (back?.isConnected && back !== this.el.ownerDocument.body ? back : this.el).focus();
257
300
  }
301
+ this._contextReturn = null;
302
+ this._contextPoint = null;
258
303
  };
259
304
  /**
260
305
  * While menu is open, you can recalculate its dimensions if its contents have changed.
@@ -272,6 +317,7 @@ var _Menu = class _Menu extends Component {
272
317
  this._originalInert = this.menuEl?.getAttribute("inert") ?? null;
273
318
  this.options = {
274
319
  ..._Menu.defaults,
320
+ contextMenu: el.hasAttribute("data-context-menu"),
275
321
  ...options
276
322
  };
277
323
  this.isOpen = false;
@@ -279,10 +325,12 @@ var _Menu = class _Menu extends Component {
279
325
  this.isTouchMoving = false;
280
326
  this.focusedIndex = -1;
281
327
  this.filterQuery = [];
282
- this.el.ariaExpanded = "false";
283
- if (!this.el.hasAttribute("aria-haspopup")) this.el.setAttribute("aria-haspopup", "menu");
284
- if (this.id && !this.el.hasAttribute("aria-controls"))
285
- this.el.setAttribute("aria-controls", this.id);
328
+ if (!this.options.contextMenu) {
329
+ this.el.ariaExpanded = "false";
330
+ if (!this.el.hasAttribute("aria-haspopup")) this.el.setAttribute("aria-haspopup", "menu");
331
+ if (this.id && !this.el.hasAttribute("aria-controls"))
332
+ this.el.setAttribute("aria-controls", this.id);
333
+ }
286
334
  this._moveMenuToElement();
287
335
  this._setupAccessibility();
288
336
  this._makeMenuFocusable();
@@ -306,7 +354,7 @@ var _Menu = class _Menu extends Component {
306
354
  destroy() {
307
355
  this._clearPendingTimers();
308
356
  this.isOpen = false;
309
- this.el.ariaExpanded = "false";
357
+ this._setExpanded(false);
310
358
  clearTimeout(this.filterTimeout);
311
359
  this._resetMenuStyles();
312
360
  this._restoreInert();
@@ -322,7 +370,9 @@ var _Menu = class _Menu extends Component {
322
370
  if (this.menuEl?.querySelector(":scope > li > menu")) {
323
371
  this.menuEl.addEventListener("mouseover", this._handleSubmenuAlign);
324
372
  }
325
- if (this.options.hover) {
373
+ if (this.options.contextMenu) {
374
+ this.el.addEventListener("contextmenu", this._handleContextMenu);
375
+ } else if (this.options.hover) {
326
376
  this.el.addEventListener("mouseenter", this._handleMouseEnter);
327
377
  this.el.addEventListener("mouseleave", this._handleMouseLeave);
328
378
  this.menuEl.addEventListener("mouseleave", this._handleMouseLeave);
@@ -334,7 +384,9 @@ var _Menu = class _Menu extends Component {
334
384
  this.el.removeEventListener("keydown", this._handleTriggerKeydown);
335
385
  this.menuEl?.removeEventListener("click", this._handleMenuClick);
336
386
  this.menuEl?.removeEventListener("mouseover", this._handleSubmenuAlign);
337
- if (this.options.hover) {
387
+ if (this.options.contextMenu) {
388
+ this.el.removeEventListener("contextmenu", this._handleContextMenu);
389
+ } else if (this.options.hover) {
338
390
  this.el.removeEventListener("mouseenter", this._handleMouseEnter);
339
391
  this.el.removeEventListener("mouseleave", this._handleMouseLeave);
340
392
  this.menuEl?.removeEventListener("mouseleave", this._handleMouseLeave);
@@ -344,17 +396,21 @@ var _Menu = class _Menu extends Component {
344
396
  }
345
397
  _setupTemporaryEventHandlers() {
346
398
  document.body.addEventListener("click", this._handleDocumentClick);
399
+ this.menuEl?.addEventListener("touchmove", this._handleDocumentTouchmove, { passive: true });
347
400
  document.body.addEventListener("touchmove", this._handleDocumentTouchmove, {
348
401
  passive: true
349
402
  });
350
403
  this.menuEl?.addEventListener("keydown", this._handleMenuKeydown);
351
404
  window.addEventListener("resize", this._handleWindowResize);
405
+ if (this.options.contextMenu) document.addEventListener("contextmenu", this._handleDocumentContextMenu);
352
406
  }
353
407
  _removeTemporaryEventHandlers() {
354
408
  document.body.removeEventListener("click", this._handleDocumentClick);
409
+ this.menuEl?.removeEventListener("touchmove", this._handleDocumentTouchmove);
355
410
  document.body.removeEventListener("touchmove", this._handleDocumentTouchmove);
356
411
  this.menuEl?.removeEventListener("keydown", this._handleMenuKeydown);
357
412
  window.removeEventListener("resize", this._handleWindowResize);
413
+ document.removeEventListener("contextmenu", this._handleDocumentContextMenu);
358
414
  }
359
415
  _setupAccessibility() {
360
416
  if (!this.menuEl) return;
@@ -644,8 +700,12 @@ var _Menu = class _Menu extends Component {
644
700
  closestOverflowParent.style.position = "relative";
645
701
  const natural = this.menuEl.getBoundingClientRect().width;
646
702
  const triggerWidth = this.el.getBoundingClientRect().width;
647
- const idealWidth = this.options.constrainWidth && triggerWidth >= MIN_SURFACE_WIDTH ? triggerWidth : Math.min(MAX_SURFACE_WIDTH, Math.max(MIN_SURFACE_WIDTH, natural));
703
+ const idealWidth = this.options.constrainWidth && !this.options.contextMenu && triggerWidth >= MIN_SURFACE_WIDTH ? triggerWidth : Math.min(MAX_SURFACE_WIDTH, Math.max(MIN_SURFACE_WIDTH, natural));
648
704
  this.menuEl.style.width = idealWidth + "px";
705
+ if (this.options.contextMenu) {
706
+ this._placeMenuAtPoint(this._contextPoint ?? this._regionCorner(), closestOverflowParent);
707
+ return;
708
+ }
649
709
  const positionInfo = this._getMenuPosition(closestOverflowParent);
650
710
  this.menuEl.style.left = positionInfo.x + "px";
651
711
  this.menuEl.style.top = positionInfo.y + "px";
@@ -653,6 +713,58 @@ var _Menu = class _Menu extends Component {
653
713
  this.menuEl.style.width = positionInfo.width + "px";
654
714
  this.menuEl.style.transformOrigin = `${positionInfo.horizontalAlignment === "left" ? "0" : "100%"} ${positionInfo.verticalAlignment === "top" ? "0" : "100%"}`;
655
715
  }
716
+ /** Where a context menu opens without a pointer: the region's start corner. */
717
+ _regionCorner() {
718
+ const rect = this.el.getBoundingClientRect();
719
+ const rtl = getComputedStyle(this.el).direction === "rtl";
720
+ return { x: rtl ? rect.right : rect.left, y: rect.bottom };
721
+ }
722
+ /**
723
+ * Places a context menu with its start corner on the point, flipping to
724
+ * the other side of the point where it would not fit. The bounds are the
725
+ * viewport, narrowed to a scrolling or clipping ancestor the menu sits in;
726
+ * a menu taller than them gets their height and scrolls.
727
+ *
728
+ * The menu rests at left/top 0 of its containing block, so its rect is the
729
+ * offset to subtract - the same trick _getMenuPosition uses. It is measured
730
+ * without the entry animation's scale, which a second right-click can catch.
731
+ */
732
+ _placeMenuAtPoint({ x, y }, container) {
733
+ const { transform, transition } = this.menuEl.style;
734
+ this.menuEl.style.transition = "none";
735
+ this.menuEl.style.transform = "none";
736
+ const menu = this.menuEl.getBoundingClientRect();
737
+ this.menuEl.style.transform = transform;
738
+ this.menuEl.style.transition = transition;
739
+ let bounds = { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
740
+ if (container && getComputedStyle(container).overflow !== "visible") {
741
+ const clip = container.getBoundingClientRect();
742
+ bounds = {
743
+ left: Math.max(bounds.left, clip.left),
744
+ top: Math.max(bounds.top, clip.top),
745
+ right: Math.min(bounds.right, clip.right),
746
+ bottom: Math.min(bounds.bottom, clip.bottom)
747
+ };
748
+ }
749
+ const width = menu.width;
750
+ const height = Math.min(menu.height, bounds.bottom - bounds.top);
751
+ this.isScrollable = height < menu.height;
752
+ if (this.isScrollable) this.menuEl.style.height = height + "px";
753
+ const rtl = getComputedStyle(this.el).direction === "rtl";
754
+ const fitsRight = x + width <= bounds.right;
755
+ const fitsLeft = x - width >= bounds.left;
756
+ const leftward = rtl ? fitsLeft || !fitsRight : !fitsRight && fitsLeft;
757
+ const upward = y + height > bounds.bottom && y - height >= bounds.top;
758
+ const clamp = (value, min, max) => Math.max(min, Math.min(value, max));
759
+ const left = clamp(leftward ? x - width : x, bounds.left, bounds.right - width);
760
+ const top = clamp(upward ? y - height : y, bounds.top, bounds.bottom - height);
761
+ this.menuEl.style.left = left - menu.left + "px";
762
+ this.menuEl.style.top = top - menu.top + "px";
763
+ this.menuEl.style.transformOrigin = `${leftward ? "100%" : "0"} ${upward ? "100%" : "0"}`;
764
+ }
765
+ _setExpanded(expanded) {
766
+ if (!this.options.contextMenu) this.el.ariaExpanded = String(expanded);
767
+ }
656
768
  };
657
769
  _Menu._menus = [];
658
770
  var Menu = _Menu;
@@ -660,4 +772,4 @@ var Menu = _Menu;
660
772
  export {
661
773
  Menu
662
774
  };
663
- //# sourceMappingURL=chunk-TK6PX2GC.mjs.map
775
+ //# sourceMappingURL=chunk-U2OTHOTI.mjs.map