ngwr 10.2.1 → 11.0.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 (425) hide show
  1. package/README.md +39 -19
  2. package/anchor/styles/_index.scss +4 -4
  3. package/back-top/styles/_index.scss +6 -1
  4. package/blur-text/styles/_index.scss +13 -0
  5. package/button/styles/_index.scss +11 -6
  6. package/carousel/styles/_index.scss +13 -2
  7. package/cascader/styles/_index.scss +2 -2
  8. package/circular-text/styles/_index.scss +2 -0
  9. package/collapse/styles/_index.scss +1 -1
  10. package/color-picker/styles/_index.scss +6 -1
  11. package/compare/styles/_index.scss +1 -2
  12. package/context-menu/styles/_index.scss +6 -1
  13. package/descriptions/styles/_index.scss +2 -2
  14. package/dialog/styles/_index.scss +7 -0
  15. package/drawer/styles/_index.scss +20 -1
  16. package/dropdown/styles/_index.scss +19 -5
  17. package/empty/styles/_index.scss +10 -0
  18. package/fesm2022/ngwr-action-sheet.mjs +13 -3
  19. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  20. package/fesm2022/ngwr-affix.mjs +40 -13
  21. package/fesm2022/ngwr-affix.mjs.map +1 -1
  22. package/fesm2022/ngwr-anchor.mjs +18 -6
  23. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  24. package/fesm2022/ngwr-aurora.mjs +2 -2
  25. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  26. package/fesm2022/ngwr-avatar.mjs +31 -7
  27. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  28. package/fesm2022/ngwr-back-top.mjs +6 -2
  29. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  30. package/fesm2022/ngwr-badge.mjs +1 -1
  31. package/fesm2022/ngwr-badge.mjs.map +1 -1
  32. package/fesm2022/ngwr-bar-chart.mjs +30 -9
  33. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  34. package/fesm2022/ngwr-blur-text.mjs +2 -2
  35. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  36. package/fesm2022/ngwr-border-glow.mjs +2 -2
  37. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  38. package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
  39. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  40. package/fesm2022/ngwr-burger.mjs +9 -4
  41. package/fesm2022/ngwr-burger.mjs.map +1 -1
  42. package/fesm2022/ngwr-button-testing.mjs +96 -0
  43. package/fesm2022/ngwr-button-testing.mjs.map +1 -0
  44. package/fesm2022/ngwr-button.mjs +31 -14
  45. package/fesm2022/ngwr-button.mjs.map +1 -1
  46. package/fesm2022/ngwr-calendar-heatmap.mjs +40 -8
  47. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  48. package/fesm2022/ngwr-calendar.mjs +20 -1
  49. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  50. package/fesm2022/ngwr-card.mjs +2 -2
  51. package/fesm2022/ngwr-card.mjs.map +1 -1
  52. package/fesm2022/ngwr-carousel-testing.mjs +377 -0
  53. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -0
  54. package/fesm2022/ngwr-carousel.mjs +67 -13
  55. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  56. package/fesm2022/ngwr-cascader-testing.mjs +534 -0
  57. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -0
  58. package/fesm2022/ngwr-cascader.mjs +2 -2
  59. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  60. package/fesm2022/ngwr-checkbox-testing.mjs +90 -0
  61. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -0
  62. package/fesm2022/ngwr-checkbox.mjs +9 -3
  63. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  64. package/fesm2022/ngwr-circular-text.mjs +2 -2
  65. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  66. package/fesm2022/ngwr-click-spark.mjs +2 -2
  67. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  68. package/fesm2022/ngwr-collapse-testing.mjs +368 -0
  69. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -0
  70. package/fesm2022/ngwr-color-picker.mjs +134 -14
  71. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  72. package/fesm2022/ngwr-command-palette-testing.mjs +589 -0
  73. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -0
  74. package/fesm2022/ngwr-command-palette.mjs +58 -8
  75. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  76. package/fesm2022/ngwr-compare.mjs +103 -13
  77. package/fesm2022/ngwr-compare.mjs.map +1 -1
  78. package/fesm2022/ngwr-config.mjs +106 -0
  79. package/fesm2022/ngwr-config.mjs.map +1 -0
  80. package/fesm2022/ngwr-context-menu-testing.mjs +631 -0
  81. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -0
  82. package/fesm2022/ngwr-context-menu.mjs +94 -8
  83. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  84. package/fesm2022/ngwr-cookie.mjs +5 -1
  85. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  86. package/fesm2022/ngwr-counter.mjs +11 -3
  87. package/fesm2022/ngwr-counter.mjs.map +1 -1
  88. package/fesm2022/ngwr-date-adapter-luxon.mjs +52 -4
  89. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -1
  90. package/fesm2022/ngwr-date-adapter.mjs +54 -8
  91. package/fesm2022/ngwr-date-adapter.mjs.map +1 -1
  92. package/fesm2022/ngwr-date-picker-testing.mjs +766 -0
  93. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -0
  94. package/fesm2022/ngwr-date-picker.mjs +73 -3
  95. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  96. package/fesm2022/ngwr-decrypt-text.mjs +27 -9
  97. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  98. package/fesm2022/ngwr-dialog-testing.mjs +89 -0
  99. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -0
  100. package/fesm2022/ngwr-directives.mjs +36 -12
  101. package/fesm2022/ngwr-directives.mjs.map +1 -1
  102. package/fesm2022/ngwr-donut-chart.mjs +22 -5
  103. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  104. package/fesm2022/ngwr-drag-drop.mjs +2 -2
  105. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  106. package/fesm2022/ngwr-drawer-testing.mjs +250 -0
  107. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -0
  108. package/fesm2022/ngwr-dropdown-testing.mjs +291 -0
  109. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -0
  110. package/fesm2022/ngwr-dropdown.mjs +10 -2
  111. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  112. package/fesm2022/ngwr-empty.mjs +24 -7
  113. package/fesm2022/ngwr-empty.mjs.map +1 -1
  114. package/fesm2022/ngwr-event-calendar.mjs +57 -10
  115. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  116. package/fesm2022/ngwr-falling-text.mjs +2 -2
  117. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  118. package/fesm2022/ngwr-file-upload-testing.mjs +332 -0
  119. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -0
  120. package/fesm2022/ngwr-form-testing.mjs +451 -0
  121. package/fesm2022/ngwr-form-testing.mjs.map +1 -0
  122. package/fesm2022/ngwr-form.mjs +19 -3
  123. package/fesm2022/ngwr-form.mjs.map +1 -1
  124. package/fesm2022/ngwr-fuzzy-text.mjs +9 -2
  125. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  126. package/fesm2022/ngwr-gauge.mjs +19 -5
  127. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  128. package/fesm2022/ngwr-glitch-text.mjs +2 -2
  129. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  130. package/fesm2022/ngwr-gradient-text.mjs +2 -2
  131. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  132. package/fesm2022/ngwr-i18n-en.mjs +50 -0
  133. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  134. package/fesm2022/ngwr-i18n-ru.mjs +48 -0
  135. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  136. package/fesm2022/ngwr-image-cropper.mjs +23 -0
  137. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  138. package/fesm2022/ngwr-input-number-testing.mjs +306 -0
  139. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -0
  140. package/fesm2022/ngwr-input-number.mjs +35 -3
  141. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  142. package/fesm2022/ngwr-input-otp-testing.mjs +419 -0
  143. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -0
  144. package/fesm2022/ngwr-input-otp.mjs +36 -5
  145. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  146. package/fesm2022/ngwr-input-testing.mjs +102 -0
  147. package/fesm2022/ngwr-input-testing.mjs.map +1 -0
  148. package/fesm2022/ngwr-input.mjs +47 -10
  149. package/fesm2022/ngwr-input.mjs.map +1 -1
  150. package/fesm2022/ngwr-knob.mjs +44 -8
  151. package/fesm2022/ngwr-knob.mjs.map +1 -1
  152. package/fesm2022/ngwr-layout.mjs +21 -5
  153. package/fesm2022/ngwr-layout.mjs.map +1 -1
  154. package/fesm2022/ngwr-lightbox.mjs +33 -6
  155. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  156. package/fesm2022/ngwr-line-chart.mjs +17 -4
  157. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  158. package/fesm2022/ngwr-list.mjs +2 -2
  159. package/fesm2022/ngwr-list.mjs.map +1 -1
  160. package/fesm2022/ngwr-loading-bar.mjs +2 -2
  161. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  162. package/fesm2022/ngwr-markdown-testing.mjs +516 -0
  163. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -0
  164. package/fesm2022/ngwr-markdown.mjs +1492 -0
  165. package/fesm2022/ngwr-markdown.mjs.map +1 -0
  166. package/fesm2022/ngwr-marquee.mjs +17 -6
  167. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  168. package/fesm2022/ngwr-mention-testing.mjs +513 -0
  169. package/fesm2022/ngwr-mention-testing.mjs.map +1 -0
  170. package/fesm2022/ngwr-mention.mjs +10 -1
  171. package/fesm2022/ngwr-mention.mjs.map +1 -1
  172. package/fesm2022/ngwr-meta.mjs +22 -7
  173. package/fesm2022/ngwr-meta.mjs.map +1 -1
  174. package/fesm2022/ngwr-meter-group.mjs +19 -5
  175. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  176. package/fesm2022/ngwr-overlay.mjs +41 -1
  177. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  178. package/fesm2022/ngwr-pagination-testing.mjs +389 -0
  179. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -0
  180. package/fesm2022/ngwr-pagination.mjs +14 -2
  181. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  182. package/fesm2022/ngwr-popconfirm-testing.mjs +309 -0
  183. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -0
  184. package/fesm2022/ngwr-popconfirm.mjs +95 -17
  185. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  186. package/fesm2022/ngwr-popover-testing.mjs +316 -0
  187. package/fesm2022/ngwr-popover-testing.mjs.map +1 -0
  188. package/fesm2022/ngwr-popover.mjs +21 -3
  189. package/fesm2022/ngwr-popover.mjs.map +1 -1
  190. package/fesm2022/ngwr-pull-to-refresh.mjs +17 -2
  191. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  192. package/fesm2022/ngwr-qr.mjs +13 -3
  193. package/fesm2022/ngwr-qr.mjs.map +1 -1
  194. package/fesm2022/ngwr-radio-testing.mjs +392 -0
  195. package/fesm2022/ngwr-radio-testing.mjs.map +1 -0
  196. package/fesm2022/ngwr-radio.mjs +10 -3
  197. package/fesm2022/ngwr-radio.mjs.map +1 -1
  198. package/fesm2022/ngwr-rating-testing.mjs +384 -0
  199. package/fesm2022/ngwr-rating-testing.mjs.map +1 -0
  200. package/fesm2022/ngwr-rating.mjs +37 -2
  201. package/fesm2022/ngwr-rating.mjs.map +1 -1
  202. package/fesm2022/ngwr-segmented-testing.mjs +512 -0
  203. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -0
  204. package/fesm2022/ngwr-select-testing.mjs +268 -0
  205. package/fesm2022/ngwr-select-testing.mjs.map +1 -0
  206. package/fesm2022/ngwr-select.mjs +87 -16
  207. package/fesm2022/ngwr-select.mjs.map +1 -1
  208. package/fesm2022/ngwr-shiny-text.mjs +2 -2
  209. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  210. package/fesm2022/ngwr-sidebar.mjs +65 -12
  211. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  212. package/fesm2022/ngwr-slider-testing.mjs +478 -0
  213. package/fesm2022/ngwr-slider-testing.mjs.map +1 -0
  214. package/fesm2022/ngwr-slider.mjs +57 -13
  215. package/fesm2022/ngwr-slider.mjs.map +1 -1
  216. package/fesm2022/ngwr-sparkline.mjs +26 -6
  217. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  218. package/fesm2022/ngwr-speed-dial.mjs +37 -4
  219. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  220. package/fesm2022/ngwr-spinner.mjs +12 -3
  221. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  222. package/fesm2022/ngwr-splash-cursor.mjs +2 -2
  223. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  224. package/fesm2022/ngwr-split-text.mjs +2 -2
  225. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  226. package/fesm2022/ngwr-splitter.mjs +70 -9
  227. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  228. package/fesm2022/ngwr-spotlight-card.mjs +2 -2
  229. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  230. package/fesm2022/ngwr-star-border.mjs +2 -2
  231. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  232. package/fesm2022/ngwr-statistic.mjs +24 -7
  233. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  234. package/fesm2022/ngwr-stepper-testing.mjs +477 -0
  235. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -0
  236. package/fesm2022/ngwr-switch-testing.mjs +77 -0
  237. package/fesm2022/ngwr-switch-testing.mjs.map +1 -0
  238. package/fesm2022/ngwr-switch.mjs +9 -3
  239. package/fesm2022/ngwr-switch.mjs.map +1 -1
  240. package/fesm2022/ngwr-table-testing.mjs +580 -0
  241. package/fesm2022/ngwr-table-testing.mjs.map +1 -0
  242. package/fesm2022/ngwr-table.mjs +87 -14
  243. package/fesm2022/ngwr-table.mjs.map +1 -1
  244. package/fesm2022/ngwr-tabs-testing.mjs +617 -0
  245. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -0
  246. package/fesm2022/ngwr-tabs.mjs +38 -6
  247. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  248. package/fesm2022/ngwr-textarea-testing.mjs +238 -0
  249. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -0
  250. package/fesm2022/ngwr-textarea.mjs +32 -4
  251. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  252. package/fesm2022/ngwr-tilt-card.mjs +2 -2
  253. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  254. package/fesm2022/ngwr-toast-testing.mjs +101 -0
  255. package/fesm2022/ngwr-toast-testing.mjs.map +1 -0
  256. package/fesm2022/ngwr-toast.mjs +2 -2
  257. package/fesm2022/ngwr-toast.mjs.map +1 -1
  258. package/fesm2022/ngwr-tour.mjs +61 -9
  259. package/fesm2022/ngwr-tour.mjs.map +1 -1
  260. package/fesm2022/ngwr-transfer-testing.mjs +538 -0
  261. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -0
  262. package/fesm2022/ngwr-transfer.mjs +45 -14
  263. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  264. package/fesm2022/ngwr-tree-testing.mjs +734 -0
  265. package/fesm2022/ngwr-tree-testing.mjs.map +1 -0
  266. package/fesm2022/ngwr-tree.mjs +73 -7
  267. package/fesm2022/ngwr-tree.mjs.map +1 -1
  268. package/fesm2022/ngwr-typewriter.mjs +5 -3
  269. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  270. package/fesm2022/ngwr-version.mjs +1 -1
  271. package/fesm2022/ngwr-version.mjs.map +1 -1
  272. package/fesm2022/ngwr-virtual-scroll.mjs +31 -5
  273. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  274. package/fesm2022/ngwr-waves.mjs +3 -3
  275. package/fesm2022/ngwr-waves.mjs.map +1 -1
  276. package/fesm2022/ngwr-window.mjs +8 -0
  277. package/fesm2022/ngwr-window.mjs.map +1 -1
  278. package/gauge/styles/_index.scss +1 -1
  279. package/glitch-text/styles/_index.scss +4 -0
  280. package/i18n/en.json +48 -0
  281. package/i18n/ru.json +48 -0
  282. package/image-cropper/styles/_index.scss +13 -1
  283. package/input/styles/_index.scss +2 -2
  284. package/input-number/styles/_index.scss +7 -2
  285. package/knob/styles/_index.scss +8 -1
  286. package/layout/styles/_index.scss +3 -3
  287. package/lightbox/styles/_index.scss +10 -2
  288. package/line-chart/styles/_index.scss +6 -0
  289. package/llms-full.txt +151 -4
  290. package/llms.txt +57 -8
  291. package/loading-bar/styles/_index.scss +1 -2
  292. package/markdown/styles/_index.scss +296 -0
  293. package/marquee/styles/_index.scss +12 -0
  294. package/mcp/api.js +512 -0
  295. package/mcp/catalog.js +157 -0
  296. package/mcp/server.js +273 -0
  297. package/mcp/tools.js +366 -0
  298. package/meter-group/styles/_index.scss +1 -1
  299. package/package.json +186 -35
  300. package/pagination/styles/_index.scss +6 -6
  301. package/popover/styles/_index.scss +37 -5
  302. package/rating/styles/_index.scss +10 -0
  303. package/schematics/use/symbol-map.json +2 -0
  304. package/segmented/styles/_index.scss +7 -0
  305. package/select/styles/_index.scss +29 -9
  306. package/sidebar/styles/_index.scss +30 -5
  307. package/slider/styles/_index.scss +15 -0
  308. package/speed-dial/styles/_index.scss +20 -0
  309. package/split-text/styles/_index.scss +13 -0
  310. package/star-border/styles/_index.scss +2 -0
  311. package/stepper/styles/_index.scss +1 -1
  312. package/styles/_grid.scss +4 -4
  313. package/styles.scss +1 -0
  314. package/switch/styles/_index.scss +5 -3
  315. package/table/styles/_index.scss +22 -11
  316. package/tabs/styles/_index.scss +11 -1
  317. package/textarea/styles/_index.scss +5 -0
  318. package/theme/styles/_colors.scss +29 -13
  319. package/theme/styles/_dark.scss +11 -3
  320. package/theme/styles/_mixins.scss +3 -0
  321. package/timeline/styles/_index.scss +2 -2
  322. package/toast/styles/_index.scss +17 -8
  323. package/toolbar/styles/_index.scss +3 -3
  324. package/tour/styles/_index.scss +7 -1
  325. package/tree/styles/_index.scss +5 -3
  326. package/types/ngwr-action-sheet.d.ts +9 -1
  327. package/types/ngwr-affix.d.ts +7 -1
  328. package/types/ngwr-anchor.d.ts +8 -1
  329. package/types/ngwr-avatar.d.ts +13 -3
  330. package/types/ngwr-bar-chart.d.ts +16 -0
  331. package/types/ngwr-burger.d.ts +6 -2
  332. package/types/ngwr-button-testing.d.ts +85 -0
  333. package/types/ngwr-button.d.ts +26 -10
  334. package/types/ngwr-calendar-heatmap.d.ts +16 -2
  335. package/types/ngwr-calendar.d.ts +10 -0
  336. package/types/ngwr-carousel-testing.d.ts +257 -0
  337. package/types/ngwr-carousel.d.ts +20 -4
  338. package/types/ngwr-cascader-testing.d.ts +392 -0
  339. package/types/ngwr-checkbox-testing.d.ts +77 -0
  340. package/types/ngwr-checkbox.d.ts +7 -2
  341. package/types/ngwr-collapse-testing.d.ts +290 -0
  342. package/types/ngwr-color-picker.d.ts +20 -11
  343. package/types/ngwr-command-palette-testing.d.ts +437 -0
  344. package/types/ngwr-command-palette.d.ts +25 -2
  345. package/types/ngwr-compare.d.ts +50 -2
  346. package/types/ngwr-config.d.ts +176 -0
  347. package/types/ngwr-context-menu-testing.d.ts +401 -0
  348. package/types/ngwr-context-menu.d.ts +65 -17
  349. package/types/ngwr-counter.d.ts +6 -1
  350. package/types/ngwr-date-adapter-luxon.d.ts +26 -0
  351. package/types/ngwr-date-picker-testing.d.ts +518 -0
  352. package/types/ngwr-date-picker.d.ts +4 -0
  353. package/types/ngwr-dialog-testing.d.ts +79 -0
  354. package/types/ngwr-directives.d.ts +10 -1
  355. package/types/ngwr-donut-chart.d.ts +8 -1
  356. package/types/ngwr-drawer-testing.d.ts +196 -0
  357. package/types/ngwr-dropdown-testing.d.ts +202 -0
  358. package/types/ngwr-dropdown.d.ts +7 -1
  359. package/types/ngwr-empty.d.ts +19 -4
  360. package/types/ngwr-event-calendar.d.ts +26 -1
  361. package/types/ngwr-file-upload-testing.d.ts +194 -0
  362. package/types/ngwr-form-testing.d.ts +379 -0
  363. package/types/ngwr-form.d.ts +17 -0
  364. package/types/ngwr-gauge.d.ts +13 -1
  365. package/types/ngwr-image-cropper.d.ts +10 -0
  366. package/types/ngwr-input-number-testing.d.ts +226 -0
  367. package/types/ngwr-input-number.d.ts +29 -1
  368. package/types/ngwr-input-otp-testing.d.ts +299 -0
  369. package/types/ngwr-input-otp.d.ts +23 -2
  370. package/types/ngwr-input-testing.d.ts +84 -0
  371. package/types/ngwr-input.d.ts +20 -6
  372. package/types/ngwr-knob.d.ts +18 -0
  373. package/types/ngwr-layout.d.ts +1 -0
  374. package/types/ngwr-lightbox.d.ts +17 -3
  375. package/types/ngwr-line-chart.d.ts +9 -2
  376. package/types/ngwr-markdown-testing.d.ts +462 -0
  377. package/types/ngwr-markdown.d.ts +534 -0
  378. package/types/ngwr-marquee.d.ts +12 -2
  379. package/types/ngwr-mention-testing.d.ts +359 -0
  380. package/types/ngwr-meta.d.ts +4 -2
  381. package/types/ngwr-meter-group.d.ts +11 -1
  382. package/types/ngwr-overlay.d.ts +33 -2
  383. package/types/ngwr-pagination-testing.d.ts +291 -0
  384. package/types/ngwr-pagination.d.ts +1 -0
  385. package/types/ngwr-popconfirm-testing.d.ts +235 -0
  386. package/types/ngwr-popconfirm.d.ts +31 -21
  387. package/types/ngwr-popover-testing.d.ts +221 -0
  388. package/types/ngwr-popover.d.ts +9 -0
  389. package/types/ngwr-pull-to-refresh.d.ts +7 -0
  390. package/types/ngwr-qr.d.ts +9 -1
  391. package/types/ngwr-radio-testing.d.ts +296 -0
  392. package/types/ngwr-radio.d.ts +8 -2
  393. package/types/ngwr-rating-testing.d.ts +291 -0
  394. package/types/ngwr-rating.d.ts +23 -0
  395. package/types/ngwr-segmented-testing.d.ts +401 -0
  396. package/types/ngwr-select-testing.d.ts +175 -0
  397. package/types/ngwr-select.d.ts +45 -4
  398. package/types/ngwr-sidebar.d.ts +17 -2
  399. package/types/ngwr-slider-testing.d.ts +329 -0
  400. package/types/ngwr-slider.d.ts +35 -1
  401. package/types/ngwr-sparkline.d.ts +12 -2
  402. package/types/ngwr-speed-dial.d.ts +17 -18
  403. package/types/ngwr-spinner.d.ts +8 -1
  404. package/types/ngwr-splitter.d.ts +24 -1
  405. package/types/ngwr-statistic.d.ts +12 -2
  406. package/types/ngwr-stepper-testing.d.ts +346 -0
  407. package/types/ngwr-switch-testing.d.ts +66 -0
  408. package/types/ngwr-switch.d.ts +7 -2
  409. package/types/ngwr-table-testing.d.ts +423 -0
  410. package/types/ngwr-table.d.ts +42 -0
  411. package/types/ngwr-tabs-testing.d.ts +470 -0
  412. package/types/ngwr-tabs.d.ts +9 -0
  413. package/types/ngwr-textarea-testing.d.ts +184 -0
  414. package/types/ngwr-textarea.d.ts +7 -2
  415. package/types/ngwr-toast-testing.d.ts +80 -0
  416. package/types/ngwr-tour.d.ts +27 -0
  417. package/types/ngwr-transfer-testing.d.ts +444 -0
  418. package/types/ngwr-transfer.d.ts +16 -1
  419. package/types/ngwr-tree-testing.d.ts +501 -0
  420. package/types/ngwr-tree.d.ts +33 -0
  421. package/types/ngwr-version.d.ts +1 -1
  422. package/types/ngwr-virtual-scroll.d.ts +13 -1
  423. package/typewriter/styles/_index.scss +1 -1
  424. package/typography/styles/_index.scss +1 -1
  425. package/window/styles/_index.scss +32 -9
