ngwr 10.2.1 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) hide show
  1. package/README.md +39 -19
  2. package/action-sheet/styles/_index.scss +4 -1
  3. package/anchor/styles/_index.scss +6 -5
  4. package/back-top/styles/_index.scss +6 -1
  5. package/blur-text/styles/_index.scss +13 -0
  6. package/button/styles/_index.scss +11 -6
  7. package/calendar/styles/_index.scss +11 -2
  8. package/carousel/styles/_index.scss +13 -2
  9. package/cascader/styles/_index.scss +5 -3
  10. package/circular-text/styles/_index.scss +2 -0
  11. package/collapse/styles/_index.scss +1 -1
  12. package/color-picker/styles/_index.scss +6 -1
  13. package/command-palette/styles/_index.scss +2 -1
  14. package/compare/styles/_index.scss +1 -2
  15. package/context-menu/styles/_index.scss +8 -2
  16. package/descriptions/styles/_index.scss +2 -2
  17. package/dialog/styles/_index.scss +7 -0
  18. package/drawer/styles/_index.scss +20 -1
  19. package/dropdown/styles/_index.scss +19 -5
  20. package/empty/styles/_index.scss +10 -0
  21. package/falling-text/styles/_index.scss +2 -1
  22. package/fesm2022/ngwr-action-sheet.mjs +13 -3
  23. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  24. package/fesm2022/ngwr-affix.mjs +40 -13
  25. package/fesm2022/ngwr-affix.mjs.map +1 -1
  26. package/fesm2022/ngwr-anchor.mjs +18 -6
  27. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  28. package/fesm2022/ngwr-aurora.mjs +2 -2
  29. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  30. package/fesm2022/ngwr-avatar.mjs +31 -7
  31. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  32. package/fesm2022/ngwr-back-top.mjs +6 -2
  33. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  34. package/fesm2022/ngwr-badge.mjs +1 -1
  35. package/fesm2022/ngwr-badge.mjs.map +1 -1
  36. package/fesm2022/ngwr-bar-chart.mjs +30 -9
  37. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  38. package/fesm2022/ngwr-blur-text.mjs +2 -2
  39. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  40. package/fesm2022/ngwr-border-glow.mjs +2 -2
  41. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  42. package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
  43. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  44. package/fesm2022/ngwr-burger.mjs +9 -4
  45. package/fesm2022/ngwr-burger.mjs.map +1 -1
  46. package/fesm2022/ngwr-button-testing.mjs +96 -0
  47. package/fesm2022/ngwr-button-testing.mjs.map +1 -0
  48. package/fesm2022/ngwr-button.mjs +31 -14
  49. package/fesm2022/ngwr-button.mjs.map +1 -1
  50. package/fesm2022/ngwr-calendar-heatmap.mjs +40 -8
  51. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  52. package/fesm2022/ngwr-calendar.mjs +20 -1
  53. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  54. package/fesm2022/ngwr-card.mjs +2 -2
  55. package/fesm2022/ngwr-card.mjs.map +1 -1
  56. package/fesm2022/ngwr-carousel-testing.mjs +377 -0
  57. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -0
  58. package/fesm2022/ngwr-carousel.mjs +67 -13
  59. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  60. package/fesm2022/ngwr-cascader-testing.mjs +534 -0
  61. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -0
  62. package/fesm2022/ngwr-cascader.mjs +2 -2
  63. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  64. package/fesm2022/ngwr-checkbox-testing.mjs +90 -0
  65. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -0
  66. package/fesm2022/ngwr-checkbox.mjs +9 -3
  67. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  68. package/fesm2022/ngwr-circular-text.mjs +2 -2
  69. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  70. package/fesm2022/ngwr-click-spark.mjs +2 -2
  71. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  72. package/fesm2022/ngwr-collapse-testing.mjs +368 -0
  73. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -0
  74. package/fesm2022/ngwr-color-picker.mjs +134 -14
  75. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  76. package/fesm2022/ngwr-command-palette-testing.mjs +589 -0
  77. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -0
  78. package/fesm2022/ngwr-command-palette.mjs +58 -8
  79. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  80. package/fesm2022/ngwr-compare.mjs +103 -13
  81. package/fesm2022/ngwr-compare.mjs.map +1 -1
  82. package/fesm2022/ngwr-config.mjs +106 -0
  83. package/fesm2022/ngwr-config.mjs.map +1 -0
  84. package/fesm2022/ngwr-context-menu-testing.mjs +631 -0
  85. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -0
  86. package/fesm2022/ngwr-context-menu.mjs +94 -8
  87. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  88. package/fesm2022/ngwr-cookie.mjs +5 -1
  89. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  90. package/fesm2022/ngwr-counter.mjs +11 -3
  91. package/fesm2022/ngwr-counter.mjs.map +1 -1
  92. package/fesm2022/ngwr-date-adapter-luxon.mjs +52 -4
  93. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -1
  94. package/fesm2022/ngwr-date-adapter.mjs +54 -8
  95. package/fesm2022/ngwr-date-adapter.mjs.map +1 -1
  96. package/fesm2022/ngwr-date-picker-testing.mjs +766 -0
  97. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -0
  98. package/fesm2022/ngwr-date-picker.mjs +73 -3
  99. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  100. package/fesm2022/ngwr-decrypt-text.mjs +27 -9
  101. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  102. package/fesm2022/ngwr-dialog-testing.mjs +89 -0
  103. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -0
  104. package/fesm2022/ngwr-directives.mjs +36 -12
  105. package/fesm2022/ngwr-directives.mjs.map +1 -1
  106. package/fesm2022/ngwr-donut-chart.mjs +22 -5
  107. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  108. package/fesm2022/ngwr-drag-drop.mjs +2 -2
  109. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  110. package/fesm2022/ngwr-drawer-testing.mjs +250 -0
  111. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -0
  112. package/fesm2022/ngwr-dropdown-testing.mjs +291 -0
  113. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -0
  114. package/fesm2022/ngwr-dropdown.mjs +10 -2
  115. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  116. package/fesm2022/ngwr-empty.mjs +24 -7
  117. package/fesm2022/ngwr-empty.mjs.map +1 -1
  118. package/fesm2022/ngwr-event-calendar.mjs +57 -10
  119. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  120. package/fesm2022/ngwr-falling-text.mjs +2 -2
  121. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  122. package/fesm2022/ngwr-file-upload-testing.mjs +332 -0
  123. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -0
  124. package/fesm2022/ngwr-form-testing.mjs +451 -0
  125. package/fesm2022/ngwr-form-testing.mjs.map +1 -0
  126. package/fesm2022/ngwr-form.mjs +19 -3
  127. package/fesm2022/ngwr-form.mjs.map +1 -1
  128. package/fesm2022/ngwr-fuzzy-text.mjs +9 -2
  129. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  130. package/fesm2022/ngwr-gauge.mjs +19 -5
  131. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  132. package/fesm2022/ngwr-glitch-text.mjs +2 -2
  133. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  134. package/fesm2022/ngwr-gradient-text.mjs +2 -2
  135. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  136. package/fesm2022/ngwr-i18n-en.mjs +50 -0
  137. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  138. package/fesm2022/ngwr-i18n-ru.mjs +48 -0
  139. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  140. package/fesm2022/ngwr-image-cropper.mjs +23 -0
  141. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  142. package/fesm2022/ngwr-input-number-testing.mjs +306 -0
  143. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -0
  144. package/fesm2022/ngwr-input-number.mjs +35 -3
  145. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  146. package/fesm2022/ngwr-input-otp-testing.mjs +419 -0
  147. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -0
  148. package/fesm2022/ngwr-input-otp.mjs +36 -5
  149. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  150. package/fesm2022/ngwr-input-testing.mjs +102 -0
  151. package/fesm2022/ngwr-input-testing.mjs.map +1 -0
  152. package/fesm2022/ngwr-input.mjs +47 -10
  153. package/fesm2022/ngwr-input.mjs.map +1 -1
  154. package/fesm2022/ngwr-knob.mjs +44 -8
  155. package/fesm2022/ngwr-knob.mjs.map +1 -1
  156. package/fesm2022/ngwr-layout.mjs +21 -5
  157. package/fesm2022/ngwr-layout.mjs.map +1 -1
  158. package/fesm2022/ngwr-lightbox.mjs +33 -6
  159. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  160. package/fesm2022/ngwr-line-chart.mjs +17 -4
  161. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  162. package/fesm2022/ngwr-list.mjs +2 -2
  163. package/fesm2022/ngwr-list.mjs.map +1 -1
  164. package/fesm2022/ngwr-loading-bar.mjs +2 -2
  165. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  166. package/fesm2022/ngwr-markdown-testing.mjs +516 -0
  167. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -0
  168. package/fesm2022/ngwr-markdown.mjs +1492 -0
  169. package/fesm2022/ngwr-markdown.mjs.map +1 -0
  170. package/fesm2022/ngwr-marquee.mjs +17 -6
  171. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  172. package/fesm2022/ngwr-mention-testing.mjs +513 -0
  173. package/fesm2022/ngwr-mention-testing.mjs.map +1 -0
  174. package/fesm2022/ngwr-mention.mjs +10 -1
  175. package/fesm2022/ngwr-mention.mjs.map +1 -1
  176. package/fesm2022/ngwr-meta.mjs +22 -7
  177. package/fesm2022/ngwr-meta.mjs.map +1 -1
  178. package/fesm2022/ngwr-meter-group.mjs +19 -5
  179. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  180. package/fesm2022/ngwr-overlay.mjs +41 -1
  181. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  182. package/fesm2022/ngwr-pagination-testing.mjs +389 -0
  183. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -0
  184. package/fesm2022/ngwr-pagination.mjs +14 -2
  185. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  186. package/fesm2022/ngwr-popconfirm-testing.mjs +309 -0
  187. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -0
  188. package/fesm2022/ngwr-popconfirm.mjs +95 -17
  189. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  190. package/fesm2022/ngwr-popover-testing.mjs +316 -0
  191. package/fesm2022/ngwr-popover-testing.mjs.map +1 -0
  192. package/fesm2022/ngwr-popover.mjs +21 -3
  193. package/fesm2022/ngwr-popover.mjs.map +1 -1
  194. package/fesm2022/ngwr-pull-to-refresh.mjs +17 -2
  195. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  196. package/fesm2022/ngwr-qr.mjs +13 -3
  197. package/fesm2022/ngwr-qr.mjs.map +1 -1
  198. package/fesm2022/ngwr-radio-testing.mjs +392 -0
  199. package/fesm2022/ngwr-radio-testing.mjs.map +1 -0
  200. package/fesm2022/ngwr-radio.mjs +10 -3
  201. package/fesm2022/ngwr-radio.mjs.map +1 -1
  202. package/fesm2022/ngwr-rating-testing.mjs +384 -0
  203. package/fesm2022/ngwr-rating-testing.mjs.map +1 -0
  204. package/fesm2022/ngwr-rating.mjs +37 -2
  205. package/fesm2022/ngwr-rating.mjs.map +1 -1
  206. package/fesm2022/ngwr-segmented-testing.mjs +512 -0
  207. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -0
  208. package/fesm2022/ngwr-select-testing.mjs +268 -0
  209. package/fesm2022/ngwr-select-testing.mjs.map +1 -0
  210. package/fesm2022/ngwr-select.mjs +87 -16
  211. package/fesm2022/ngwr-select.mjs.map +1 -1
  212. package/fesm2022/ngwr-shiny-text.mjs +2 -2
  213. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  214. package/fesm2022/ngwr-sidebar.mjs +65 -12
  215. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  216. package/fesm2022/ngwr-slider-testing.mjs +478 -0
  217. package/fesm2022/ngwr-slider-testing.mjs.map +1 -0
  218. package/fesm2022/ngwr-slider.mjs +57 -13
  219. package/fesm2022/ngwr-slider.mjs.map +1 -1
  220. package/fesm2022/ngwr-sparkline.mjs +26 -6
  221. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  222. package/fesm2022/ngwr-speed-dial.mjs +37 -4
  223. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  224. package/fesm2022/ngwr-spinner.mjs +12 -3
  225. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  226. package/fesm2022/ngwr-splash-cursor.mjs +2 -2
  227. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  228. package/fesm2022/ngwr-split-text.mjs +2 -2
  229. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  230. package/fesm2022/ngwr-splitter.mjs +70 -9
  231. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  232. package/fesm2022/ngwr-spotlight-card.mjs +2 -2
  233. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  234. package/fesm2022/ngwr-star-border.mjs +2 -2
  235. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  236. package/fesm2022/ngwr-statistic.mjs +24 -7
  237. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  238. package/fesm2022/ngwr-stepper-testing.mjs +477 -0
  239. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -0
  240. package/fesm2022/ngwr-switch-testing.mjs +77 -0
  241. package/fesm2022/ngwr-switch-testing.mjs.map +1 -0
  242. package/fesm2022/ngwr-switch.mjs +9 -3
  243. package/fesm2022/ngwr-switch.mjs.map +1 -1
  244. package/fesm2022/ngwr-table-testing.mjs +580 -0
  245. package/fesm2022/ngwr-table-testing.mjs.map +1 -0
  246. package/fesm2022/ngwr-table.mjs +87 -14
  247. package/fesm2022/ngwr-table.mjs.map +1 -1
  248. package/fesm2022/ngwr-tabs-testing.mjs +617 -0
  249. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -0
  250. package/fesm2022/ngwr-tabs.mjs +38 -6
  251. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  252. package/fesm2022/ngwr-textarea-testing.mjs +238 -0
  253. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -0
  254. package/fesm2022/ngwr-textarea.mjs +32 -4
  255. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  256. package/fesm2022/ngwr-tilt-card.mjs +2 -2
  257. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  258. package/fesm2022/ngwr-toast-testing.mjs +101 -0
  259. package/fesm2022/ngwr-toast-testing.mjs.map +1 -0
  260. package/fesm2022/ngwr-toast.mjs +2 -2
  261. package/fesm2022/ngwr-toast.mjs.map +1 -1
  262. package/fesm2022/ngwr-tour.mjs +61 -9
  263. package/fesm2022/ngwr-tour.mjs.map +1 -1
  264. package/fesm2022/ngwr-transfer-testing.mjs +538 -0
  265. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -0
  266. package/fesm2022/ngwr-transfer.mjs +45 -14
  267. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  268. package/fesm2022/ngwr-tree-testing.mjs +734 -0
  269. package/fesm2022/ngwr-tree-testing.mjs.map +1 -0
  270. package/fesm2022/ngwr-tree.mjs +73 -7
  271. package/fesm2022/ngwr-tree.mjs.map +1 -1
  272. package/fesm2022/ngwr-typewriter.mjs +5 -3
  273. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  274. package/fesm2022/ngwr-version.mjs +1 -1
  275. package/fesm2022/ngwr-version.mjs.map +1 -1
  276. package/fesm2022/ngwr-virtual-scroll.mjs +31 -5
  277. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  278. package/fesm2022/ngwr-waves.mjs +3 -3
  279. package/fesm2022/ngwr-waves.mjs.map +1 -1
  280. package/fesm2022/ngwr-window.mjs +8 -0
  281. package/fesm2022/ngwr-window.mjs.map +1 -1
  282. package/file-upload/styles/_index.scss +7 -2
  283. package/gauge/styles/_index.scss +1 -1
  284. package/glitch-text/styles/_index.scss +4 -0
  285. package/i18n/en.json +48 -0
  286. package/i18n/ru.json +48 -0
  287. package/image-cropper/styles/_index.scss +13 -1
  288. package/input/styles/_index.scss +2 -2
  289. package/input-number/styles/_index.scss +7 -2
  290. package/knob/styles/_index.scss +8 -1
  291. package/layout/styles/_index.scss +3 -3
  292. package/lightbox/styles/_index.scss +10 -2
  293. package/line-chart/styles/_index.scss +6 -0
  294. package/llms-full.txt +151 -4
  295. package/llms.txt +57 -8
  296. package/loading-bar/styles/_index.scss +1 -2
  297. package/markdown/styles/_index.scss +296 -0
  298. package/marquee/styles/_index.scss +12 -0
  299. package/mcp/api.js +512 -0
  300. package/mcp/catalog.js +157 -0
  301. package/mcp/server.js +273 -0
  302. package/mcp/tools.js +366 -0
  303. package/meter-group/styles/_index.scss +1 -1
  304. package/package.json +186 -35
  305. package/pagination/styles/_index.scss +6 -6
  306. package/popover/styles/_index.scss +37 -5
  307. package/rating/styles/_index.scss +10 -0
  308. package/result/styles/_index.scss +3 -1
  309. package/schematics/use/symbol-map.json +2 -0
  310. package/segmented/styles/_index.scss +12 -1
  311. package/select/styles/_index.scss +32 -10
  312. package/sidebar/styles/_index.scss +30 -5
  313. package/slider/styles/_index.scss +15 -0
  314. package/speed-dial/styles/_index.scss +20 -0
  315. package/split-text/styles/_index.scss +13 -0
  316. package/star-border/styles/_index.scss +2 -0
  317. package/stepper/styles/_index.scss +19 -3
  318. package/styles/_grid.scss +4 -4
  319. package/styles.scss +1 -0
  320. package/switch/styles/_index.scss +5 -3
  321. package/table/styles/_index.scss +25 -12
  322. package/tabs/styles/_index.scss +11 -1
  323. package/textarea/styles/_index.scss +5 -0
  324. package/theme/styles/_colors.scss +44 -15
  325. package/theme/styles/_dark.scss +35 -15
  326. package/theme/styles/_mixins.scss +3 -0
  327. package/timeline/styles/_index.scss +2 -2
  328. package/toast/styles/_index.scss +17 -8
  329. package/toolbar/styles/_index.scss +3 -3
  330. package/tour/styles/_index.scss +7 -1
  331. package/tree/styles/_index.scss +9 -5
  332. package/types/ngwr-action-sheet.d.ts +9 -1
  333. package/types/ngwr-affix.d.ts +7 -1
  334. package/types/ngwr-anchor.d.ts +8 -1
  335. package/types/ngwr-avatar.d.ts +13 -3
  336. package/types/ngwr-bar-chart.d.ts +16 -0
  337. package/types/ngwr-burger.d.ts +6 -2
  338. package/types/ngwr-button-testing.d.ts +85 -0
  339. package/types/ngwr-button.d.ts +26 -10
  340. package/types/ngwr-calendar-heatmap.d.ts +16 -2
  341. package/types/ngwr-calendar.d.ts +10 -0
  342. package/types/ngwr-carousel-testing.d.ts +257 -0
  343. package/types/ngwr-carousel.d.ts +20 -4
  344. package/types/ngwr-cascader-testing.d.ts +392 -0
  345. package/types/ngwr-checkbox-testing.d.ts +77 -0
  346. package/types/ngwr-checkbox.d.ts +7 -2
  347. package/types/ngwr-collapse-testing.d.ts +290 -0
  348. package/types/ngwr-color-picker.d.ts +20 -11
  349. package/types/ngwr-command-palette-testing.d.ts +437 -0
  350. package/types/ngwr-command-palette.d.ts +25 -2
  351. package/types/ngwr-compare.d.ts +50 -2
  352. package/types/ngwr-config.d.ts +176 -0
  353. package/types/ngwr-context-menu-testing.d.ts +401 -0
  354. package/types/ngwr-context-menu.d.ts +65 -17
  355. package/types/ngwr-counter.d.ts +6 -1
  356. package/types/ngwr-date-adapter-luxon.d.ts +26 -0
  357. package/types/ngwr-date-picker-testing.d.ts +518 -0
  358. package/types/ngwr-date-picker.d.ts +4 -0
  359. package/types/ngwr-dialog-testing.d.ts +79 -0
  360. package/types/ngwr-directives.d.ts +10 -1
  361. package/types/ngwr-donut-chart.d.ts +8 -1
  362. package/types/ngwr-drawer-testing.d.ts +196 -0
  363. package/types/ngwr-dropdown-testing.d.ts +202 -0
  364. package/types/ngwr-dropdown.d.ts +7 -1
  365. package/types/ngwr-empty.d.ts +19 -4
  366. package/types/ngwr-event-calendar.d.ts +26 -1
  367. package/types/ngwr-file-upload-testing.d.ts +194 -0
  368. package/types/ngwr-form-testing.d.ts +379 -0
  369. package/types/ngwr-form.d.ts +17 -0
  370. package/types/ngwr-gauge.d.ts +13 -1
  371. package/types/ngwr-image-cropper.d.ts +10 -0
  372. package/types/ngwr-input-number-testing.d.ts +226 -0
  373. package/types/ngwr-input-number.d.ts +29 -1
  374. package/types/ngwr-input-otp-testing.d.ts +299 -0
  375. package/types/ngwr-input-otp.d.ts +23 -2
  376. package/types/ngwr-input-testing.d.ts +84 -0
  377. package/types/ngwr-input.d.ts +20 -6
  378. package/types/ngwr-knob.d.ts +18 -0
  379. package/types/ngwr-layout.d.ts +1 -0
  380. package/types/ngwr-lightbox.d.ts +17 -3
  381. package/types/ngwr-line-chart.d.ts +9 -2
  382. package/types/ngwr-markdown-testing.d.ts +462 -0
  383. package/types/ngwr-markdown.d.ts +534 -0
  384. package/types/ngwr-marquee.d.ts +12 -2
  385. package/types/ngwr-mention-testing.d.ts +359 -0
  386. package/types/ngwr-meta.d.ts +4 -2
  387. package/types/ngwr-meter-group.d.ts +11 -1
  388. package/types/ngwr-overlay.d.ts +33 -2
  389. package/types/ngwr-pagination-testing.d.ts +291 -0
  390. package/types/ngwr-pagination.d.ts +1 -0
  391. package/types/ngwr-popconfirm-testing.d.ts +235 -0
  392. package/types/ngwr-popconfirm.d.ts +31 -21
  393. package/types/ngwr-popover-testing.d.ts +221 -0
  394. package/types/ngwr-popover.d.ts +9 -0
  395. package/types/ngwr-pull-to-refresh.d.ts +7 -0
  396. package/types/ngwr-qr.d.ts +9 -1
  397. package/types/ngwr-radio-testing.d.ts +296 -0
  398. package/types/ngwr-radio.d.ts +8 -2
  399. package/types/ngwr-rating-testing.d.ts +291 -0
  400. package/types/ngwr-rating.d.ts +23 -0
  401. package/types/ngwr-segmented-testing.d.ts +401 -0
  402. package/types/ngwr-select-testing.d.ts +175 -0
  403. package/types/ngwr-select.d.ts +45 -4
  404. package/types/ngwr-sidebar.d.ts +17 -2
  405. package/types/ngwr-slider-testing.d.ts +329 -0
  406. package/types/ngwr-slider.d.ts +35 -1
  407. package/types/ngwr-sparkline.d.ts +12 -2
  408. package/types/ngwr-speed-dial.d.ts +17 -18
  409. package/types/ngwr-spinner.d.ts +8 -1
  410. package/types/ngwr-splitter.d.ts +24 -1
  411. package/types/ngwr-statistic.d.ts +12 -2
  412. package/types/ngwr-stepper-testing.d.ts +346 -0
  413. package/types/ngwr-switch-testing.d.ts +66 -0
  414. package/types/ngwr-switch.d.ts +7 -2
  415. package/types/ngwr-table-testing.d.ts +423 -0
  416. package/types/ngwr-table.d.ts +42 -0
  417. package/types/ngwr-tabs-testing.d.ts +470 -0
  418. package/types/ngwr-tabs.d.ts +9 -0
  419. package/types/ngwr-textarea-testing.d.ts +184 -0
  420. package/types/ngwr-textarea.d.ts +7 -2
  421. package/types/ngwr-toast-testing.d.ts +80 -0
  422. package/types/ngwr-tour.d.ts +27 -0
  423. package/types/ngwr-transfer-testing.d.ts +444 -0
  424. package/types/ngwr-transfer.d.ts +16 -1
  425. package/types/ngwr-tree-testing.d.ts +501 -0
  426. package/types/ngwr-tree.d.ts +33 -0
  427. package/types/ngwr-version.d.ts +1 -1
  428. package/types/ngwr-virtual-scroll.d.ts +13 -1
  429. package/typewriter/styles/_index.scss +1 -1
  430. package/typography/styles/_index.scss +1 -1
  431. package/window/styles/_index.scss +32 -9
@@ -1,6 +1,7 @@
1
+ import { Directionality } from '@angular/cdk/bidi';
1
2
  import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
2
3
  import * as i0 from '@angular/core';
3
- import { input, computed, model, output, signal, viewChild, effect, ViewEncapsulation, Component } from '@angular/core';
4
+ import { input, computed, model, output, signal, viewChild, inject, effect, ViewEncapsulation, Component } from '@angular/core';
4
5
  import { clamp, round } from 'ngwr/utils';
5
6
 
6
7
  /**
@@ -22,6 +23,11 @@ import { clamp, round } from 'ngwr/utils';
22
23
  * Keyboard: ← / → adjust by `step`; ↑ / ↓ same; Shift+arrow by `step × 10`;
23
24
  * Home / End jump to min / max; PageUp / PageDown by `step × 10`.
24
25
  *
26
+ * Reading direction: the arrows follow the VISUAL track, so under `dir="rtl"`
27
+ * → moves toward the lower end and ← toward the higher one, and a drag reads
28
+ * its ratio from the right edge. ↑ / ↓, PageUp / PageDown and Home / End are
29
+ * unchanged — they name a value, not a side of the screen.
30
+ *
25
31
  * @example
26
32
  * ```html
27
33
  * <!-- signal forms -->
@@ -81,6 +87,17 @@ class WrSlider {
81
87
  ...(ngDevMode ? [{ debugName: "high" }] : /* istanbul ignore next */ []));