@@ -0,0 +1,368 @@
1
+ import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+ /**
10
+ * Test harness for one `<wr-collapse>` — a disclosure: a header button, and the region
11
+ * it opens. {@link WrCollapseGroupHarness} is the container several of them sit in.
12
+ *
13
+ * Open and closed are read from the header's `aria-expanded`, never from the panel's
14
+ * size, and that is not merely a jsdom workaround. The body animates through
15
+ * `grid-template-rows: 0fr → 1fr`, so a measured box answers with whatever frame the
16
+ * transition happens to be on; a unit test has no layout at all, which makes every box
17
+ * 0×0 and every such answer wrong in the same direction. The attribute is also the only
18
+ * thing a screen-reader user gets — a panel that slides open without flipping it is
19
+ * closed as far as they are concerned.
20
+ *
21
+ * The body stays in the DOM while the panel is closed. It has to: there would be no
22
+ * height to animate otherwise. So its text is READABLE from a spec long before it is
23
+ * visible on screen, which is why {@link getContentText} refuses to answer for a closed
24
+ * panel instead of handing back invisible text, and why {@link isContentHidden} exists
25
+ * to assert that a closed panel really is out of reach.
26
+ *
27
+ * The region itself is a plain `<div>` — the component gives it no `role="region"` — so
28
+ * `aria-expanded` plus `aria-controls` is the whole of the wiring, and
29
+ * {@link isRegionBound} is how a spec checks it still points at this panel's own body.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const loader = TestbedHarnessEnvironment.loader(fixture);
34
+ * const shipping = await loader.getHarness(WrCollapseHarness.with({ title: 'Shipping' }));
35
+ *
36
+ * await shipping.open();
37
+ * expect(await shipping.getContentText()).toContain('Ships Monday');
38
+ * ```
39
+ *
40
+ * @see https://ngwr.dev/guides/testing
41
+ */
42
+ class WrCollapseHarness extends ComponentHarness {
43
+ static hostSelector = 'wr-collapse';
44
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
45
+ static with(options = {}) {
46
+ return new HarnessPredicate(WrCollapseHarness, options)
47
+ .addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))
48
+ .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open)
49
+ .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);
50
+ }
51
+ header = this.locatorFor('button.wr-collapse__header');
52
+ body = this.locatorFor('.wr-collapse__body');
53
+ /**
54
+ * The header text, trimmed.
55
+ *
56
+ * Read from `.wr-collapse__title` rather than from the header button. Reading the
57
+ * button would return the same string today — the chevron beside the title is an
58
+ * `<svg>`, and an `<svg>` contributes no text — but the title element is the thing that
59
+ * holds the name, so an icon that ever grows a `<text>` node or a label, or a badge
60
+ * added next to the title, cannot leak into it.
61
+ */
62
+ async getTitle() {
63
+ return (await this.locatorFor('.wr-collapse__title')()).text();
64
+ }
65
+ /**
66
+ * Whether the panel is open, from the header's `aria-expanded`.
67
+ *
68
+ * The `wr-collapse--open` modifier on the host is equally public and says the same
69
+ * thing today, but the attribute is the one a screen reader acts on: a panel that
70
+ * looks open while announcing `false` is the bug worth catching, and reading the class
71
+ * would agree with the animation and miss it.
72
+ */
73
+ async isOpen() {
74
+ return (await (await this.header()).getAttribute('aria-expanded')) === 'true';
75
+ }
76
+ /**
77
+ * Whether the panel refuses interaction.
78
+ *
79
+ * From the header's own `disabled` property — the thing that actually turns a click
80
+ * away, and what the CDK consults before dispatching one — rather than from the
81
+ * `wr-collapse--disabled` modifier the host also carries. Both are public, but the
82
+ * class is styling: a panel could lose it and still refuse every click, and what a
83
+ * spec wants to know is whether the control is inert.
84
+ */
85
+ async isDisabled() {
86
+ return (await this.header()).getProperty('disabled');
87
+ }
88
+ /**
89
+ * The id the header's `aria-controls` names, or `null` when it names nothing.
90
+ *
91
+ * Per-instance and generated, so a spec cannot predict it — what it can do is check
92
+ * that two panels do not share one, and {@link isRegionBound} does the rest.
93
+ */
94
+ async getRegionId() {
95
+ return (await this.header()).getAttribute('aria-controls');
96
+ }
97
+ /**
98
+ * Whether the header points at THIS panel's body, and at nothing else on the page.
99
+ *
100
+ * Both halves matter, and neither is provable from the header alone. The id is
101
+ * resolved inside the host first, so a header naming a SIBLING panel's body — which
102
+ * resolves perfectly well from the document and would look correctly wired — fails
103
+ * here. Then it is counted across the document, because two bodies answering to one
104
+ * id hand every reference to whichever comes first, and the second panel's region
105
+ * becomes unreachable to anything that follows the reference.
106
+ */
107
+ async isRegionBound() {
108
+ const id = await this.getRegionId();
109
+ if (id === null)
110
+ return false;
111
+ const own = await this.locatorForOptional(`.wr-collapse__body#${id}`)();
112
+ if (!own)
113
+ return false;
114
+ const everywhere = await this.documentRootLocatorFactory().locatorForAll(`#${id}`)();
115
+ return everywhere.length === 1;
116
+ }
117
+ /**
118
+ * Whether the region is hidden from assistive tech AND from the keyboard.
119
+ *
120
+ * Two attributes, because a closed panel needs both and they cover different people:
121
+ * `aria-hidden` keeps the content out of the screen reader's document, `inert` keeps
122
+ * Tab from landing on a link inside a panel nobody can see. The body is never removed
123
+ * from the DOM — the height animation needs it — so these are the only thing standing
124
+ * between a collapsed panel and a keyboard user, and losing either is invisible on
125
+ * screen.
126
+ *
127
+ * They are asserted together on purpose: this answers "is the closed content actually
128
+ * out of reach", and half of it is not.
129
+ */
130
+ async isContentHidden() {
131
+ const body = await this.body();
132
+ const hiddenFromAt = (await body.getAttribute('aria-hidden')) === 'true';
133
+ const inert = (await body.getAttribute('inert')) !== null;
134
+ return hiddenFromAt && inert;
135
+ }
136
+ /**
137
+ * The text of an OPEN panel's region, trimmed.
138
+ *
139
+ * Throws while the panel is closed rather than answering, because the body is still in
140
+ * the DOM and the text would come back exactly as it does when open — a spec asserting
141
+ * it would be pinning content the user cannot see, and would keep passing if the panel
142
+ * stopped opening at all.
143
+ */
144
+ async getContentText() {
145
+ if (!(await this.isOpen())) {
146
+ throw new Error(`WrCollapseHarness.getContentText(): "${await this.getTitle()}" is closed. Its content stays in the ` +
147
+ 'DOM while collapsed — the body has to be there for the height to animate — so reading it now would ' +
148
+ 'assert text nobody can see. Open the panel first, or ask isContentHidden().');
149
+ }
150
+ return (await this.locatorFor('.wr-collapse__inner')()).text();
151
+ }
152
+ /** Open the panel. An already-open one is left alone; a disabled one throws. */
153
+ async open() {
154
+ if (await this.isOpen())
155
+ return;
156
+ await this.toggle();
157
+ if (!(await this.isOpen())) {
158
+ throw new Error(`WrCollapseHarness.open(): "${await this.getTitle()}" did not open. A disabled panel refuses its own ` +
159
+ 'header — the button carries `disabled`, and the component checks it again before toggling.');
160
+ }
161
+ }
162
+ /** Close the panel. An already-closed one is left alone; a disabled one throws. */
163
+ async close() {
164
+ if (!(await this.isOpen()))
165
+ return;
166
+ await this.toggle();
167
+ if (await this.isOpen()) {
168
+ throw new Error(`WrCollapseHarness.close(): "${await this.getTitle()}" did not close. A disabled panel that was opened ` +
169
+ 'through `[open]` cannot be closed from the page at all: the toggle refuses while `disabled` is set, ' +
170
+ 'whichever direction the click would have gone.');
171
+ }
172
+ }
173
+ /**
174
+ * Click the header — the panel's entire interactive surface.
175
+ *
176
+ * A click rather than Enter / Space, and nothing is lost by it: the header is a native
177
+ * `<button>`, so a real keyboard activation is the BROWSER synthesising a click from
178
+ * the key press, and jsdom synthesises none. The click is dispatched onto the header
179
+ * itself, so it needs no coordinate and cannot miss in a test that has no layout.
180
+ *
181
+ * A disabled panel ignores this and stays as it was, twice over: the CDK withholds the
182
+ * `click` from an element carrying `disabled`, and the component checks `disabled`
183
+ * again inside its own toggle. Assert rather than assume, or reach for {@link open} /
184
+ * {@link close}, which say so.
185
+ */
186
+ async toggle() {
187
+ return (await this.header()).click();
188
+ }
189
+ /**
190
+ * Move keyboard focus to the header — the only focusable element the component itself
191
+ * renders. Projected content can of course hold its own (which is why a closed panel is
192
+ * `inert`), but that belongs to the consumer, not to this harness.
193
+ */
194
+ async focus() {
195
+ return (await this.header()).focus();
196
+ }
197
+ /** Whether the header currently holds focus. */
198
+ async isFocused() {
199
+ return (await this.header()).isFocused();
200
+ }
201
+ }
202
+
203
+ /**
204
+ * @license
205
+ *
206
+ * Use of this source code is governed by an MIT-style license that can be
207
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
208
+ */
209
+ /**
210
+ * Test harness for `<wr-collapse-group>` and `<wr-accordion>` — the container a set of
211
+ * {@link WrCollapseHarness} panels sits in, matched in both of its element shapes.
212
+ *
213
+ * There is deliberately no `isAccordion()`, and the reason is worth knowing before a
214
+ * spec goes looking for one: single-open behaviour is not written down anywhere on the
215
+ * page. `<wr-accordion>` carries the marker class `wr-collapse-group--accordion`, but
216
+ * `<wr-collapse-group [accordion]="true">` enforces exactly the same rule and carries
217
+ * nothing — a bound input reaches the component without touching the DOM, and even the
218
+ * literal `accordion` attribute survives only because Angular leaves a static attribute
219
+ * alone. So the class answers "which element did the template use", not "does this group
220
+ * close siblings", and a harness reporting it would call the group form false. Assert the
221
+ * BEHAVIOUR instead: open two panels and read {@link getOpenTitles} — the assertion that
222
+ * would catch a broken accordion anyway.
223
+ *
224
+ * A collapse group is also not a composite widget. Every header is a native `<button>`
225
+ * in the normal tab order, with no roving `tabindex` and no arrow-key navigation (the
226
+ * APG lists both as optional for an accordion), so there is no "active" panel to tell
227
+ * apart from the open one — {@link getFocusedTitle} is the only cursor there is.
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * const settings = await loader.getHarness(WrCollapseGroupHarness.with({ panelTitle: 'Profile' }));
232
+ *
233
+ * await settings.openPanel({ title: 'Security' });
234
+ * expect(await settings.getOpenTitles()).toEqual(['Security']);
235
+ * ```
236
+ *
237
+ * @see https://ngwr.dev/guides/testing
238
+ */
239
+ class WrCollapseGroupHarness extends ComponentHarness {
240
+ /** Both shapes the container ships in: the plain group, and the always-accordion one. */
241
+ static hostSelector = 'wr-collapse-group, wr-accordion';
242
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
243
+ static with(options = {}) {
244
+ return new HarnessPredicate(WrCollapseGroupHarness, options)
245
+ .addOption('panelTitle', options.panelTitle, async (harness, title) => {
246
+ for (const panel of await harness.getPanelTitles()) {
247
+ if (await HarnessPredicate.stringMatches(panel, title))
248
+ return true;
249
+ }
250
+ return false;
251
+ })
252
+ .addOption('openPanelTitle', options.openPanelTitle, async (harness, title) => {
253
+ for (const open of await harness.getOpenTitles()) {
254
+ if (await HarnessPredicate.stringMatches(open, title))
255
+ return true;
256
+ }
257
+ return false;
258
+ });
259
+ }
260
+ /**
261
+ * The group's panels, in DOM order.
262
+ *
263
+ * Every `<wr-collapse>` inside the host, which includes one nested in another panel's
264
+ * content and every panel of a nested group — the group scopes no more tightly than
265
+ * the DOM does, and a nested accordion's children are still descendants of this one.
266
+ */
267
+ async getPanels(filters = {}) {
268
+ return this.locatorForAll(WrCollapseHarness.with(filters))();
269
+ }
270
+ /** The header text of every panel, in DOM order. */
271
+ async getPanelTitles() {
272
+ const panels = await this.getPanels();
273
+ return Promise.all(panels.map(panel => panel.getTitle()));
274
+ }
275
+ /** The first panel matching the filters, or a failure naming the panels that exist. */
276
+ async getPanel(filters) {
277
+ const [panel] = await this.getPanels(filters);
278
+ if (!panel) {
279
+ throw new Error(`WrCollapseGroupHarness.getPanel(): no panel matched ${JSON.stringify(filters)}. This group holds: ` +
280
+ `${(await this.getPanelTitles()).join(', ')}.`);
281
+ }
282
+ return panel;
283
+ }
284
+ /**
285
+ * The panel at a 0-based position in DOM order — the other half of "by text or by
286
+ * index", and the only way to address a panel whose title is not unique.
287
+ *
288
+ * Throws for an index past the end rather than resolving to nothing, and says how many
289
+ * panels there are: an out-of-range index is a mistake in the spec, and a `undefined`
290
+ * handed back would surface three lines later as a failure about something else.
291
+ */
292
+ async getPanelAt(index) {
293
+ const panels = await this.getPanels();
294
+ const panel = panels[index];
295
+ if (!panel) {
296
+ throw new Error(`WrCollapseGroupHarness.getPanelAt(): index ${index} is out of range — this group holds ` +
297
+ `${panels.length} panel(s), so the last index is ${panels.length - 1}.`);
298
+ }
299
+ return panel;
300
+ }
301
+ /** The panels that are open, in DOM order. In accordion mode there is at most one. */
302
+ async getOpenPanels() {
303
+ return this.getPanels({ open: true });
304
+ }
305
+ /**
306
+ * The header text of every open panel, in DOM order.
307
+ *
308
+ * The assertion an accordion is worth pinning with: opening a second panel has to leave
309
+ * this a list of ONE, and it reads the state from each header's `aria-expanded`, so a
310
+ * sibling that collapsed on screen while still announcing itself open fails here.
311
+ */
312
+ async getOpenTitles() {
313
+ const open = await this.getOpenPanels();
314
+ return Promise.all(open.map(panel => panel.getTitle()));
315
+ }
316
+ /**
317
+ * Open the first panel matching the filters.
318
+ *
319
+ * In accordion mode this is also what CLOSES the siblings — the group is told by the
320
+ * panel that opened, not by the click — so a spec that opens two panels in a row is
321
+ * asserting the mode, and {@link getOpenTitles} is where the answer shows up. Throws
322
+ * when nothing matched, and again when the panel refused to open (it is disabled).
323
+ */
324
+ async openPanel(filters) {
325
+ await (await this.getPanel(filters)).open();
326
+ }
327
+ /** Close the first panel matching the filters. */
328
+ async closePanel(filters) {
329
+ await (await this.getPanel(filters)).close();
330
+ }
331
+ /**
332
+ * Close every open panel.
333
+ *
334
+ * The open panels are resolved first and then closed one at a time. Every click a
335
+ * harness makes already awaits its own change detection, so closing them concurrently
336
+ * would work too — this reads in the order a user would click, and keeps each failure
337
+ * attributable to the panel that caused it. A disabled panel that was opened through
338
+ * `[open]` cannot be closed from the page and throws — the same failure
339
+ * {@link WrCollapseHarness.close} reports, for the same reason.
340
+ */
341
+ async closeAll() {
342
+ for (const panel of await this.getOpenPanels()) {
343
+ await panel.close();
344
+ }
345
+ }
346
+ /**
347
+ * The header text of the panel whose header holds focus, or `null` when focus is
348
+ * elsewhere.
349
+ *
350
+ * Every header is its own tab stop here, so this reports where focus actually IS
351
+ * rather than where the group would send it — there is no roving tab stop to diverge
352
+ * from.
353
+ */
354
+ async getFocusedTitle() {
355
+ for (const panel of await this.getPanels()) {
356
+ if (await panel.isFocused())
357
+ return panel.getTitle();
358
+ }
359
+ return null;
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Generated bundle index. Do not edit.
365
+ */
366
+
367
+ export { WrCollapseGroupHarness, WrCollapseHarness };
368
+ //# sourceMappingURL=ngwr-collapse-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-collapse-testing.mjs","sources":["../../../projects/lib/collapse/testing/wr-collapse-harness.ts","../../../projects/lib/collapse/testing/wr-collapse-group-harness.ts","../../../projects/lib/collapse/testing/ngwr-collapse-testing.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 { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrCollapseHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one `<wr-collapse>` — a disclosure: a header button, and the region\n * it opens. {@link WrCollapseGroupHarness} is the container several of them sit in.\n *\n * Open and closed are read from the header's `aria-expanded`, never from the panel's\n * size, and that is not merely a jsdom workaround. The body animates through\n * `grid-template-rows: 0fr → 1fr`, so a measured box answers with whatever frame the\n * transition happens to be on; a unit test has no layout at all, which makes every box\n * 0×0 and every such answer wrong in the same direction. The attribute is also the only\n * thing a screen-reader user gets — a panel that slides open without flipping it is\n * closed as far as they are concerned.\n *\n * The body stays in the DOM while the panel is closed. It has to: there would be no\n * height to animate otherwise. So its text is READABLE from a spec long before it is\n * visible on screen, which is why {@link getContentText} refuses to answer for a closed\n * panel instead of handing back invisible text, and why {@link isContentHidden} exists\n * to assert that a closed panel really is out of reach.\n *\n * The region itself is a plain `<div>` — the component gives it no `role=\"region\"` — so\n * `aria-expanded` plus `aria-controls` is the whole of the wiring, and\n * {@link isRegionBound} is how a spec checks it still points at this panel's own body.\n *\n * @example\n * ```ts\n * const loader = TestbedHarnessEnvironment.loader(fixture);\n * const shipping = await loader.getHarness(WrCollapseHarness.with({ title: 'Shipping' }));\n *\n * await shipping.open();\n * expect(await shipping.getContentText()).toContain('Ships Monday');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCollapseHarness extends ComponentHarness {\n static hostSelector = 'wr-collapse';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrCollapseHarnessFilters = {}): HarnessPredicate<WrCollapseHarness> {\n return new HarnessPredicate(WrCollapseHarness, options)\n .addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly header = this.locatorFor('button.wr-collapse__header');\n private readonly body = this.locatorFor('.wr-collapse__body');\n\n /**\n * The header text, trimmed.\n *\n * Read from `.wr-collapse__title` rather than from the header button. Reading the\n * button would return the same string today — the chevron beside the title is an\n * `<svg>`, and an `<svg>` contributes no text — but the title element is the thing that\n * holds the name, so an icon that ever grows a `<text>` node or a label, or a badge\n * added next to the title, cannot leak into it.\n */\n async getTitle(): Promise<string> {\n return (await this.locatorFor('.wr-collapse__title')()).text();\n }\n\n /**\n * Whether the panel is open, from the header's `aria-expanded`.\n *\n * The `wr-collapse--open` modifier on the host is equally public and says the same\n * thing today, but the attribute is the one a screen reader acts on: a panel that\n * looks open while announcing `false` is the bug worth catching, and reading the class\n * would agree with the animation and miss it.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.header()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Whether the panel refuses interaction.\n *\n * From the header's own `disabled` property — the thing that actually turns a click\n * away, and what the CDK consults before dispatching one — rather than from the\n * `wr-collapse--disabled` modifier the host also carries. Both are public, but the\n * class is styling: a panel could lose it and still refuse every click, and what a\n * spec wants to know is whether the control is inert.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.header()).getProperty<boolean>('disabled');\n }\n\n /**\n * The id the header's `aria-controls` names, or `null` when it names nothing.\n *\n * Per-instance and generated, so a spec cannot predict it — what it can do is check\n * that two panels do not share one, and {@link isRegionBound} does the rest.\n */\n async getRegionId(): Promise<string | null> {\n return (await this.header()).getAttribute('aria-controls');\n }\n\n /**\n * Whether the header points at THIS panel's body, and at nothing else on the page.\n *\n * Both halves matter, and neither is provable from the header alone. The id is\n * resolved inside the host first, so a header naming a SIBLING panel's body — which\n * resolves perfectly well from the document and would look correctly wired — fails\n * here. Then it is counted across the document, because two bodies answering to one\n * id hand every reference to whichever comes first, and the second panel's region\n * becomes unreachable to anything that follows the reference.\n */\n async isRegionBound(): Promise<boolean> {\n const id = await this.getRegionId();\n if (id === null) return false;\n\n const own = await this.locatorForOptional(`.wr-collapse__body#${id}`)();\n if (!own) return false;\n\n const everywhere = await this.documentRootLocatorFactory().locatorForAll(`#${id}`)();\n return everywhere.length === 1;\n }\n\n /**\n * Whether the region is hidden from assistive tech AND from the keyboard.\n *\n * Two attributes, because a closed panel needs both and they cover different people:\n * `aria-hidden` keeps the content out of the screen reader's document, `inert` keeps\n * Tab from landing on a link inside a panel nobody can see. The body is never removed\n * from the DOM — the height animation needs it — so these are the only thing standing\n * between a collapsed panel and a keyboard user, and losing either is invisible on\n * screen.\n *\n * They are asserted together on purpose: this answers \"is the closed content actually\n * out of reach\", and half of it is not.\n */\n async isContentHidden(): Promise<boolean> {\n const body = await this.body();\n const hiddenFromAt = (await body.getAttribute('aria-hidden')) === 'true';\n const inert = (await body.getAttribute('inert')) !== null;\n return hiddenFromAt && inert;\n }\n\n /**\n * The text of an OPEN panel's region, trimmed.\n *\n * Throws while the panel is closed rather than answering, because the body is still in\n * the DOM and the text would come back exactly as it does when open — a spec asserting\n * it would be pinning content the user cannot see, and would keep passing if the panel\n * stopped opening at all.\n */\n async getContentText(): Promise<string> {\n if (!(await this.isOpen())) {\n throw new Error(\n `WrCollapseHarness.getContentText(): \"${await this.getTitle()}\" is closed. Its content stays in the ` +\n 'DOM while collapsed — the body has to be there for the height to animate — so reading it now would ' +\n 'assert text nobody can see. Open the panel first, or ask isContentHidden().'\n );\n }\n return (await this.locatorFor('.wr-collapse__inner')()).text();\n }\n\n /** Open the panel. An already-open one is left alone; a disabled one throws. */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n await this.toggle();\n\n if (!(await this.isOpen())) {\n throw new Error(\n `WrCollapseHarness.open(): \"${await this.getTitle()}\" did not open. A disabled panel refuses its own ` +\n 'header — the button carries `disabled`, and the component checks it again before toggling.'\n );\n }\n }\n\n /** Close the panel. An already-closed one is left alone; a disabled one throws. */\n async close(): Promise<void> {\n if (!(await this.isOpen())) return;\n await this.toggle();\n\n if (await this.isOpen()) {\n throw new Error(\n `WrCollapseHarness.close(): \"${await this.getTitle()}\" did not close. A disabled panel that was opened ` +\n 'through `[open]` cannot be closed from the page at all: the toggle refuses while `disabled` is set, ' +\n 'whichever direction the click would have gone.'\n );\n }\n }\n\n /**\n * Click the header — the panel's entire interactive surface.\n *\n * A click rather than Enter / Space, and nothing is lost by it: the header is a native\n * `<button>`, so a real keyboard activation is the BROWSER synthesising a click from\n * the key press, and jsdom synthesises none. The click is dispatched onto the header\n * itself, so it needs no coordinate and cannot miss in a test that has no layout.\n *\n * A disabled panel ignores this and stays as it was, twice over: the CDK withholds the\n * `click` from an element carrying `disabled`, and the component checks `disabled`\n * again inside its own toggle. Assert rather than assume, or reach for {@link open} /\n * {@link close}, which say so.\n */\n async toggle(): Promise<void> {\n return (await this.header()).click();\n }\n\n /**\n * Move keyboard focus to the header — the only focusable element the component itself\n * renders. Projected content can of course hold its own (which is why a closed panel is\n * `inert`), but that belongs to the consumer, not to this harness.\n */\n async focus(): Promise<void> {\n return (await this.header()).focus();\n }\n\n /** Whether the header currently holds focus. */\n async isFocused(): Promise<boolean> {\n return (await this.header()).isFocused();\n }\n}\n","/**\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 { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrCollapseGroupHarnessFilters, WrCollapseHarnessFilters } from './interfaces';\nimport { WrCollapseHarness } from './wr-collapse-harness';\n\n/**\n * Test harness for `<wr-collapse-group>` and `<wr-accordion>` — the container a set of\n * {@link WrCollapseHarness} panels sits in, matched in both of its element shapes.\n *\n * There is deliberately no `isAccordion()`, and the reason is worth knowing before a\n * spec goes looking for one: single-open behaviour is not written down anywhere on the\n * page. `<wr-accordion>` carries the marker class `wr-collapse-group--accordion`, but\n * `<wr-collapse-group [accordion]=\"true\">` enforces exactly the same rule and carries\n * nothing — a bound input reaches the component without touching the DOM, and even the\n * literal `accordion` attribute survives only because Angular leaves a static attribute\n * alone. So the class answers \"which element did the template use\", not \"does this group\n * close siblings\", and a harness reporting it would call the group form false. Assert the\n * BEHAVIOUR instead: open two panels and read {@link getOpenTitles} — the assertion that\n * would catch a broken accordion anyway.\n *\n * A collapse group is also not a composite widget. Every header is a native `<button>`\n * in the normal tab order, with no roving `tabindex` and no arrow-key navigation (the\n * APG lists both as optional for an accordion), so there is no \"active\" panel to tell\n * apart from the open one — {@link getFocusedTitle} is the only cursor there is.\n *\n * @example\n * ```ts\n * const settings = await loader.getHarness(WrCollapseGroupHarness.with({ panelTitle: 'Profile' }));\n *\n * await settings.openPanel({ title: 'Security' });\n * expect(await settings.getOpenTitles()).toEqual(['Security']);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCollapseGroupHarness extends ComponentHarness {\n /** Both shapes the container ships in: the plain group, and the always-accordion one. */\n static hostSelector = 'wr-collapse-group, wr-accordion';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrCollapseGroupHarnessFilters = {}): HarnessPredicate<WrCollapseGroupHarness> {\n return new HarnessPredicate(WrCollapseGroupHarness, options)\n .addOption('panelTitle', options.panelTitle, async (harness, title) => {\n for (const panel of await harness.getPanelTitles()) {\n if (await HarnessPredicate.stringMatches(panel, title)) return true;\n }\n return false;\n })\n .addOption('openPanelTitle', options.openPanelTitle, async (harness, title) => {\n for (const open of await harness.getOpenTitles()) {\n if (await HarnessPredicate.stringMatches(open, title)) return true;\n }\n return false;\n });\n }\n\n /**\n * The group's panels, in DOM order.\n *\n * Every `<wr-collapse>` inside the host, which includes one nested in another panel's\n * content and every panel of a nested group — the group scopes no more tightly than\n * the DOM does, and a nested accordion's children are still descendants of this one.\n */\n async getPanels(filters: WrCollapseHarnessFilters = {}): Promise<WrCollapseHarness[]> {\n return this.locatorForAll(WrCollapseHarness.with(filters))();\n }\n\n /** The header text of every panel, in DOM order. */\n async getPanelTitles(): Promise<string[]> {\n const panels = await this.getPanels();\n return Promise.all(panels.map(panel => panel.getTitle()));\n }\n\n /** The first panel matching the filters, or a failure naming the panels that exist. */\n async getPanel(filters: WrCollapseHarnessFilters): Promise<WrCollapseHarness> {\n const [panel] = await this.getPanels(filters);\n if (!panel) {\n throw new Error(\n `WrCollapseGroupHarness.getPanel(): no panel matched ${JSON.stringify(filters)}. This group holds: ` +\n `${(await this.getPanelTitles()).join(', ')}.`\n );\n }\n return panel;\n }\n\n /**\n * The panel at a 0-based position in DOM order — the other half of \"by text or by\n * index\", and the only way to address a panel whose title is not unique.\n *\n * Throws for an index past the end rather than resolving to nothing, and says how many\n * panels there are: an out-of-range index is a mistake in the spec, and a `undefined`\n * handed back would surface three lines later as a failure about something else.\n */\n async getPanelAt(index: number): Promise<WrCollapseHarness> {\n const panels = await this.getPanels();\n const panel = panels[index];\n if (!panel) {\n throw new Error(\n `WrCollapseGroupHarness.getPanelAt(): index ${index} is out of range — this group holds ` +\n `${panels.length} panel(s), so the last index is ${panels.length - 1}.`\n );\n }\n return panel;\n }\n\n /** The panels that are open, in DOM order. In accordion mode there is at most one. */\n async getOpenPanels(): Promise<WrCollapseHarness[]> {\n return this.getPanels({ open: true });\n }\n\n /**\n * The header text of every open panel, in DOM order.\n *\n * The assertion an accordion is worth pinning with: opening a second panel has to leave\n * this a list of ONE, and it reads the state from each header's `aria-expanded`, so a\n * sibling that collapsed on screen while still announcing itself open fails here.\n */\n async getOpenTitles(): Promise<string[]> {\n const open = await this.getOpenPanels();\n return Promise.all(open.map(panel => panel.getTitle()));\n }\n\n /**\n * Open the first panel matching the filters.\n *\n * In accordion mode this is also what CLOSES the siblings — the group is told by the\n * panel that opened, not by the click — so a spec that opens two panels in a row is\n * asserting the mode, and {@link getOpenTitles} is where the answer shows up. Throws\n * when nothing matched, and again when the panel refused to open (it is disabled).\n */\n async openPanel(filters: WrCollapseHarnessFilters): Promise<void> {\n await (await this.getPanel(filters)).open();\n }\n\n /** Close the first panel matching the filters. */\n async closePanel(filters: WrCollapseHarnessFilters): Promise<void> {\n await (await this.getPanel(filters)).close();\n }\n\n /**\n * Close every open panel.\n *\n * The open panels are resolved first and then closed one at a time. Every click a\n * harness makes already awaits its own change detection, so closing them concurrently\n * would work too — this reads in the order a user would click, and keeps each failure\n * attributable to the panel that caused it. A disabled panel that was opened through\n * `[open]` cannot be closed from the page and throws — the same failure\n * {@link WrCollapseHarness.close} reports, for the same reason.\n */\n async closeAll(): Promise<void> {\n for (const panel of await this.getOpenPanels()) {\n await panel.close();\n }\n }\n\n /**\n * The header text of the panel whose header holds focus, or `null` when focus is\n * elsewhere.\n *\n * Every header is its own tab stop here, so this reports where focus actually IS\n * rather than where the group would send it — there is no roving tab stop to diverge\n * from.\n */\n async getFocusedTitle(): Promise<string | null> {\n for (const panel of await this.getPanels()) {\n if (await panel.isFocused()) return panel.getTitle();\n }\n return null;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -251,6 +251,102 @@ function toHex(rgb, withAlpha = false) {
251
251
  * Use of this source code is governed by an MIT-style license that can be
252
252
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
253
253
  */
254
+ /**
255
+ * `rgb(…)` / `rgba(…)`, comma- or space-separated, with an optional `/ alpha`.
256
+ * Signs are accepted so an out-of-range channel CLAMPS the way CSS clamps it,
257
+ * rather than failing to parse and reading as black.
258
+ */
259
+ const RGB_RE = /^rgba?\(\s*(-?[\d.]+)\s*[,\s]\s*(-?[\d.]+)\s*[,\s]\s*(-?[\d.]+)\s*(?:[,/]\s*(-?[\d.]+%?)\s*)?\)$/i;
260
+ /** `hsl(…)` / `hsla(…)`, with `%` optional on saturation and lightness. */
261
+ const HSL_RE = /^hsla?\(\s*(-?[\d.]+)(?:deg)?\s*[,\s]\s*(-?[\d.]+)%?\s*[,\s]\s*(-?[\d.]+)%?\s*(?:[,/]\s*(-?[\d.]+%?)\s*)?\)$/i;
262
+ /** `0.5` and `50%` are the same alpha; anything absent is fully opaque. */
263
+ function alphaOf(raw) {
264
+ if (raw === undefined)
265
+ return 1;
266
+ const value = raw.endsWith('%') ? Number(raw.slice(0, -1)) / 100 : Number(raw);
267
+ return Number.isFinite(value) ? clamp(value, 0, 1) : 1;
268
+ }
269
+ function channel(raw) {
270
+ return clamp(Math.round(Number(raw)), 0, 255);
271
+ }
272
+ /**
273
+ * Read a colour written in any format `<wr-color-picker>` can emit — hex,
274
+ * `rgb()` / `rgba()`, `hsl()` / `hsla()` — or `null` when it is none of them.
275
+ *
276
+ * The picker emits in whichever format it was given, so a `[(value)]` binding
277
+ * hands its own output straight back on the next external write. Accepting only
278
+ * hex there (which is what it used to do) turned every non-hex value into black.
279
+ *
280
+ * `null` rather than a throw or a black fallback: the caller is often a text
281
+ * field mid-typing, where "not valid yet" is the normal state.
282
+ */
283
+ function parseColor(input) {
284
+ const value = input.trim();
285
+ if (value === '')
286
+ return null;
287
+ const hex = parseHex(value);
288
+ if (hex)
289
+ return hex;
290
+ const rgb = RGB_RE.exec(value);
291
+ if (rgb) {
292
+ return { r: channel(rgb[1]), g: channel(rgb[2]), b: channel(rgb[3]), a: alphaOf(rgb[4]) };
293
+ }
294
+ const hsl = HSL_RE.exec(value);
295
+ if (hsl) {
296
+ return hslToRgb({
297
+ // Normalised here rather than trusted downstream: `hslToRgb` happens to
298
+ // absorb one negative turn inside `hueToChannel`, which is an internal
299
+ // detail this function should not be leaning on.
300
+ h: ((Number(hsl[1]) % 360) + 360) % 360,
301
+ s: clamp(Number(hsl[2]) / 100, 0, 1),
302
+ l: clamp(Number(hsl[3]) / 100, 0, 1),
303
+ a: alphaOf(hsl[4]),
304
+ });
305
+ }
306
+ return null;
307
+ }
308
+
309
+ /**
310
+ * @license
311
+ *
312
+ * Use of this source code is governed by an MIT-style license that can be
313
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
314
+ */
315
+ /** Alpha with at most two decimals, and no trailing zeroes: `0.5`, not `0.50`. */
316
+ function alphaText(a) {
317
+ return String(Math.round(a * 100) / 100);
318
+ }
319
+ /**
320
+ * Render a colour in the format a `<wr-color-picker>` was asked for.
321
+ *
322
+ * `withAlpha` follows the picker's own `alpha` input, so each format has an
323
+ * opaque and a translucent spelling — `#3969e2` / `#3969e2ff`,
324
+ * `rgb(…)` / `rgba(…)`, `hsl(…)` / `hsla(…)` — and a picker with the alpha
325
+ * slider turned off never emits a channel the caller cannot see or edit.
326
+ */
327
+ function formatColor(rgb, format, withAlpha) {
328
+ if (format === 'hex')
329
+ return toHex(rgb, withAlpha);
330
+ if (format === 'rgba') {
331
+ const { r, g, b } = rgb;
332
+ return withAlpha ? `rgba(${r}, ${g}, ${b}, ${alphaText(rgb.a)})` : `rgb(${r}, ${g}, ${b})`;
333
+ }
334
+ const { h, s, l } = rgbToHsl(rgb);
335
+ const parts = `${Math.round(h)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%`;
336
+ return withAlpha ? `hsla(${parts}, ${alphaText(rgb.a)})` : `hsl(${parts})`;
337
+ }
338
+
339
+ /**
340
+ * @license
341
+ *
342
+ * Use of this source code is governed by an MIT-style license that can be
343
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
344
+ */
345
+ /** Whether a hex string carries its own alpha (`#rgba` or `#rrggbbaa`). */
346
+ function hasAlphaDigits(hex) {
347
+ const digits = hex.trim().replace(/^#/, '').length;
348
+ return digits === 4 || digits === 8;
349
+ }
254
350
  /**
255
351
  * Inline colour picker — HSV canvas, hue slider, optional alpha slider, HEX
256
352
  * input.
@@ -276,7 +372,10 @@ class WrColorPicker {
276
372
  alpha = input(true, { ...(ngDevMode ? { debugName: "alpha" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
277
373
  /** Disable interaction. @default false */
278
374
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
279
- /** Output format produced by the control. @default 'hex' */
375
+ /**
376
+ * Format the control writes into `value`. See {@link WrColorFormat} — all
377
+ * three are accepted on the way in whichever one is set. @default 'hex'
378
+ */
280
379
  format = input('hex', /* @ts-ignore */
281
380
  ...(ngDevMode ? [{ debugName: "format" }] : /* istanbul ignore next */ []));
282
381
  /** Preset hex colours rendered as a clickable row beneath the inputs. Empty = no row. @default [] */
@@ -300,9 +399,12 @@ class WrColorPicker {
300
399
  /** RGB view of the current colour, computed lazily. */
301
400
  rgb = computed(() => hsvToRgb({ h: this.h(), s: this.s(), v: this.v(), a: this.a() }), /* @ts-ignore */
302
401
  ...(ngDevMode ? [{ debugName: "rgb" }] : /* istanbul ignore next */ []));
303
- /** Canonical hex (used by the input field and by drag emits). */
402
+ /** Canonical hex — what the HEX field shows, whatever `format` emits. */
304
403
  hex = computed(() => toHex(this.rgb(), this.alpha()), /* @ts-ignore */
305
404
  ...(ngDevMode ? [{ debugName: "hex" }] : /* istanbul ignore next */ []));
405
+ /** The colour as the consumer asked for it; this is what `value` receives. */
406
+ formatted = computed(() => formatColor(this.rgb(), this.format(), this.alpha()), /* @ts-ignore */
407
+ ...(ngDevMode ? [{ debugName: "formatted" }] : /* istanbul ignore next */ []));
306
408
  /** SV canvas background colour (full-saturation hue). */
307
409
  hueCss = computed(() => `hsl(${this.h()}, 100%, 50%)`, /* @ts-ignore */
308
410
  ...(ngDevMode ? [{ debugName: "hueCss" }] : /* istanbul ignore next */ []));
@@ -347,17 +449,17 @@ class WrColorPicker {
347
449
  return parts.join(' ');
348
450
  }, /* @ts-ignore */
349
451
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
350
- /** Last hex we pushed into `value`; lets the sync effect ignore our own writes. */
351
- lastEmittedHex = null;
452
+ /** Last string we pushed into `value`; lets the sync effect ignore our own writes. */
453
+ lastEmitted = null;
352
454
  constructor() {
353
455
  // External writes (via `[formField]` / `[(value)]`) flow the incoming hex
354
456
  // string back into the HSV source of truth. Our own emits are skipped via
355
- // `lastEmittedHex` so dragging keeps its continuous (unquantised) values.
457
+ // `lastEmitted` so dragging keeps its continuous (unquantised) values.
356
458
  effect(() => {
357
459
  const value = this.value();
358
- if (value === this.lastEmittedHex)
460
+ if (value === this.lastEmitted)
359
461
  return;
360
- const rgb = parseHex(value) ?? { r: 0, g: 0, b: 0, a: 1 };
462
+ const rgb = parseColor(value) ?? { r: 0, g: 0, b: 0, a: 1 };
361
463
  const hsv = rgbToHsv(rgb);
362
464
  this.h.set(hsv.h);
363
465
  this.s.set(hsv.s);
@@ -370,6 +472,10 @@ class WrColorPicker {
370
472
  onPointerDown(event, surface) {
371
473
  if (this.disabled())
372
474
  return;
475
+ // Primary button, primary pointer: a right-click used to open the context
476
+ // menu AND start a drag, and a second finger used to fight the first one.
477
+ if (event.button !== 0 || !event.isPrimary)
478
+ return;
373
479
  event.preventDefault();
374
480
  const target = event.currentTarget;
375
481
  target.setPointerCapture(event.pointerId);
@@ -378,10 +484,15 @@ class WrColorPicker {
378
484
  target.releasePointerCapture(e.pointerId);
379
485
  target.removeEventListener('pointermove', update);
380
486
  target.removeEventListener('pointerup', cleanup);
487
+ target.removeEventListener('pointercancel', cleanup);
381
488
  this.touch.emit();
382
489
  };
383
490
  target.addEventListener('pointermove', update);
384
491
  target.addEventListener('pointerup', cleanup);
492
+ // `pointercancel` never sends a `pointerup` after it, so without this the
493
+ // move listener outlived the drag: a cancelled gesture left the surface
494
+ // repainting the colour under a pointer that was only hovering.
495
+ target.addEventListener('pointercancel', cleanup);
385
496
  this.updateFromEvent(event, target, surface);
386
497
  }
387
498
  updateFromEvent(event, target, surface) {
@@ -425,9 +536,9 @@ class WrColorPicker {
425
536
  this.hexInput.set(this.hex());
426
537
  }
427
538
  emitWithoutHexSync() {
428
- const hex = this.hex();
429
- this.lastEmittedHex = hex;
430
- this.value.set(hex);
539
+ const next = this.formatted();
540
+ this.lastEmitted = next;
541
+ this.value.set(next);
431
542
  }
432
543
  // Tab + channel handlers
433
544
  onTabChange(tab) {
@@ -463,9 +574,12 @@ class WrColorPicker {
463
574
  const rgb = parseHex(hex);
464
575
  if (!rgb)
465
576
  return;
466
- this.applyRgb(rgb);
577
+ // A 6-digit swatch says nothing about alpha, and `parseHex` reports the
578
+ // absence as `1` — so picking a preset used to silently snap a translucent
579
+ // colour back to fully opaque.
580
+ this.applyRgb(hasAlphaDigits(hex) ? rgb : { ...rgb, a: this.a() });
467
581
  }
468
- /** Push an RGB triple into the HSV source of truth (preserving the current alpha if the swap removes it). */
582
+ /** Push an RGB triple into the HSV source of truth. */
469
583
  applyRgb(rgb) {
470
584
  const hsv = rgbToHsv(rgb);
471
585
  this.h.set(hsv.h);
@@ -621,13 +735,19 @@ class WrColorPickerTrigger {
621
735
  }
622
736
  }
623
737
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrColorPickerTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
624
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.1", type: WrColorPickerTrigger, isStandalone: true, selector: "[wrColorPickerTrigger]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, swatches: { classPropertyName: "swatches", publicName: "swatches", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", opened: "opened", closed: "closed" }, host: { listeners: { "click": "toggle()" } }, ngImport: i0 });
738
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.1", type: WrColorPickerTrigger, isStandalone: true, selector: "[wrColorPickerTrigger]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, swatches: { classPropertyName: "swatches", publicName: "swatches", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", opened: "opened", closed: "closed" }, host: { listeners: { "click": "toggle()" }, properties: { "attr.aria-haspopup": "\"dialog\"", "attr.aria-expanded": "isOpen()" } }, ngImport: i0 });
625
739
  }
626
740
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrColorPickerTrigger, decorators: [{
627
741
  type: Directive,
628
742
  args: [{
629
743
  selector: '[wrColorPickerTrigger]',
630
- host: { '(click)': 'toggle()' },
744
+ host: {
745
+ '(click)': 'toggle()',
746
+ // The same two attributes `<wr-popover>`'s trigger carries: without them the
747
+ // button gave no hint that it opens anything, and never said it was open.
748
+ '[attr.aria-haspopup]': '"dialog"',
749
+ '[attr.aria-expanded]': 'isOpen()',
750
+ },
631
751
  }]
632
752
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], alpha: [{ type: i0.Input, args: [{ isSignal: true, alias: "alpha", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], swatches: [{ type: i0.Input, args: [{ isSignal: true, alias: "swatches", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
633
753