82
88
  track = viewChild.required('track', /* @ts-ignore */
83
89
  ...(ngDevMode ? [{ debugName: "track" }] : /* istanbul ignore next */ []));
90
+ /**
91
+ * Ambient reading direction. Optional so a bare `TestBed` — or any consumer
92
+ * that never set a direction — needs no provider; `Directionality` is
93
+ * root-provided, so `null` only ever means "nobody asked", which is LTR.
94
+ *
95
+ * Nothing caches a direction-derived value: the thumbs are painted with
96
+ * `inset-inline-start`, so the track mirrors from CSS alone, and `isRtl()` is
97
+ * read inside the event handlers. A runtime flip therefore needs no
98
+ * subscription to `Directionality.change`.
99
+ */
100
+ dir = inject(Directionality, { optional: true });
84
101
  classes = computed(() => {
85
102
  const parts = ['wr-slider'];
86
103
  if (this.range())
@@ -94,8 +111,9 @@ class WrSlider {
94
111
  ...(ngDevMode ? [{ debugName: "lowPercent" }] : /* istanbul ignore next */ []));
95
112
  highPercent = computed(() => this.percentOf(this.high()), /* @ts-ignore */
96
113
  ...(ngDevMode ? [{ debugName: "highPercent" }] : /* istanbul ignore next */ []));
97
- fillLeft = computed(() => (this.range() ? this.lowPercent() : 0), /* @ts-ignore */
98
- ...(ngDevMode ? [{ debugName: "fillLeft" }] : /* istanbul ignore next */ []));
114
+ /** Offset of the fill from the track's START edge — the left one in LTR, the right one in RTL. */
115
+ fillStart = computed(() => (this.range() ? this.lowPercent() : 0), /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "fillStart" }] : /* istanbul ignore next */ []));
99
117
  fillWidth = computed(() => this.range() ? this.highPercent() - this.lowPercent() : this.lowPercent(), /* @ts-ignore */
100
118
  ...(ngDevMode ? [{ debugName: "fillWidth" }] : /* istanbul ignore next */ []));
101
119
  label = computed(() => (this.range() ? `${this.low()} – ${this.high()}` : `${this.low()}`), /* @ts-ignore */
@@ -148,14 +166,21 @@ class WrSlider {
148
166
  const big = event.shiftKey || event.key === 'PageUp' || event.key === 'PageDown' ? 10 : 1;
149
167
  const delta = this.step() * big;
150
168
  const current = thumb === 'low' ? this.low() : this.high();
169
+ // The horizontal arrows name a SIDE of the track, so they follow the
170
+ // reading direction; every other key names a value and does not.
171
+ const inline = this.isRtl() ? -delta : delta;
151
172
  let next = null;
152
173
  switch (event.key) {
153
174
  case 'ArrowRight':
175
+ next = current + inline;
176
+ break;
177
+ case 'ArrowLeft':
178
+ next = current - inline;
179
+ break;
154
180
  case 'ArrowUp':
155
181
  case 'PageUp':
156
182
  next = current + delta;
157
183
  break;
158
- case 'ArrowLeft':
159
184
  case 'ArrowDown':
160
185
  case 'PageDown':
161
186
  next = current - delta;
@@ -175,19 +200,38 @@ class WrSlider {
175
200
  this.touch.emit();
176
201
  }
177
202
  // Internals
178
- updateFromEvent(event, thumb) {
203
+ isRtl() {
204
+ return this.dir?.value === 'rtl';
205
+ }
206
+ /**
207
+ * The value under a pointer, measured along the track's INLINE axis.
208
+ *
209
+ * The offset is taken from whichever edge the track starts at — the left one
210
+ * in LTR, the right one in RTL — so the whole component needs the direction
211
+ * in exactly one place. Measuring from `rect.left` regardless would read 0 at
212
+ * the visual maximum of a mirrored track.
213
+ */
214
+ valueAt(event) {
179
215
  const rect = this.track().nativeElement.getBoundingClientRect();
180
- const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);
181
- const raw = this.minValue() + ratio * (this.maxValue() - this.minValue());
182
- this.setThumb(thumb, raw);
216
+ const offset = this.isRtl() ? rect.right - event.clientX : event.clientX - rect.left;
217
+ const ratio = clamp(offset / rect.width, 0, 1);
218
+ return this.minValue() + ratio * (this.maxValue() - this.minValue());
219
+ }
220
+ updateFromEvent(event, thumb) {
221
+ this.setThumb(thumb, this.valueAt(event));
183
222
  this.emitChange();
184
223
  }
224
+ /**
225
+ * Which thumb a track click grabs: the one nearest the pointer ON SCREEN.
226
+ *
227
+ * Value distance is the same thing as visual distance once {@link valueAt}
228
+ * has mirrored the ratio — in RTL the low thumb is the RIGHT one, and a click
229
+ * near the right edge lands close to `low` in value space too.
230
+ */
185
231
  nearestThumb(event) {
186
232
  if (!this.range())
187
233
  return 'low';
188
- const rect = this.track().nativeElement.getBoundingClientRect();
189
- const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);
190
- const raw = this.minValue() + ratio * (this.maxValue() - this.minValue());
234
+ const raw = this.valueAt(event);
191
235
  return Math.abs(raw - this.low()) <= Math.abs(raw - this.high()) ? 'low' : 'high';
192
236
  }
193
237
  focusThumb(thumb) {
@@ -225,11 +269,11 @@ class WrSlider {
225
269
  this.value.set(value);
226
270
  }
227
271
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
228
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSlider, isStandalone: true, selector: "wr-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <span class=\"wr-slider__fill\" [style.left.%]=\"fillLeft()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.left.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.left.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
272
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSlider, isStandalone: true, selector: "wr-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
229
273
  }
230
274
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSlider, decorators: [{
231
275
  type: Component,
232
- args: [{ selector: 'wr-slider', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <span class=\"wr-slider__fill\" [style.left.%]=\"fillLeft()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.left.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.left.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n" }]
276
+ args: [{ selector: 'wr-slider', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n" }]
233
277
  }], ctorParameters: () => [], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], range: [{ type: i0.Input, args: [{ isSignal: true, alias: "range", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
234
278
 
235
279
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-slider.mjs","sources":["../../../projects/lib/slider/slider.ts","../../../projects/lib/slider/slider.html","../../../projects/lib/slider/ngwr-slider.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport type { ElementRef } from '@angular/core';\nimport { Component, ViewEncapsulation, computed, effect, input, model, output, signal, viewChild } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { clamp, round } from 'ngwr/utils';\n\nimport type { WrSliderValue } from './interfaces';\n\n/** Trim float drift from step calculations. */\n\n/**\n * Numeric slider with optional dual-thumb range mode.\n *\n * A signal-forms native control: it implements `FormValueControl<WrSliderValue>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone. The value\n * shape depends on `range`: `number` (default) or `[number, number]` when\n * `range=\"true\"`.\n *\n * Keyboard: ← / → adjust by `step`; ↑ / ↓ same; Shift+arrow by `step × 10`;\n * Home / End jump to min / max; PageUp / PageDown by `step × 10`.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-slider [formField]=\"form.volume\" min=\"0\" max=\"100\" step=\"5\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-slider [(value)]=\"range\" range min=\"0\" max=\"1000\" step=\"10\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/slider\n */\n@Component({\n selector: 'wr-slider',\n templateUrl: './slider.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrSlider implements FormValueControl<WrSliderValue> {\n // Typed `WrSliderValue | undefined` (not `number`) to satisfy the reserved\n // `FormUiControl` min/max slots, which are keyed to the control's value type.\n // The transform still coerces to a plain number, so the bounds stay numeric\n // at runtime; `minValue`/`maxValue` narrow them back for arithmetic.\n /** Lower bound. @default 0 */\n readonly min = input<WrSliderValue | undefined>(0, { transform: (v: unknown): number => coerceNumberProperty(v, 0) });\n /** Upper bound. @default 100 */\n readonly max = input<WrSliderValue | undefined>(100, {\n transform: (v: unknown): number => coerceNumberProperty(v, 100),\n });\n\n /** Resolved numeric lower bound for internal arithmetic. */\n private readonly minValue = computed(() => {\n const m = this.min();\n return typeof m === 'number' ? m : 0;\n });\n /** Resolved numeric upper bound for internal arithmetic. */\n private readonly maxValue = computed(() => {\n const m = this.max();\n return typeof m === 'number' ? m : 100;\n });\n\n /** Step size for keyboard and drag. @default 1 */\n readonly step = input(1, { transform: (v: unknown): number => Math.max(0.0001, coerceNumberProperty(v, 1)) });\n /** Render two thumbs and emit `[low, high]`. @default false */\n readonly range = input(false, { transform: coerceBooleanProperty });\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n /** Render the current value below the track. @default true */\n readonly showLabel = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Current value. Bound by `[formField]`, or two-way via `[(value)]`. Shape\n * follows `range`: a plain `number`, or `[low, high]` in range mode.\n */\n readonly value = model<WrSliderValue>(0);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly low = signal(0);\n protected readonly high = signal(100);\n\n protected readonly track = viewChild.required<ElementRef<HTMLElement>>('track');\n\n protected readonly classes = computed(() => {\n const parts = ['wr-slider'];\n if (this.range()) parts.push('wr-slider--range');\n if (this.disabled()) parts.push('wr-slider--disabled');\n return parts.join(' ');\n });\n\n protected readonly lowPercent = computed(() => this.percentOf(this.low()));\n protected readonly highPercent = computed(() => this.percentOf(this.high()));\n\n protected readonly fillLeft = computed(() => (this.range() ? this.lowPercent() : 0));\n protected readonly fillWidth = computed(() =>\n this.range() ? this.highPercent() - this.lowPercent() : this.lowPercent()\n );\n\n protected readonly label = computed(() => (this.range() ? `${this.low()} – ${this.high()}` : `${this.low()}`));\n\n constructor() {\n // Keep the internal thumbs in sync with external writes to `value`\n // (the old `writeValue`): split the tuple / clamp into the low & high cells.\n effect(() => {\n const v = this.value();\n if (Array.isArray(v)) {\n const tuple = v as readonly [number, number];\n this.low.set(this.clampToBounds(tuple[0]));\n this.high.set(this.clampToBounds(tuple[1]));\n } else if (typeof v === 'number') {\n this.low.set(this.clampToBounds(v));\n }\n });\n }\n\n // Interaction\n\n protected onPointerDown(event: PointerEvent, thumb: 'low' | 'high'): void {\n if (this.disabled()) return;\n event.preventDefault();\n const target = event.currentTarget as HTMLElement;\n target.setPointerCapture(event.pointerId);\n\n const move = (e: PointerEvent): void => this.updateFromEvent(e, thumb);\n const up = (e: PointerEvent): void => {\n target.releasePointerCapture(e.pointerId);\n target.removeEventListener('pointermove', move);\n target.removeEventListener('pointerup', up);\n this.touch.emit();\n };\n\n target.addEventListener('pointermove', move);\n target.addEventListener('pointerup', up);\n this.updateFromEvent(event, thumb);\n }\n\n protected onTrackPointerDown(event: PointerEvent): void {\n if (this.disabled()) return;\n if ((event.target as HTMLElement).closest('.wr-slider__thumb')) return;\n const thumb = this.nearestThumb(event);\n this.updateFromEvent(event, thumb);\n this.focusThumb(thumb);\n }\n\n protected onKey(event: KeyboardEvent, thumb: 'low' | 'high'): void {\n if (this.disabled()) return;\n const big = event.shiftKey || event.key === 'PageUp' || event.key === 'PageDown' ? 10 : 1;\n const delta = this.step() * big;\n const current = thumb === 'low' ? this.low() : this.high();\n let next: number | null = null;\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n case 'PageUp':\n next = current + delta;\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n case 'PageDown':\n next = current - delta;\n break;\n case 'Home':\n next = this.minValue();\n break;\n case 'End':\n next = this.maxValue();\n break;\n }\n\n if (next === null) return;\n event.preventDefault();\n this.setThumb(thumb, next);\n this.emitChange();\n this.touch.emit();\n }\n\n // Internals\n\n private updateFromEvent(event: PointerEvent, thumb: 'low' | 'high'): void {\n const rect = this.track().nativeElement.getBoundingClientRect();\n const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);\n const raw = this.minValue() + ratio * (this.maxValue() - this.minValue());\n this.setThumb(thumb, raw);\n this.emitChange();\n }\n\n private nearestThumb(event: PointerEvent): 'low' | 'high' {\n if (!this.range()) return 'low';\n const rect = this.track().nativeElement.getBoundingClientRect();\n const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);\n const raw = this.minValue() + ratio * (this.maxValue() - this.minValue());\n return Math.abs(raw - this.low()) <= Math.abs(raw - this.high()) ? 'low' : 'high';\n }\n\n private focusThumb(thumb: 'low' | 'high'): void {\n queueMicrotask(() => {\n const root = this.track().nativeElement;\n const el = root.querySelector<HTMLElement>(`.wr-slider__thumb--${thumb}`);\n el?.focus();\n });\n }\n\n private setThumb(thumb: 'low' | 'high', raw: number): void {\n const stepped = this.snap(raw);\n if (thumb === 'low') {\n const next = this.range() ? Math.min(stepped, this.high()) : stepped;\n this.low.set(next);\n } else {\n this.high.set(Math.max(stepped, this.low()));\n }\n }\n\n private snap(value: number): number {\n const stepped = Math.round((value - this.minValue()) / this.step()) * this.step() + this.minValue();\n return this.clampToBounds(round(stepped, 6));\n }\n\n private clampToBounds(v: number): number {\n return clamp(v, this.minValue(), this.maxValue());\n }\n\n private percentOf(v: number): number {\n const span = this.maxValue() - this.minValue();\n if (span <= 0) return 0;\n return ((v - this.minValue()) / span) * 100;\n }\n\n private emitChange(): void {\n const value: WrSliderValue = this.range() ? [this.low(), this.high()] : this.low();\n this.value.set(value);\n }\n}\n","<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <span class=\"wr-slider__fill\" [style.left.%]=\"fillLeft()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.left.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.left.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-slider.mjs","sources":["../../../projects/lib/slider/slider.ts","../../../projects/lib/slider/slider.html","../../../projects/lib/slider/ngwr-slider.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport type { ElementRef } from '@angular/core';\nimport {\n Component,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { clamp, round } from 'ngwr/utils';\n\nimport type { WrSliderValue } from './interfaces';\n\n/** Trim float drift from step calculations. */\n\n/**\n * Numeric slider with optional dual-thumb range mode.\n *\n * A signal-forms native control: it implements `FormValueControl<WrSliderValue>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone. The value\n * shape depends on `range`: `number` (default) or `[number, number]` when\n * `range=\"true\"`.\n *\n * Keyboard: ← / → adjust by `step`; ↑ / ↓ same; Shift+arrow by `step × 10`;\n * Home / End jump to min / max; PageUp / PageDown by `step × 10`.\n *\n * Reading direction: the arrows follow the VISUAL track, so under `dir=\"rtl\"`\n * → moves toward the lower end and ← toward the higher one, and a drag reads\n * its ratio from the right edge. ↑ / ↓, PageUp / PageDown and Home / End are\n * unchanged — they name a value, not a side of the screen.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-slider [formField]=\"form.volume\" min=\"0\" max=\"100\" step=\"5\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-slider [(value)]=\"range\" range min=\"0\" max=\"1000\" step=\"10\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/slider\n */\n@Component({\n selector: 'wr-slider',\n templateUrl: './slider.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrSlider implements FormValueControl<WrSliderValue> {\n // Typed `WrSliderValue | undefined` (not `number`) to satisfy the reserved\n // `FormUiControl` min/max slots, which are keyed to the control's value type.\n // The transform still coerces to a plain number, so the bounds stay numeric\n // at runtime; `minValue`/`maxValue` narrow them back for arithmetic.\n /** Lower bound. @default 0 */\n readonly min = input<WrSliderValue | undefined>(0, { transform: (v: unknown): number => coerceNumberProperty(v, 0) });\n /** Upper bound. @default 100 */\n readonly max = input<WrSliderValue | undefined>(100, {\n transform: (v: unknown): number => coerceNumberProperty(v, 100),\n });\n\n /** Resolved numeric lower bound for internal arithmetic. */\n private readonly minValue = computed(() => {\n const m = this.min();\n return typeof m === 'number' ? m : 0;\n });\n /** Resolved numeric upper bound for internal arithmetic. */\n private readonly maxValue = computed(() => {\n const m = this.max();\n return typeof m === 'number' ? m : 100;\n });\n\n /** Step size for keyboard and drag. @default 1 */\n readonly step = input(1, { transform: (v: unknown): number => Math.max(0.0001, coerceNumberProperty(v, 1)) });\n /** Render two thumbs and emit `[low, high]`. @default false */\n readonly range = input(false, { transform: coerceBooleanProperty });\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n /** Render the current value below the track. @default true */\n readonly showLabel = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Current value. Bound by `[formField]`, or two-way via `[(value)]`. Shape\n * follows `range`: a plain `number`, or `[low, high]` in range mode.\n */\n readonly value = model<WrSliderValue>(0);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly low = signal(0);\n protected readonly high = signal(100);\n\n protected readonly track = viewChild.required<ElementRef<HTMLElement>>('track');\n\n /**\n * Ambient reading direction. Optional so a bare `TestBed` — or any consumer\n * that never set a direction — needs no provider; `Directionality` is\n * root-provided, so `null` only ever means \"nobody asked\", which is LTR.\n *\n * Nothing caches a direction-derived value: the thumbs are painted with\n * `inset-inline-start`, so the track mirrors from CSS alone, and `isRtl()` is\n * read inside the event handlers. A runtime flip therefore needs no\n * subscription to `Directionality.change`.\n */\n private readonly dir = inject(Directionality, { optional: true });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-slider'];\n if (this.range()) parts.push('wr-slider--range');\n if (this.disabled()) parts.push('wr-slider--disabled');\n return parts.join(' ');\n });\n\n protected readonly lowPercent = computed(() => this.percentOf(this.low()));\n protected readonly highPercent = computed(() => this.percentOf(this.high()));\n\n /** Offset of the fill from the track's START edge — the left one in LTR, the right one in RTL. */\n protected readonly fillStart = computed(() => (this.range() ? this.lowPercent() : 0));\n protected readonly fillWidth = computed(() =>\n this.range() ? this.highPercent() - this.lowPercent() : this.lowPercent()\n );\n\n protected readonly label = computed(() => (this.range() ? `${this.low()} – ${this.high()}` : `${this.low()}`));\n\n constructor() {\n // Keep the internal thumbs in sync with external writes to `value`\n // (the old `writeValue`): split the tuple / clamp into the low & high cells.\n effect(() => {\n const v = this.value();\n if (Array.isArray(v)) {\n const tuple = v as readonly [number, number];\n this.low.set(this.clampToBounds(tuple[0]));\n this.high.set(this.clampToBounds(tuple[1]));\n } else if (typeof v === 'number') {\n this.low.set(this.clampToBounds(v));\n }\n });\n }\n\n // Interaction\n\n protected onPointerDown(event: PointerEvent, thumb: 'low' | 'high'): void {\n if (this.disabled()) return;\n event.preventDefault();\n const target = event.currentTarget as HTMLElement;\n target.setPointerCapture(event.pointerId);\n\n const move = (e: PointerEvent): void => this.updateFromEvent(e, thumb);\n const up = (e: PointerEvent): void => {\n target.releasePointerCapture(e.pointerId);\n target.removeEventListener('pointermove', move);\n target.removeEventListener('pointerup', up);\n this.touch.emit();\n };\n\n target.addEventListener('pointermove', move);\n target.addEventListener('pointerup', up);\n this.updateFromEvent(event, thumb);\n }\n\n protected onTrackPointerDown(event: PointerEvent): void {\n if (this.disabled()) return;\n if ((event.target as HTMLElement).closest('.wr-slider__thumb')) return;\n const thumb = this.nearestThumb(event);\n this.updateFromEvent(event, thumb);\n this.focusThumb(thumb);\n }\n\n protected onKey(event: KeyboardEvent, thumb: 'low' | 'high'): void {\n if (this.disabled()) return;\n const big = event.shiftKey || event.key === 'PageUp' || event.key === 'PageDown' ? 10 : 1;\n const delta = this.step() * big;\n const current = thumb === 'low' ? this.low() : this.high();\n // The horizontal arrows name a SIDE of the track, so they follow the\n // reading direction; every other key names a value and does not.\n const inline = this.isRtl() ? -delta : delta;\n let next: number | null = null;\n\n switch (event.key) {\n case 'ArrowRight':\n next = current + inline;\n break;\n case 'ArrowLeft':\n next = current - inline;\n break;\n case 'ArrowUp':\n case 'PageUp':\n next = current + delta;\n break;\n case 'ArrowDown':\n case 'PageDown':\n next = current - delta;\n break;\n case 'Home':\n next = this.minValue();\n break;\n case 'End':\n next = this.maxValue();\n break;\n }\n\n if (next === null) return;\n event.preventDefault();\n this.setThumb(thumb, next);\n this.emitChange();\n this.touch.emit();\n }\n\n // Internals\n\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\n }\n\n /**\n * The value under a pointer, measured along the track's INLINE axis.\n *\n * The offset is taken from whichever edge the track starts at — the left one\n * in LTR, the right one in RTL — so the whole component needs the direction\n * in exactly one place. Measuring from `rect.left` regardless would read 0 at\n * the visual maximum of a mirrored track.\n */\n private valueAt(event: PointerEvent): number {\n const rect = this.track().nativeElement.getBoundingClientRect();\n const offset = this.isRtl() ? rect.right - event.clientX : event.clientX - rect.left;\n const ratio = clamp(offset / rect.width, 0, 1);\n return this.minValue() + ratio * (this.maxValue() - this.minValue());\n }\n\n private updateFromEvent(event: PointerEvent, thumb: 'low' | 'high'): void {\n this.setThumb(thumb, this.valueAt(event));\n this.emitChange();\n }\n\n /**\n * Which thumb a track click grabs: the one nearest the pointer ON SCREEN.\n *\n * Value distance is the same thing as visual distance once {@link valueAt}\n * has mirrored the ratio — in RTL the low thumb is the RIGHT one, and a click\n * near the right edge lands close to `low` in value space too.\n */\n private nearestThumb(event: PointerEvent): 'low' | 'high' {\n if (!this.range()) return 'low';\n const raw = this.valueAt(event);\n return Math.abs(raw - this.low()) <= Math.abs(raw - this.high()) ? 'low' : 'high';\n }\n\n private focusThumb(thumb: 'low' | 'high'): void {\n queueMicrotask(() => {\n const root = this.track().nativeElement;\n const el = root.querySelector<HTMLElement>(`.wr-slider__thumb--${thumb}`);\n el?.focus();\n });\n }\n\n private setThumb(thumb: 'low' | 'high', raw: number): void {\n const stepped = this.snap(raw);\n if (thumb === 'low') {\n const next = this.range() ? Math.min(stepped, this.high()) : stepped;\n this.low.set(next);\n } else {\n this.high.set(Math.max(stepped, this.low()));\n }\n }\n\n private snap(value: number): number {\n const stepped = Math.round((value - this.minValue()) / this.step()) * this.step() + this.minValue();\n return this.clampToBounds(round(stepped, 6));\n }\n\n private clampToBounds(v: number): number {\n return clamp(v, this.minValue(), this.maxValue());\n }\n\n private percentOf(v: number): number {\n const span = this.maxValue() - this.minValue();\n if (span <= 0) return 0;\n return ((v - this.minValue()) / span) * 100;\n }\n\n private emitChange(): void {\n const value: WrSliderValue = this.range() ? [this.low(), this.high()] : this.low();\n this.value.set(value);\n }\n}\n","<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -23,10 +23,21 @@ import { input, computed, ViewEncapsulation, Component } from '@angular/core';
23
23
  class WrSparkline {
24
24
  data = input([], /* @ts-ignore */
25
25
  ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
26
+ /**
27
+ * Accessible name. A sparkline usually sits beside the number it summarises, where
28
+ * announcing it again is noise — so it is `aria-hidden` by default and becomes a
29
+ * named `role="img"` only when a consumer says what it shows.
30
+ */
31
+ ariaLabel = input(null, /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
26
33
  /** Stroke colour. @default `var(--wr-color-primary)` */
27
34
  color = input('var(--wr-color-primary)', /* @ts-ignore */
28
35
  ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
29
- /** Stroke width in viewBox units. @default 1.5 */
36
+ /**
37
+ * Line thickness in CSS pixels — `vector-effect="non-scaling-stroke"` keeps it at
38
+ * that width whatever the box is stretched to, so it is NOT in viewBox units.
39
+ * @default 1.5
40
+ */
30
41
  strokeWidth = input(1.5, { ...(ngDevMode ? { debugName: "strokeWidth" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(0.1, coerceNumberProperty(v, 1.5)) });
31
42
  /** Fill the area below the line. @default false */
32
43
  showArea = input(false, { ...(ngDevMode ? { debugName: "showArea" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
@@ -44,7 +55,10 @@ class WrSparkline {
44
55
  padding = 2;
45
56
  /** Mapped `{ x, y }` points for the path. */
46
57
  points = computed(() => {
47
- const data = this.data();
58
+ // Non-finite data is dropped rather than scaled: every `min`/`max` comparison
59
+ // against a NaN is false, so it survived into the scale and `toFixed(2)` wrote the
60
+ // literal text `NaN` into the path `d` — invalid geometry, and the line vanishes.
61
+ const data = this.data().filter(v => Number.isFinite(v));
48
62
  if (data.length === 0)
49
63
  return [];
50
64
  if (data.length === 1)
@@ -57,10 +71,16 @@ class WrSparkline {
57
71
  if (v > max)
58
72
  max = v;
59
73
  }
60
- const span = max - min || 1;
61
74
  const pad = this.padding;
62
75
  const w = this.vbW - pad * 2;
63
76
  const h = this.vbH - pad * 2;
77
+ // A series with no spread has no shape, and dividing by the old `|| 1` fallback
78
+ // pinned every point to `pad + h` — the bottom edge — so a steady series at 500
79
+ // read as rock bottom. Centred instead, which is what a lone datum already does.
80
+ if (max === min) {
81
+ return data.map((_, i) => ({ x: pad + (i / (data.length - 1)) * w, y: this.vbH / 2 }));
82
+ }
83
+ const span = max - min;
64
84
  return data.map((v, i) => ({
65
85
  x: pad + (i / (data.length - 1)) * w,
66
86
  y: pad + h - ((v - min) / span) * h,
@@ -89,12 +109,12 @@ class WrSparkline {
89
109
  ...(ngDevMode ? [{ debugName: "tipPoint" }] : /* istanbul ignore next */ []));
90
110
  viewBox = `0 0 ${this.vbW} ${this.vbH}`;
91
111
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSparkline, deps: [], target: i0.ɵɵFactoryTarget.Component });
92
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSparkline, isStandalone: true, selector: "wr-sparkline", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, showArea: { classPropertyName: "showArea", publicName: "showArea", isSignal: true, isRequired: false, transformFunction: null }, showTip: { classPropertyName: "showTip", publicName: "showTip", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-sparkline" }, ngImport: i0, template: "<svg [attr.viewBox]=\"viewBox\" [style.width]=\"width()\" [style.height]=\"height()\" preserveAspectRatio=\"none\">\n @if (showArea() && areaPath()) {\n <path class=\"wr-sparkline__area\" [attr.d]=\"areaPath()\" [attr.fill]=\"color()\" fill-opacity=\"0.15\" stroke=\"none\" />\n }\n @if (linePath()) {\n <path\n class=\"wr-sparkline__line\"\n [attr.d]=\"linePath()\"\n fill=\"none\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n vector-effect=\"non-scaling-stroke\"\n />\n }\n @if (showTip() && tipPoint(); as tip) {\n <circle class=\"wr-sparkline__tip\" [attr.cx]=\"tip.x\" [attr.cy]=\"tip.y\" r=\"1.5\" [attr.fill]=\"color()\" />\n }\n</svg>\n", encapsulation: i0.ViewEncapsulation.None });
112
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSparkline, isStandalone: true, selector: "wr-sparkline", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, showArea: { classPropertyName: "showArea", publicName: "showArea", isSignal: true, isRequired: false, transformFunction: null }, showTip: { classPropertyName: "showTip", publicName: "showTip", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-sparkline" }, ngImport: i0, template: "<svg\n [attr.viewBox]=\"viewBox\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [attr.role]=\"ariaLabel() ? 'img' : null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-hidden]=\"ariaLabel() ? null : 'true'\"\n preserveAspectRatio=\"none\"\n>\n @if (showArea() && areaPath()) {\n <path class=\"wr-sparkline__area\" [attr.d]=\"areaPath()\" [attr.fill]=\"color()\" fill-opacity=\"0.15\" stroke=\"none\" />\n }\n @if (linePath()) {\n <path\n class=\"wr-sparkline__line\"\n [attr.d]=\"linePath()\"\n fill=\"none\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n vector-effect=\"non-scaling-stroke\"\n />\n }\n @if (showTip() && tipPoint(); as tip) {\n <circle class=\"wr-sparkline__tip\" [attr.cx]=\"tip.x\" [attr.cy]=\"tip.y\" r=\"1.5\" [attr.fill]=\"color()\" />\n }\n</svg>\n", encapsulation: i0.ViewEncapsulation.None });
93
113
  }
94
114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSparkline, decorators: [{
95
115
  type: Component,
96
- args: [{ selector: 'wr-sparkline', encapsulation: ViewEncapsulation.None, host: { class: 'wr-sparkline' }, template: "<svg [attr.viewBox]=\"viewBox\" [style.width]=\"width()\" [style.height]=\"height()\" preserveAspectRatio=\"none\">\n @if (showArea() && areaPath()) {\n <path class=\"wr-sparkline__area\" [attr.d]=\"areaPath()\" [attr.fill]=\"color()\" fill-opacity=\"0.15\" stroke=\"none\" />\n }\n @if (linePath()) {\n <path\n class=\"wr-sparkline__line\"\n [attr.d]=\"linePath()\"\n fill=\"none\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n vector-effect=\"non-scaling-stroke\"\n />\n }\n @if (showTip() && tipPoint(); as tip) {\n <circle class=\"wr-sparkline__tip\" [attr.cx]=\"tip.x\" [attr.cy]=\"tip.y\" r=\"1.5\" [attr.fill]=\"color()\" />\n }\n</svg>\n" }]
97
- }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], showArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArea", required: false }] }], showTip: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTip", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
116
+ args: [{ selector: 'wr-sparkline', encapsulation: ViewEncapsulation.None, host: { class: 'wr-sparkline' }, template: "<svg\n [attr.viewBox]=\"viewBox\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [attr.role]=\"ariaLabel() ? 'img' : null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-hidden]=\"ariaLabel() ? null : 'true'\"\n preserveAspectRatio=\"none\"\n>\n @if (showArea() && areaPath()) {\n <path class=\"wr-sparkline__area\" [attr.d]=\"areaPath()\" [attr.fill]=\"color()\" fill-opacity=\"0.15\" stroke=\"none\" />\n }\n @if (linePath()) {\n <path\n class=\"wr-sparkline__line\"\n [attr.d]=\"linePath()\"\n fill=\"none\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n vector-effect=\"non-scaling-stroke\"\n />\n }\n @if (showTip() && tipPoint(); as tip) {\n <circle class=\"wr-sparkline__tip\" [attr.cx]=\"tip.x\" [attr.cy]=\"tip.y\" r=\"1.5\" [attr.fill]=\"color()\" />\n }\n</svg>\n" }]
117
+ }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], showArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArea", required: false }] }], showTip: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTip", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
98
118
 
99
119
  /**
100
120
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-sparkline.mjs","sources":["../../../projects/lib/sparkline/sparkline.ts","../../../projects/lib/sparkline/sparkline.html","../../../projects/lib/sparkline/ngwr-sparkline.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\n/**\n * Tiny inline trend line. SVG path drawn from `[data]: number[]` — no\n * axes, no labels, just the shape. Optional area fill + tip dot.\n *\n * @example\n * ```html\n * <wr-sparkline [data]=\"[12, 14, 9, 17, 21, 18, 23]\" />\n * <wr-sparkline [data]=\"prices\" [showArea]=\"true\" color=\"var(--wr-color-success)\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/sparkline\n */\n@Component({\n selector: 'wr-sparkline',\n templateUrl: './sparkline.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-sparkline' },\n})\nexport class WrSparkline {\n readonly data = input<readonly number[]>([]);\n\n /** Stroke colour. @default `var(--wr-color-primary)` */\n readonly color = input<string>('var(--wr-color-primary)');\n\n /** Stroke width in viewBox units. @default 1.5 */\n readonly strokeWidth = input(1.5, {\n transform: (v: unknown): number => Math.max(0.1, coerceNumberProperty(v, 1.5)),\n });\n\n /** Fill the area below the line. @default false */\n readonly showArea = input(false, { transform: coerceBooleanProperty });\n\n /** Show a dot at the last data point. @default true */\n readonly showTip = input(true, { transform: coerceBooleanProperty });\n\n /** CSS width. @default '8rem' */\n readonly width = input<string>('8rem');\n\n /** CSS height. @default '2rem' */\n readonly height = input<string>('2rem');\n\n // Drawn in a 100×40 viewBox — scales smoothly to any CSS size.\n private readonly vbW = 100;\n private readonly vbH = 40;\n private readonly padding = 2;\n\n /** Mapped `{ x, y }` points for the path. */\n protected readonly points = computed(() => {\n const data = this.data();\n if (data.length === 0) return [] as readonly { x: number; y: number }[];\n if (data.length === 1) return [{ x: this.vbW / 2, y: this.vbH / 2 }];\n\n let min = data[0];\n let max = data[0];\n for (const v of data) {\n if (v < min) min = v;\n if (v > max) max = v;\n }\n const span = max - min || 1;\n const pad = this.padding;\n const w = this.vbW - pad * 2;\n const h = this.vbH - pad * 2;\n return data.map((v, i) => ({\n x: pad + (i / (data.length - 1)) * w,\n y: pad + h - ((v - min) / span) * h,\n }));\n });\n\n protected readonly linePath = computed(() =>\n this.points()\n .map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x.toFixed(2)} ${p.y.toFixed(2)}`)\n .join(' ')\n );\n\n protected readonly areaPath = computed(() => {\n const pts = this.points();\n if (pts.length < 2) return '';\n const baseY = this.vbH - this.padding;\n const start = `M ${pts[0].x.toFixed(2)} ${baseY}`;\n const line = pts.map(p => `L ${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(' ');\n const close = `L ${pts[pts.length - 1].x.toFixed(2)} ${baseY} Z`;\n return `${start} ${line} ${close}`;\n });\n\n protected readonly tipPoint = computed(() => {\n const pts = this.points();\n return pts.length > 0 ? pts[pts.length - 1] : null;\n });\n\n protected readonly viewBox = `0 0 ${this.vbW} ${this.vbH}`;\n}\n","<svg [attr.viewBox]=\"viewBox\" [style.width]=\"width()\" [style.height]=\"height()\" preserveAspectRatio=\"none\">\n @if (showArea() && areaPath()) {\n <path class=\"wr-sparkline__area\" [attr.d]=\"areaPath()\" [attr.fill]=\"color()\" fill-opacity=\"0.15\" stroke=\"none\" />\n }\n @if (linePath()) {\n <path\n class=\"wr-sparkline__line\"\n [attr.d]=\"linePath()\"\n fill=\"none\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n vector-effect=\"non-scaling-stroke\"\n />\n }\n @if (showTip() && tipPoint(); as tip) {\n <circle class=\"wr-sparkline__tip\" [attr.cx]=\"tip.x\" [attr.cy]=\"tip.y\" r=\"1.5\" [attr.fill]=\"color()\" />\n }\n</svg>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-sparkline.mjs","sources":["../../../projects/lib/sparkline/sparkline.ts","../../../projects/lib/sparkline/sparkline.html","../../../projects/lib/sparkline/ngwr-sparkline.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\n/**\n * Tiny inline trend line. SVG path drawn from `[data]: number[]` — no\n * axes, no labels, just the shape. Optional area fill + tip dot.\n *\n * @example\n * ```html\n * <wr-sparkline [data]=\"[12, 14, 9, 17, 21, 18, 23]\" />\n * <wr-sparkline [data]=\"prices\" [showArea]=\"true\" color=\"var(--wr-color-success)\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/sparkline\n */\n@Component({\n selector: 'wr-sparkline',\n templateUrl: './sparkline.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-sparkline' },\n})\nexport class WrSparkline {\n readonly data = input<readonly number[]>([]);\n\n /**\n * Accessible name. A sparkline usually sits beside the number it summarises, where\n * announcing it again is noise — so it is `aria-hidden` by default and becomes a\n * named `role=\"img\"` only when a consumer says what it shows.\n */\n readonly ariaLabel = input<string | null>(null);\n\n /** Stroke colour. @default `var(--wr-color-primary)` */\n readonly color = input<string>('var(--wr-color-primary)');\n\n /**\n * Line thickness in CSS pixels — `vector-effect=\"non-scaling-stroke\"` keeps it at\n * that width whatever the box is stretched to, so it is NOT in viewBox units.\n * @default 1.5\n */\n readonly strokeWidth = input(1.5, {\n transform: (v: unknown): number => Math.max(0.1, coerceNumberProperty(v, 1.5)),\n });\n\n /** Fill the area below the line. @default false */\n readonly showArea = input(false, { transform: coerceBooleanProperty });\n\n /** Show a dot at the last data point. @default true */\n readonly showTip = input(true, { transform: coerceBooleanProperty });\n\n /** CSS width. @default '8rem' */\n readonly width = input<string>('8rem');\n\n /** CSS height. @default '2rem' */\n readonly height = input<string>('2rem');\n\n // Drawn in a 100×40 viewBox — scales smoothly to any CSS size.\n private readonly vbW = 100;\n private readonly vbH = 40;\n private readonly padding = 2;\n\n /** Mapped `{ x, y }` points for the path. */\n protected readonly points = computed(() => {\n // Non-finite data is dropped rather than scaled: every `min`/`max` comparison\n // against a NaN is false, so it survived into the scale and `toFixed(2)` wrote the\n // literal text `NaN` into the path `d` — invalid geometry, and the line vanishes.\n const data = this.data().filter(v => Number.isFinite(v));\n if (data.length === 0) return [] as readonly { x: number; y: number }[];\n if (data.length === 1) return [{ x: this.vbW / 2, y: this.vbH / 2 }];\n\n let min = data[0];\n let max = data[0];\n for (const v of data) {\n if (v < min) min = v;\n if (v > max) max = v;\n }\n const pad = this.padding;\n const w = this.vbW - pad * 2;\n const h = this.vbH - pad * 2;\n // A series with no spread has no shape, and dividing by the old `|| 1` fallback\n // pinned every point to `pad + h` — the bottom edge — so a steady series at 500\n // read as rock bottom. Centred instead, which is what a lone datum already does.\n if (max === min) {\n return data.map((_, i) => ({ x: pad + (i / (data.length - 1)) * w, y: this.vbH / 2 }));\n }\n const span = max - min;\n return data.map((v, i) => ({\n x: pad + (i / (data.length - 1)) * w,\n y: pad + h - ((v - min) / span) * h,\n }));\n });\n\n protected readonly linePath = computed(() =>\n this.points()\n .map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x.toFixed(2)} ${p.y.toFixed(2)}`)\n .join(' ')\n );\n\n protected readonly areaPath = computed(() => {\n const pts = this.points();\n if (pts.length < 2) return '';\n const baseY = this.vbH - this.padding;\n const start = `M ${pts[0].x.toFixed(2)} ${baseY}`;\n const line = pts.map(p => `L ${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(' ');\n const close = `L ${pts[pts.length - 1].x.toFixed(2)} ${baseY} Z`;\n return `${start} ${line} ${close}`;\n });\n\n protected readonly tipPoint = computed(() => {\n const pts = this.points();\n return pts.length > 0 ? pts[pts.length - 1] : null;\n });\n\n protected readonly viewBox = `0 0 ${this.vbW} ${this.vbH}`;\n}\n","<svg\n [attr.viewBox]=\"viewBox\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [attr.role]=\"ariaLabel() ? 'img' : null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-hidden]=\"ariaLabel() ? null : 'true'\"\n preserveAspectRatio=\"none\"\n>\n @if (showArea() && areaPath()) {\n <path class=\"wr-sparkline__area\" [attr.d]=\"areaPath()\" [attr.fill]=\"color()\" fill-opacity=\"0.15\" stroke=\"none\" />\n }\n @if (linePath()) {\n <path\n class=\"wr-sparkline__line\"\n [attr.d]=\"linePath()\"\n fill=\"none\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n vector-effect=\"non-scaling-stroke\"\n />\n }\n @if (showTip() && tipPoint(); as tip) {\n <circle class=\"wr-sparkline__tip\" [attr.cx]=\"tip.x\" [attr.cy]=\"tip.y\" r=\"1.5\" [attr.fill]=\"color()\" />\n }\n</svg>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
2
  import * as i0 from '@angular/core';
3
- import { input, model, output, computed, ViewEncapsulation, Component } from '@angular/core';
3
+ import { input, model, output, viewChild, effect, computed, ViewEncapsulation, Component } from '@angular/core';
4
4
  import { useI18nText } from 'ngwr/i18n';
5
5
  import { WrIcon } from 'ngwr/icon';
6
6
 
@@ -28,6 +28,7 @@ import { WrIcon } from 'ngwr/icon';
28
28
  *
29
29
  * @see https://ngwr.dev/reference/components/speed-dial
30
30
  */
31
+ let menuUid = 0;
31
32
  class WrSpeedDial {
32
33
  actions = input([], /* @ts-ignore */
33
34
  ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
@@ -57,6 +58,18 @@ class WrSpeedDial {
57
58
  safeArea = input(false, { ...(ngDevMode ? { debugName: "safeArea" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
58
59
  /** Fires when the user picks one of the actions. */
59
60
  pick = output();
61
+ /** Id the trigger's `aria-controls` points at. */
62
+ menuId = `wr-speed-dial-menu-${++menuUid}`;
63
+ triggerEl = viewChild('trigger', /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []));
65
+ constructor() {
66
+ // A disabled trigger cannot be pressed, and the trigger is the only way to close
67
+ // the dial — so being disabled while open left it fanned out with no way back.
68
+ effect(() => {
69
+ if (this.disabled() && this.open())
70
+ this.open.set(false);
71
+ });
72
+ }
60
73
  classes = computed(() => {
61
74
  const parts = ['wr-speed-dial', `wr-speed-dial--${this.direction()}`];
62
75
  if (this.open())
@@ -76,16 +89,36 @@ class WrSpeedDial {
76
89
  onPick(action) {
77
90
  if (this.disabled())
78
91
  return;
92
+ this.open.set(false);
79
93
  this.pick.emit(action);
94
+ }
95
+ /**
96
+ * `role="menu"` promises a way out, and the actions are ordinary buttons in the tab
97
+ * order — without this a keyboard user who opened the dial had to tab through every
98
+ * action to leave it.
99
+ */
100
+ onKeydown(event) {
101
+ if (event.key !== 'Escape' || !this.open())
102
+ return;
103
+ event.preventDefault();
80
104
  this.open.set(false);
105
+ this.triggerEl()?.nativeElement.focus();
106
+ }
107
+ /**
108
+ * First GLYPH of the label, for an action with no icon. `charAt(0)` returns one
109
+ * UTF-16 code unit, which cuts an astral character (an emoji) in half and renders a
110
+ * replacement character.
111
+ */
112
+ initial(label) {
113
+ return [...label][0] ?? '';
81
114
  }
82
115
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSpeedDial, deps: [], target: i0.ɵɵFactoryTarget.Component });
83
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSpeedDial, isStandalone: true, selector: "wr-speed-dial", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, triggerLabel: { classPropertyName: "triggerLabel", publicName: "triggerLabel", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, safeArea: { classPropertyName: "safeArea", publicName: "safeArea", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", pick: "pick" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"wr-speed-dial__trigger\"\n [attr.aria-expanded]=\"open()\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"resolvedTriggerLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onTrigger()\"\n>\n <wr-icon [name]=\"icon()\" />\n</button>\n\n<ul class=\"wr-speed-dial__actions\" role=\"menu\">\n @for (action of actions(); track action.id) {\n <!--\n role=\"none\" is load-bearing: role=\"menu\" on the <ul> strips the implicit\n list semantics from its children, so an unroled <li> sits between the menu\n and its menuitem and breaks the required menu -> menuitem parentage.\n Marking it presentational restores the direct relationship.\n -->\n <li class=\"wr-speed-dial__action-wrap\" role=\"none\" [style.--wr-speed-dial-i]=\"$index + 1\">\n <button\n type=\"button\"\n class=\"wr-speed-dial__action\"\n role=\"menuitem\"\n [attr.aria-label]=\"action.label\"\n [title]=\"action.label\"\n (click)=\"onPick(action)\"\n >\n @if (action.icon) {\n <wr-icon [name]=\"action.icon\" />\n } @else {\n <span>{{ action.label.charAt(0) }}</span>\n }\n </button>\n </li>\n }\n</ul>\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }], encapsulation: i0.ViewEncapsulation.None });
116
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSpeedDial, isStandalone: true, selector: "wr-speed-dial", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, triggerLabel: { classPropertyName: "triggerLabel", publicName: "triggerLabel", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, safeArea: { classPropertyName: "safeArea", publicName: "safeArea", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", pick: "pick" }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "triggerEl", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n #trigger\n type=\"button\"\n class=\"wr-speed-dial__trigger\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"menuId\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"resolvedTriggerLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onTrigger()\"\n>\n <wr-icon [name]=\"icon()\" />\n</button>\n\n<ul class=\"wr-speed-dial__actions\" role=\"menu\" [attr.id]=\"menuId\">\n @for (action of actions(); track action.id) {\n <!--\n role=\"none\" is load-bearing: role=\"menu\" on the <ul> strips the implicit\n list semantics from its children, so an unroled <li> sits between the menu\n and its menuitem and breaks the required menu -> menuitem parentage.\n Marking it presentational restores the direct relationship.\n -->\n <li class=\"wr-speed-dial__action-wrap\" role=\"none\" [style.--wr-speed-dial-i]=\"$index + 1\">\n <button\n type=\"button\"\n class=\"wr-speed-dial__action\"\n role=\"menuitem\"\n [attr.aria-label]=\"action.label\"\n [title]=\"action.label\"\n [disabled]=\"disabled()\"\n (click)=\"onPick(action)\"\n >\n @if (action.icon) {\n <wr-icon [name]=\"action.icon\" />\n } @else {\n <span>{{ initial(action.label) }}</span>\n }\n </button>\n </li>\n }\n</ul>\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }], encapsulation: i0.ViewEncapsulation.None });
84
117
  }
85
118
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSpeedDial, decorators: [{
86
119
  type: Component,
87
- args: [{ selector: 'wr-speed-dial', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrIcon], template: "<button\n type=\"button\"\n class=\"wr-speed-dial__trigger\"\n [attr.aria-expanded]=\"open()\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"resolvedTriggerLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onTrigger()\"\n>\n <wr-icon [name]=\"icon()\" />\n</button>\n\n<ul class=\"wr-speed-dial__actions\" role=\"menu\">\n @for (action of actions(); track action.id) {\n <!--\n role=\"none\" is load-bearing: role=\"menu\" on the <ul> strips the implicit\n list semantics from its children, so an unroled <li> sits between the menu\n and its menuitem and breaks the required menu -> menuitem parentage.\n Marking it presentational restores the direct relationship.\n -->\n <li class=\"wr-speed-dial__action-wrap\" role=\"none\" [style.--wr-speed-dial-i]=\"$index + 1\">\n <button\n type=\"button\"\n class=\"wr-speed-dial__action\"\n role=\"menuitem\"\n [attr.aria-label]=\"action.label\"\n [title]=\"action.label\"\n (click)=\"onPick(action)\"\n >\n @if (action.icon) {\n <wr-icon [name]=\"action.icon\" />\n } @else {\n <span>{{ action.label.charAt(0) }}</span>\n }\n </button>\n </li>\n }\n</ul>\n" }]
88
- }], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], triggerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabel", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], safeArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "safeArea", required: false }] }], pick: [{ type: i0.Output, args: ["pick"] }] } });
120
+ args: [{ selector: 'wr-speed-dial', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', '(keydown)': 'onKeydown($event)' }, imports: [WrIcon], template: "<button\n #trigger\n type=\"button\"\n class=\"wr-speed-dial__trigger\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"menuId\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"resolvedTriggerLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onTrigger()\"\n>\n <wr-icon [name]=\"icon()\" />\n</button>\n\n<ul class=\"wr-speed-dial__actions\" role=\"menu\" [attr.id]=\"menuId\">\n @for (action of actions(); track action.id) {\n <!--\n role=\"none\" is load-bearing: role=\"menu\" on the <ul> strips the implicit\n list semantics from its children, so an unroled <li> sits between the menu\n and its menuitem and breaks the required menu -> menuitem parentage.\n Marking it presentational restores the direct relationship.\n -->\n <li class=\"wr-speed-dial__action-wrap\" role=\"none\" [style.--wr-speed-dial-i]=\"$index + 1\">\n <button\n type=\"button\"\n class=\"wr-speed-dial__action\"\n role=\"menuitem\"\n [attr.aria-label]=\"action.label\"\n [title]=\"action.label\"\n [disabled]=\"disabled()\"\n (click)=\"onPick(action)\"\n >\n @if (action.icon) {\n <wr-icon [name]=\"action.icon\" />\n } @else {\n <span>{{ initial(action.label) }}</span>\n }\n </button>\n </li>\n }\n</ul>\n" }]
121
+ }], ctorParameters: () => [], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], triggerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabel", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], safeArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "safeArea", required: false }] }], pick: [{ type: i0.Output, args: ["pick"] }], triggerEl: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
89
122
 
90
123
  /**
91
124
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-speed-dial.mjs","sources":["../../../projects/lib/speed-dial/speed-dial.ts","../../../projects/lib/speed-dial/speed-dial.html","../../../projects/lib/speed-dial/ngwr-speed-dial.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input, model, output } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\n\nimport type { WrSpeedDialAction, WrSpeedDialDirection } from './interfaces';\n\n/**\n * Floating action button that expands into a fan of secondary actions on\n * click / hover. Place inside a positioned container or near `position: fixed`\n * — the component itself doesn't pin to the viewport.\n *\n * @example\n * ```html\n * <wr-speed-dial\n * [actions]=\"[\n * { id: 'new', label: 'New', icon: 'add' },\n * { id: 'edit', label: 'Edit', icon: 'cog' }\n * ]\"\n * (pick)=\"onPick($event)\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/speed-dial\n */\n@Component({\n selector: 'wr-speed-dial',\n templateUrl: './speed-dial.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrIcon],\n})\nexport class WrSpeedDial {\n readonly actions = input<readonly WrSpeedDialAction[]>([]);\n\n /**\n * Accessible name of the trigger. Falls back to `speedDial.label`, then\n * `'Actions'` — the trigger is icon-only, so without this it has no name at all.\n */\n readonly triggerLabel = input<string | null>(null);\n\n protected readonly resolvedTriggerLabel = useI18nText(this.triggerLabel, 'speedDial.label', 'Actions');\n\n /** Direction the actions fan out. @default 'up' */\n readonly direction = input<WrSpeedDialDirection>('up');\n\n /** Two-way bindable open state. @default false */\n readonly open = model(false);\n\n /** Icon for the main trigger button. @default 'add' */\n readonly icon = input<WrIconName>('add');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Pad the host toward the dial `direction` with `env(safe-area-inset-*)`.\n * Only matters when the consumer pins the dial to that edge of the\n * viewport; otherwise it has no effect. @default false\n */\n readonly safeArea = input(false, { transform: coerceBooleanProperty });\n\n /** Fires when the user picks one of the actions. */\n readonly pick = output<WrSpeedDialAction>();\n\n protected readonly classes = computed(() => {\n const parts = ['wr-speed-dial', `wr-speed-dial--${this.direction()}`];\n if (this.open()) parts.push('wr-speed-dial--open');\n if (this.disabled()) parts.push('wr-speed-dial--disabled');\n if (this.safeArea()) parts.push('wr-speed-dial--safe-area');\n return parts.join(' ');\n });\n\n protected onTrigger(): void {\n if (this.disabled()) return;\n this.open.update(v => !v);\n }\n\n protected onPick(action: WrSpeedDialAction): void {\n if (this.disabled()) return;\n this.pick.emit(action);\n this.open.set(false);\n }\n}\n\nexport type { WrSpeedDialAction, WrSpeedDialDirection } from './interfaces';\n","<button\n type=\"button\"\n class=\"wr-speed-dial__trigger\"\n [attr.aria-expanded]=\"open()\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"resolvedTriggerLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onTrigger()\"\n>\n <wr-icon [name]=\"icon()\" />\n</button>\n\n<ul class=\"wr-speed-dial__actions\" role=\"menu\">\n @for (action of actions(); track action.id) {\n <!--\n role=\"none\" is load-bearing: role=\"menu\" on the <ul> strips the implicit\n list semantics from its children, so an unroled <li> sits between the menu\n and its menuitem and breaks the required menu -> menuitem parentage.\n Marking it presentational restores the direct relationship.\n -->\n <li class=\"wr-speed-dial__action-wrap\" role=\"none\" [style.--wr-speed-dial-i]=\"$index + 1\">\n <button\n type=\"button\"\n class=\"wr-speed-dial__action\"\n role=\"menuitem\"\n [attr.aria-label]=\"action.label\"\n [title]=\"action.label\"\n (click)=\"onPick(action)\"\n >\n @if (action.icon) {\n <wr-icon [name]=\"action.icon\" />\n } @else {\n <span>{{ action.label.charAt(0) }}</span>\n }\n </button>\n </li>\n }\n</ul>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-speed-dial.mjs","sources":["../../../projects/lib/speed-dial/speed-dial.ts","../../../projects/lib/speed-dial/speed-dial.html","../../../projects/lib/speed-dial/ngwr-speed-dial.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n input,\n model,\n output,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\n\nimport type { WrSpeedDialAction, WrSpeedDialDirection } from './interfaces';\n\n/**\n * Floating action button that expands into a fan of secondary actions on\n * click / hover. Place inside a positioned container or near `position: fixed`\n * — the component itself doesn't pin to the viewport.\n *\n * @example\n * ```html\n * <wr-speed-dial\n * [actions]=\"[\n * { id: 'new', label: 'New', icon: 'add' },\n * { id: 'edit', label: 'Edit', icon: 'cog' }\n * ]\"\n * (pick)=\"onPick($event)\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/speed-dial\n */\nlet menuUid = 0;\n\n@Component({\n selector: 'wr-speed-dial',\n templateUrl: './speed-dial.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()', '(keydown)': 'onKeydown($event)' },\n imports: [WrIcon],\n})\nexport class WrSpeedDial {\n readonly actions = input<readonly WrSpeedDialAction[]>([]);\n\n /**\n * Accessible name of the trigger. Falls back to `speedDial.label`, then\n * `'Actions'` — the trigger is icon-only, so without this it has no name at all.\n */\n readonly triggerLabel = input<string | null>(null);\n\n protected readonly resolvedTriggerLabel = useI18nText(this.triggerLabel, 'speedDial.label', 'Actions');\n\n /** Direction the actions fan out. @default 'up' */\n readonly direction = input<WrSpeedDialDirection>('up');\n\n /** Two-way bindable open state. @default false */\n readonly open = model(false);\n\n /** Icon for the main trigger button. @default 'add' */\n readonly icon = input<WrIconName>('add');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Pad the host toward the dial `direction` with `env(safe-area-inset-*)`.\n * Only matters when the consumer pins the dial to that edge of the\n * viewport; otherwise it has no effect. @default false\n */\n readonly safeArea = input(false, { transform: coerceBooleanProperty });\n\n /** Fires when the user picks one of the actions. */\n readonly pick = output<WrSpeedDialAction>();\n\n /** Id the trigger's `aria-controls` points at. */\n protected readonly menuId = `wr-speed-dial-menu-${++menuUid}`;\n\n protected readonly triggerEl = viewChild<ElementRef<HTMLButtonElement>>('trigger');\n\n constructor() {\n // A disabled trigger cannot be pressed, and the trigger is the only way to close\n // the dial — so being disabled while open left it fanned out with no way back.\n effect(() => {\n if (this.disabled() && this.open()) this.open.set(false);\n });\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-speed-dial', `wr-speed-dial--${this.direction()}`];\n if (this.open()) parts.push('wr-speed-dial--open');\n if (this.disabled()) parts.push('wr-speed-dial--disabled');\n if (this.safeArea()) parts.push('wr-speed-dial--safe-area');\n return parts.join(' ');\n });\n\n protected onTrigger(): void {\n if (this.disabled()) return;\n this.open.update(v => !v);\n }\n\n protected onPick(action: WrSpeedDialAction): void {\n if (this.disabled()) return;\n this.open.set(false);\n this.pick.emit(action);\n }\n\n /**\n * `role=\"menu\"` promises a way out, and the actions are ordinary buttons in the tab\n * order — without this a keyboard user who opened the dial had to tab through every\n * action to leave it.\n */\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape' || !this.open()) return;\n event.preventDefault();\n this.open.set(false);\n this.triggerEl()?.nativeElement.focus();\n }\n\n /**\n * First GLYPH of the label, for an action with no icon. `charAt(0)` returns one\n * UTF-16 code unit, which cuts an astral character (an emoji) in half and renders a\n * replacement character.\n */\n protected initial(label: string): string {\n return [...label][0] ?? '';\n }\n}\n\nexport type { WrSpeedDialAction, WrSpeedDialDirection } from './interfaces';\n","<button\n #trigger\n type=\"button\"\n class=\"wr-speed-dial__trigger\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"menuId\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"resolvedTriggerLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onTrigger()\"\n>\n <wr-icon [name]=\"icon()\" />\n</button>\n\n<ul class=\"wr-speed-dial__actions\" role=\"menu\" [attr.id]=\"menuId\">\n @for (action of actions(); track action.id) {\n <!--\n role=\"none\" is load-bearing: role=\"menu\" on the <ul> strips the implicit\n list semantics from its children, so an unroled <li> sits between the menu\n and its menuitem and breaks the required menu -> menuitem parentage.\n Marking it presentational restores the direct relationship.\n -->\n <li class=\"wr-speed-dial__action-wrap\" role=\"none\" [style.--wr-speed-dial-i]=\"$index + 1\">\n <button\n type=\"button\"\n class=\"wr-speed-dial__action\"\n role=\"menuitem\"\n [attr.aria-label]=\"action.label\"\n [title]=\"action.label\"\n [disabled]=\"disabled()\"\n (click)=\"onPick(action)\"\n >\n @if (action.icon) {\n <wr-icon [name]=\"action.icon\" />\n } @else {\n <span>{{ initial(action.label) }}</span>\n }\n </button>\n </li>\n }\n</ul>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, computed, ViewEncapsulation, Component } from '@angular/core';
3
+ import { useI18nText } from 'ngwr/i18n';
3
4
 
4
5
  /**
5
6
  * @license
@@ -28,22 +29,30 @@ class WrSpinner {
28
29
  */
29
30
  size = input('md', /* @ts-ignore */
30
31
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
32
+ /**
33
+ * Accessible name for the live region. Falls back to `spinner.label`, then
34
+ * `'Loading'` — which the host used to carry as a hard-coded attribute, while
35
+ * the catalog had the translated key for it all along.
36
+ */
37
+ ariaLabel = input(null, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
39
+ resolvedAriaLabel = useI18nText(this.ariaLabel, 'spinner.label', 'Loading');
31
40
  classes = computed(() => {
32
41
  const size = this.size();
33
42
  return size === 'md' ? 'wr-spinner' : `wr-spinner wr-spinner--${size}`;
34
43
  }, /* @ts-ignore */
35
44
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
36
45
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSpinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
37
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrSpinner, isStandalone: true, selector: "wr-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "status", "aria-label": "Loading" }, properties: { "class": "classes()" } }, ngImport: i0, template: "<svg viewBox=\"0 0 50 50\" aria-hidden=\"true\">\n <circle class=\"wr-spinner__path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"5\" />\n</svg>\n", encapsulation: i0.ViewEncapsulation.None });
46
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrSpinner, isStandalone: true, selector: "wr-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "status" }, properties: { "attr.aria-label": "resolvedAriaLabel()", "class": "classes()" } }, ngImport: i0, template: "<svg viewBox=\"0 0 50 50\" aria-hidden=\"true\">\n <circle class=\"wr-spinner__path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"5\" />\n</svg>\n", encapsulation: i0.ViewEncapsulation.None });
38
47
  }
39
48
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSpinner, decorators: [{
40
49
  type: Component,
41
50
  args: [{ selector: 'wr-spinner', encapsulation: ViewEncapsulation.None, host: {
42
51
  role: 'status',
43
- 'aria-label': 'Loading',
52
+ '[attr.aria-label]': 'resolvedAriaLabel()',
44
53
  '[class]': 'classes()',
45
54
  }, template: "<svg viewBox=\"0 0 50 50\" aria-hidden=\"true\">\n <circle class=\"wr-spinner__path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"5\" />\n</svg>\n" }]
46
- }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
55
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
47
56
 
48
57
  /**
49
58
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-spinner.mjs","sources":["../../../projects/lib/spinner/spinner.ts","../../../projects/lib/spinner/spinner.html","../../../projects/lib/spinner/ngwr-spinner.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport type { WrSpinnerSize } from './interfaces';\n\n/**\n * Inline loading indicator.\n *\n * Inherits color from the surrounding text (uses `currentColor`).\n *\n * @example\n * ```html\n * <wr-spinner />\n * <wr-spinner size=\"lg\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/spinner\n */\n@Component({\n selector: 'wr-spinner',\n templateUrl: './spinner.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n role: 'status',\n 'aria-label': 'Loading',\n '[class]': 'classes()',\n },\n})\nexport class WrSpinner {\n /**\n * Size variant. Em-based — scales with surrounding font-size.\n *\n * @default 'md'\n */\n readonly size = input<WrSpinnerSize>('md');\n\n protected readonly classes = computed(() => {\n const size = this.size();\n return size === 'md' ? 'wr-spinner' : `wr-spinner wr-spinner--${size}`;\n });\n}\n","<svg viewBox=\"0 0 50 50\" aria-hidden=\"true\">\n <circle class=\"wr-spinner__path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"5\" />\n</svg>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAAA;;;;;AAKG;AAMH;;;;;;;;;;;;AAYG;MAWU,SAAS,CAAA;AACpB;;;;AAIG;IACM,IAAI,GAAG,KAAK,CAAgB,IAAI;6EAAC;AAEvB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,OAAO,IAAI,KAAK,IAAI,GAAG,YAAY,GAAG,CAAA,uBAAA,EAA0B,IAAI,EAAE;IACxE,CAAC;gFAAC;uGAXS,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAT,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,SAAS,kTClCtB,mKAGA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FD+Ba,SAAS,EAAA,UAAA,EAAA,CAAA;kBAVrB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,YAAY,EAAA,aAAA,EAEP,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B;AACJ,wBAAA,IAAI,EAAE,QAAQ;AACd,wBAAA,YAAY,EAAE,SAAS;AACvB,wBAAA,SAAS,EAAE,WAAW;AACvB,qBAAA,EAAA,QAAA,EAAA,mKAAA,EAAA;;;AEhCH;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-spinner.mjs","sources":["../../../projects/lib/spinner/spinner.ts","../../../projects/lib/spinner/spinner.html","../../../projects/lib/spinner/ngwr-spinner.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\n\nimport type { WrSpinnerSize } from './interfaces';\n\n/**\n * Inline loading indicator.\n *\n * Inherits color from the surrounding text (uses `currentColor`).\n *\n * @example\n * ```html\n * <wr-spinner />\n * <wr-spinner size=\"lg\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/spinner\n */\n@Component({\n selector: 'wr-spinner',\n templateUrl: './spinner.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n role: 'status',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n '[class]': 'classes()',\n },\n})\nexport class WrSpinner {\n /**\n * Size variant. Em-based — scales with surrounding font-size.\n *\n * @default 'md'\n */\n readonly size = input<WrSpinnerSize>('md');\n\n /**\n * Accessible name for the live region. Falls back to `spinner.label`, then\n * `'Loading'` — which the host used to carry as a hard-coded attribute, while\n * the catalog had the translated key for it all along.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'spinner.label', 'Loading');\n\n protected readonly classes = computed(() => {\n const size = this.size();\n return size === 'md' ? 'wr-spinner' : `wr-spinner wr-spinner--${size}`;\n });\n}\n","<svg viewBox=\"0 0 50 50\" aria-hidden=\"true\">\n <circle class=\"wr-spinner__path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"5\" />\n</svg>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAAA;;;;;AAKG;AAQH;;;;;;;;;;;;AAYG;MAWU,SAAS,CAAA;AACpB;;;;AAIG;IACM,IAAI,GAAG,KAAK,CAAgB,IAAI;6EAAC;AAE1C;;;;AAIG;IACM,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;IAE5B,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC;AAE3E,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,OAAO,IAAI,KAAK,IAAI,GAAG,YAAY,GAAG,CAAA,uBAAA,EAA0B,IAAI,EAAE;IACxE,CAAC;gFAAC;uGApBS,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAT,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,SAAS,ucCpCtB,mKAGA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDiCa,SAAS,EAAA,UAAA,EAAA,CAAA;kBAVrB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,YAAY,EAAA,aAAA,EAEP,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B;AACJ,wBAAA,IAAI,EAAE,QAAQ;AACd,wBAAA,mBAAmB,EAAE,qBAAqB;AAC1C,wBAAA,SAAS,EAAE,WAAW;AACvB,qBAAA,EAAA,QAAA,EAAA,mKAAA,EAAA;;;AElCH;;AAEG;;;;"}