ngwr 14.7.0 → 14.9.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 (482) hide show
  1. package/README.md +66 -332
  2. package/alert/styles/_index.scss +10 -0
  3. package/avatar/styles/_index.scss +18 -7
  4. package/badge/styles/_index.scss +7 -0
  5. package/badge/styles/_tag.scss +7 -1
  6. package/bar-chart/styles/_index.scss +1 -0
  7. package/button/styles/_index.scss +6 -0
  8. package/calendar-heatmap/styles/_index.scss +1 -0
  9. package/cascader/styles/_rules.scss +87 -15
  10. package/checkbox/styles/_index.scss +4 -0
  11. package/command-palette/styles/_index.scss +1 -1
  12. package/date-picker/styles/_index.scss +30 -0
  13. package/density/styles/_index.scss +4 -2
  14. package/divider/styles/_index.scss +2 -0
  15. package/donut-chart/styles/_index.scss +10 -0
  16. package/dropdown/styles/_index.scss +67 -5
  17. package/editor/styles/_index.scss +441 -0
  18. package/event-calendar/styles/_index.scss +209 -35
  19. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -1
  20. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  21. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  22. package/fesm2022/ngwr-affix.mjs +3 -3
  23. package/fesm2022/ngwr-affix.mjs.map +1 -1
  24. package/fesm2022/ngwr-alert.mjs +3 -3
  25. package/fesm2022/ngwr-alert.mjs.map +1 -1
  26. package/fesm2022/ngwr-anchor.mjs +3 -3
  27. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  28. package/fesm2022/ngwr-aurora.mjs +3 -3
  29. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  30. package/fesm2022/ngwr-avatar.mjs +3 -3
  31. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  32. package/fesm2022/ngwr-back-top.mjs +3 -3
  33. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  34. package/fesm2022/ngwr-badge.mjs +28 -13
  35. package/fesm2022/ngwr-badge.mjs.map +1 -1
  36. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
  37. package/fesm2022/ngwr-bar-chart.mjs +31 -6
  38. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  39. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -1
  40. package/fesm2022/ngwr-blur-text.mjs +3 -3
  41. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  42. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -1
  43. package/fesm2022/ngwr-border-glow.mjs +3 -3
  44. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  45. package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
  46. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  47. package/fesm2022/ngwr-burger.mjs +3 -3
  48. package/fesm2022/ngwr-burger.mjs.map +1 -1
  49. package/fesm2022/ngwr-button-testing.mjs.map +1 -1
  50. package/fesm2022/ngwr-button.mjs +6 -6
  51. package/fesm2022/ngwr-button.mjs.map +1 -1
  52. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
  53. package/fesm2022/ngwr-calendar-heatmap.mjs +61 -6
  54. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  55. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  56. package/fesm2022/ngwr-calendar.mjs +3 -3
  57. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  58. package/fesm2022/ngwr-card.mjs +9 -9
  59. package/fesm2022/ngwr-card.mjs.map +1 -1
  60. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -1
  61. package/fesm2022/ngwr-carousel.mjs +6 -6
  62. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  63. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
  64. package/fesm2022/ngwr-cascader.mjs +4 -4
  65. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  66. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
  67. package/fesm2022/ngwr-checkbox.mjs +18 -8
  68. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  69. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -1
  70. package/fesm2022/ngwr-circular-text.mjs +3 -3
  71. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  72. package/fesm2022/ngwr-click-spark.mjs +3 -3
  73. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  74. package/fesm2022/ngwr-clipboard.mjs +3 -3
  75. package/fesm2022/ngwr-clipboard.mjs.map +1 -1
  76. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -1
  77. package/fesm2022/ngwr-collapse.mjs +9 -9
  78. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  79. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  80. package/fesm2022/ngwr-color-picker.mjs +6 -6
  81. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  82. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -1
  83. package/fesm2022/ngwr-command-palette.mjs +3 -3
  84. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  85. package/fesm2022/ngwr-compare.mjs +3 -3
  86. package/fesm2022/ngwr-compare.mjs.map +1 -1
  87. package/fesm2022/ngwr-confetti.mjs +3 -3
  88. package/fesm2022/ngwr-confetti.mjs.map +1 -1
  89. package/fesm2022/ngwr-config.mjs.map +1 -1
  90. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
  91. package/fesm2022/ngwr-context-menu.mjs +12 -12
  92. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  93. package/fesm2022/ngwr-cookie.mjs +3 -3
  94. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  95. package/fesm2022/ngwr-counter.mjs +6 -6
  96. package/fesm2022/ngwr-counter.mjs.map +1 -1
  97. package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
  98. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  99. package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
  100. package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -1
  101. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  102. package/fesm2022/ngwr-date-picker.mjs +721 -62
  103. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  104. package/fesm2022/ngwr-date.mjs +3 -3
  105. package/fesm2022/ngwr-date.mjs.map +1 -1
  106. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -1
  107. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  108. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  109. package/fesm2022/ngwr-density.mjs +6 -6
  110. package/fesm2022/ngwr-density.mjs.map +1 -1
  111. package/fesm2022/ngwr-descriptions.mjs +6 -6
  112. package/fesm2022/ngwr-descriptions.mjs.map +1 -1
  113. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
  114. package/fesm2022/ngwr-dialog.mjs +15 -15
  115. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  116. package/fesm2022/ngwr-directives.mjs +12 -12
  117. package/fesm2022/ngwr-directives.mjs.map +1 -1
  118. package/fesm2022/ngwr-divider.mjs +3 -3
  119. package/fesm2022/ngwr-divider.mjs.map +1 -1
  120. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
  121. package/fesm2022/ngwr-donut-chart.mjs +48 -6
  122. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  123. package/fesm2022/ngwr-drag-drop.mjs +6 -6
  124. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  125. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -1
  126. package/fesm2022/ngwr-drawer.mjs +18 -18
  127. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  128. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -1
  129. package/fesm2022/ngwr-dropdown.mjs +174 -25
  130. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  131. package/fesm2022/ngwr-editor-testing.mjs +568 -0
  132. package/fesm2022/ngwr-editor-testing.mjs.map +1 -0
  133. package/fesm2022/ngwr-editor.mjs +2601 -0
  134. package/fesm2022/ngwr-editor.mjs.map +1 -0
  135. package/fesm2022/ngwr-empty.mjs +3 -3
  136. package/fesm2022/ngwr-empty.mjs.map +1 -1
  137. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  138. package/fesm2022/ngwr-event-calendar.mjs +14 -7
  139. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  140. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
  141. package/fesm2022/ngwr-falling-text.mjs +3 -3
  142. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  143. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -1
  144. package/fesm2022/ngwr-file-upload.mjs +3 -3
  145. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  146. package/fesm2022/ngwr-form-testing.mjs.map +1 -1
  147. package/fesm2022/ngwr-form.mjs +61 -10
  148. package/fesm2022/ngwr-form.mjs.map +1 -1
  149. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -1
  150. package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
  151. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  152. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
  153. package/fesm2022/ngwr-gauge.mjs +35 -6
  154. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  155. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
  156. package/fesm2022/ngwr-glitch-text.mjs +3 -3
  157. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  158. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
  159. package/fesm2022/ngwr-gradient-text.mjs +3 -3
  160. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  161. package/fesm2022/ngwr-graph-testing.mjs.map +1 -1
  162. package/fesm2022/ngwr-graph.mjs +6 -6
  163. package/fesm2022/ngwr-graph.mjs.map +1 -1
  164. package/fesm2022/ngwr-hotkey.mjs +6 -6
  165. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  166. package/fesm2022/ngwr-i18n-ar.mjs +31 -0
  167. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  168. package/fesm2022/ngwr-i18n-cs.mjs +31 -0
  169. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  170. package/fesm2022/ngwr-i18n-de.mjs +31 -0
  171. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  172. package/fesm2022/ngwr-i18n-en.mjs +39 -0
  173. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  174. package/fesm2022/ngwr-i18n-es.mjs +31 -0
  175. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  176. package/fesm2022/ngwr-i18n-fr.mjs +31 -0
  177. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  178. package/fesm2022/ngwr-i18n-he.mjs +31 -0
  179. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  180. package/fesm2022/ngwr-i18n-hi.mjs +31 -0
  181. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  182. package/fesm2022/ngwr-i18n-id.mjs +31 -0
  183. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  184. package/fesm2022/ngwr-i18n-it.mjs +31 -0
  185. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  186. package/fesm2022/ngwr-i18n-ja.mjs +31 -0
  187. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  188. package/fesm2022/ngwr-i18n-ko.mjs +31 -0
  189. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  190. package/fesm2022/ngwr-i18n-nl.mjs +31 -0
  191. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  192. package/fesm2022/ngwr-i18n-pl.mjs +31 -0
  193. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  194. package/fesm2022/ngwr-i18n-pt.mjs +31 -0
  195. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  196. package/fesm2022/ngwr-i18n-ru.mjs +31 -0
  197. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  198. package/fesm2022/ngwr-i18n-sv.mjs +31 -0
  199. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  200. package/fesm2022/ngwr-i18n-tr.mjs +31 -0
  201. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  202. package/fesm2022/ngwr-i18n-uk.mjs +31 -0
  203. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  204. package/fesm2022/ngwr-i18n-vi.mjs +31 -0
  205. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  206. package/fesm2022/ngwr-i18n-zh-TW.mjs +31 -0
  207. package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
  208. package/fesm2022/ngwr-i18n-zh.mjs +31 -0
  209. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  210. package/fesm2022/ngwr-i18n.mjs +9 -9
  211. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  212. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  213. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  214. package/fesm2022/ngwr-icon.mjs +6 -6
  215. package/fesm2022/ngwr-icon.mjs.map +1 -1
  216. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
  217. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  218. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  219. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
  220. package/fesm2022/ngwr-input-number.mjs +15 -3
  221. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  222. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
  223. package/fesm2022/ngwr-input-otp.mjs +3 -3
  224. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  225. package/fesm2022/ngwr-input-testing.mjs.map +1 -1
  226. package/fesm2022/ngwr-input.mjs +15 -15
  227. package/fesm2022/ngwr-input.mjs.map +1 -1
  228. package/fesm2022/ngwr-keyboard.mjs +3 -3
  229. package/fesm2022/ngwr-keyboard.mjs.map +1 -1
  230. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  231. package/fesm2022/ngwr-knob.mjs +3 -3
  232. package/fesm2022/ngwr-knob.mjs.map +1 -1
  233. package/fesm2022/ngwr-layout.mjs +15 -15
  234. package/fesm2022/ngwr-layout.mjs.map +1 -1
  235. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -1
  236. package/fesm2022/ngwr-lightbox.mjs +3 -3
  237. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  238. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
  239. package/fesm2022/ngwr-line-chart.mjs +18 -7
  240. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  241. package/fesm2022/ngwr-list.mjs +6 -6
  242. package/fesm2022/ngwr-list.mjs.map +1 -1
  243. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -1
  244. package/fesm2022/ngwr-loading-bar.mjs +6 -6
  245. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  246. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -1
  247. package/fesm2022/ngwr-markdown.mjs +798 -17
  248. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  249. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -1
  250. package/fesm2022/ngwr-marquee.mjs +3 -3
  251. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  252. package/fesm2022/ngwr-media.mjs +3 -3
  253. package/fesm2022/ngwr-media.mjs.map +1 -1
  254. package/fesm2022/ngwr-mention-testing.mjs.map +1 -1
  255. package/fesm2022/ngwr-mention.mjs +6 -6
  256. package/fesm2022/ngwr-mention.mjs.map +1 -1
  257. package/fesm2022/ngwr-meta.mjs +6 -6
  258. package/fesm2022/ngwr-meta.mjs.map +1 -1
  259. package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
  260. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
  261. package/fesm2022/ngwr-meter-group.mjs +26 -6
  262. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  263. package/fesm2022/ngwr-overlay.mjs +6 -6
  264. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  265. package/fesm2022/ngwr-page-header.mjs +13 -5
  266. package/fesm2022/ngwr-page-header.mjs.map +1 -1
  267. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  268. package/fesm2022/ngwr-pagination.mjs +3 -3
  269. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  270. package/fesm2022/ngwr-pipes.mjs +21 -21
  271. package/fesm2022/ngwr-pipes.mjs.map +1 -1
  272. package/fesm2022/ngwr-platform.mjs +9 -9
  273. package/fesm2022/ngwr-platform.mjs.map +1 -1
  274. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -1
  275. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  276. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  277. package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
  278. package/fesm2022/ngwr-popover.mjs +578 -16
  279. package/fesm2022/ngwr-popover.mjs.map +1 -1
  280. package/fesm2022/ngwr-progress.mjs +3 -3
  281. package/fesm2022/ngwr-progress.mjs.map +1 -1
  282. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  283. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  284. package/fesm2022/ngwr-qr.mjs +3 -3
  285. package/fesm2022/ngwr-qr.mjs.map +1 -1
  286. package/fesm2022/ngwr-radio-testing.mjs.map +1 -1
  287. package/fesm2022/ngwr-radio.mjs +6 -6
  288. package/fesm2022/ngwr-radio.mjs.map +1 -1
  289. package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
  290. package/fesm2022/ngwr-rating.mjs +3 -3
  291. package/fesm2022/ngwr-rating.mjs.map +1 -1
  292. package/fesm2022/ngwr-result.mjs +12 -12
  293. package/fesm2022/ngwr-result.mjs.map +1 -1
  294. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -1
  295. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  296. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  297. package/fesm2022/ngwr-schema-form.mjs +3 -3
  298. package/fesm2022/ngwr-schema-form.mjs.map +1 -1
  299. package/fesm2022/ngwr-scroll.mjs +3 -3
  300. package/fesm2022/ngwr-scroll.mjs.map +1 -1
  301. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
  302. package/fesm2022/ngwr-segmented.mjs +3 -3
  303. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  304. package/fesm2022/ngwr-select-testing.mjs.map +1 -1
  305. package/fesm2022/ngwr-select.mjs +12 -12
  306. package/fesm2022/ngwr-select.mjs.map +1 -1
  307. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -1
  308. package/fesm2022/ngwr-shiny-text.mjs +6 -6
  309. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  310. package/fesm2022/ngwr-sidebar.mjs +10 -3
  311. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  312. package/fesm2022/ngwr-skeleton.mjs +3 -3
  313. package/fesm2022/ngwr-skeleton.mjs.map +1 -1
  314. package/fesm2022/ngwr-slider-testing.mjs.map +1 -1
  315. package/fesm2022/ngwr-slider.mjs +3 -3
  316. package/fesm2022/ngwr-slider.mjs.map +1 -1
  317. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -1
  318. package/fesm2022/ngwr-sparkline.mjs +84 -10
  319. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  320. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
  321. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  322. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  323. package/fesm2022/ngwr-spinner.mjs +3 -3
  324. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  325. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -1
  326. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  327. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  328. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -1
  329. package/fesm2022/ngwr-split-text.mjs +3 -3
  330. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  331. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  332. package/fesm2022/ngwr-splitter.mjs +3 -3
  333. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  334. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -1
  335. package/fesm2022/ngwr-spotlight-card.mjs +6 -6
  336. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  337. package/fesm2022/ngwr-squircle.mjs +6 -6
  338. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  339. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -1
  340. package/fesm2022/ngwr-star-border.mjs +3 -3
  341. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  342. package/fesm2022/ngwr-statistic.mjs +9 -9
  343. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  344. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
  345. package/fesm2022/ngwr-stepper.mjs +6 -6
  346. package/fesm2022/ngwr-stepper.mjs.map +1 -1
  347. package/fesm2022/ngwr-storage.mjs +3 -3
  348. package/fesm2022/ngwr-storage.mjs.map +1 -1
  349. package/fesm2022/ngwr-switch-testing.mjs.map +1 -1
  350. package/fesm2022/ngwr-switch.mjs +3 -3
  351. package/fesm2022/ngwr-switch.mjs.map +1 -1
  352. package/fesm2022/ngwr-table-testing.mjs.map +1 -1
  353. package/fesm2022/ngwr-table.mjs +18 -18
  354. package/fesm2022/ngwr-table.mjs.map +1 -1
  355. package/fesm2022/ngwr-tabs-router.mjs +3 -3
  356. package/fesm2022/ngwr-tabs-router.mjs.map +1 -1
  357. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  358. package/fesm2022/ngwr-tabs.mjs +6 -6
  359. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  360. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -1
  361. package/fesm2022/ngwr-textarea.mjs +3 -3
  362. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  363. package/fesm2022/ngwr-theme.mjs +3 -3
  364. package/fesm2022/ngwr-theme.mjs.map +1 -1
  365. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -1
  366. package/fesm2022/ngwr-tilt-card.mjs +6 -6
  367. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  368. package/fesm2022/ngwr-timeline.mjs +6 -6
  369. package/fesm2022/ngwr-timeline.mjs.map +1 -1
  370. package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
  371. package/fesm2022/ngwr-toast.mjs +9 -9
  372. package/fesm2022/ngwr-toast.mjs.map +1 -1
  373. package/fesm2022/ngwr-toolbar.mjs +10 -3
  374. package/fesm2022/ngwr-toolbar.mjs.map +1 -1
  375. package/fesm2022/ngwr-tour-testing.mjs.map +1 -1
  376. package/fesm2022/ngwr-tour.mjs +6 -6
  377. package/fesm2022/ngwr-tour.mjs.map +1 -1
  378. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -1
  379. package/fesm2022/ngwr-transfer.mjs +3 -3
  380. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  381. package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
  382. package/fesm2022/ngwr-tree.mjs +3 -3
  383. package/fesm2022/ngwr-tree.mjs.map +1 -1
  384. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
  385. package/fesm2022/ngwr-typewriter.mjs +3 -3
  386. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  387. package/fesm2022/ngwr-typography.mjs +7 -3
  388. package/fesm2022/ngwr-typography.mjs.map +1 -1
  389. package/fesm2022/ngwr-utils.mjs.map +1 -1
  390. package/fesm2022/ngwr-validators.mjs.map +1 -1
  391. package/fesm2022/ngwr-version.mjs +1 -1
  392. package/fesm2022/ngwr-version.mjs.map +1 -1
  393. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  394. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  395. package/fesm2022/ngwr-waves-testing.mjs.map +1 -1
  396. package/fesm2022/ngwr-waves.mjs +3 -3
  397. package/fesm2022/ngwr-waves.mjs.map +1 -1
  398. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  399. package/fesm2022/ngwr-window.mjs +12 -12
  400. package/fesm2022/ngwr-window.mjs.map +1 -1
  401. package/form/styles/_index.scss +16 -1
  402. package/gauge/styles/_index.scss +1 -0
  403. package/i18n/ar.json +32 -1
  404. package/i18n/cs.json +32 -1
  405. package/i18n/de.json +32 -1
  406. package/i18n/en.json +32 -1
  407. package/i18n/es.json +32 -1
  408. package/i18n/fr.json +32 -1
  409. package/i18n/he.json +32 -1
  410. package/i18n/hi.json +32 -1
  411. package/i18n/id.json +32 -1
  412. package/i18n/it.json +32 -1
  413. package/i18n/ja.json +32 -1
  414. package/i18n/ko.json +32 -1
  415. package/i18n/nl.json +32 -1
  416. package/i18n/pl.json +32 -1
  417. package/i18n/pt.json +32 -1
  418. package/i18n/ru.json +32 -1
  419. package/i18n/sv.json +32 -1
  420. package/i18n/tr.json +32 -1
  421. package/i18n/uk.json +32 -1
  422. package/i18n/vi.json +32 -1
  423. package/i18n/zh-TW.json +32 -1
  424. package/i18n/zh.json +32 -1
  425. package/input/styles/_index.scss +24 -1
  426. package/input-number/styles/_index.scss +69 -0
  427. package/input-otp/styles/_index.scss +5 -5
  428. package/line-chart/styles/_index.scss +30 -2
  429. package/llms-full.txt +14 -4
  430. package/llms.txt +188 -132
  431. package/mcp/providers.js +2 -2
  432. package/mcp/server.js +1 -1
  433. package/meter-group/styles/_index.scss +1 -0
  434. package/overlay/styles/_arrow.scss +30 -2
  435. package/package.json +43 -2
  436. package/page-header/styles/_index.scss +37 -1
  437. package/pagination/styles/_index.scss +9 -5
  438. package/popover/styles/_index.scss +73 -5
  439. package/progress/styles/_index.scss +4 -0
  440. package/radio/styles/_index.scss +4 -1
  441. package/schematics/use/symbol-map.json +1 -0
  442. package/select/styles/_index.scss +51 -12
  443. package/sidebar/styles/_index.scss +44 -2
  444. package/skeleton/styles/_index.scss +3 -0
  445. package/skills/ngwr/SKILL.md +5 -5
  446. package/skills/ngwr/references/catalog.md +2 -0
  447. package/skills/ngwr/references/setup.md +3 -3
  448. package/sparkline/styles/_index.scss +19 -0
  449. package/styles.scss +1 -0
  450. package/table/styles/_index.scss +7 -0
  451. package/textarea/styles/_index.scss +12 -3
  452. package/theme/styles/_colors.scss +39 -13
  453. package/theme/styles/_contrast.scss +9 -7
  454. package/toast/styles/_index.scss +4 -0
  455. package/toolbar/styles/_index.scss +35 -1
  456. package/tree/styles/_index.scss +46 -7
  457. package/types/ngwr-badge.d.ts +22 -7
  458. package/types/ngwr-bar-chart.d.ts +19 -1
  459. package/types/ngwr-calendar-heatmap.d.ts +29 -1
  460. package/types/ngwr-checkbox.d.ts +2 -0
  461. package/types/ngwr-config.d.ts +12 -0
  462. package/types/ngwr-date-picker.d.ts +308 -43
  463. package/types/ngwr-donut-chart.d.ts +24 -1
  464. package/types/ngwr-dropdown.d.ts +22 -2
  465. package/types/ngwr-editor-testing.d.ts +378 -0
  466. package/types/ngwr-editor.d.ts +449 -0
  467. package/types/ngwr-event-calendar.d.ts +8 -0
  468. package/types/ngwr-form.d.ts +35 -5
  469. package/types/ngwr-gauge.d.ts +20 -1
  470. package/types/ngwr-input-number.d.ts +7 -0
  471. package/types/ngwr-line-chart.d.ts +9 -1
  472. package/types/ngwr-markdown.d.ts +61 -2
  473. package/types/ngwr-meter-group-testing.d.ts +3 -2
  474. package/types/ngwr-meter-group.d.ts +14 -1
  475. package/types/ngwr-page-header.d.ts +10 -2
  476. package/types/ngwr-popover.d.ts +154 -3
  477. package/types/ngwr-sidebar.d.ts +7 -0
  478. package/types/ngwr-sparkline.d.ts +43 -1
  479. package/types/ngwr-toolbar.d.ts +7 -0
  480. package/types/ngwr-typography.d.ts +4 -0
  481. package/types/ngwr-version.d.ts +1 -1
  482. package/typography/styles/_index.scss +16 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-input-number-testing.mjs","sources":["../../../projects/lib/input-number/testing/wr-input-number-harness.ts","../../../projects/lib/input-number/testing/ngwr-input-number-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, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrInputNumberHarnessFilters } from './interfaces';\n\n/** The decimal + grouping characters `Intl` formats `locale` with. */\nfunction separatorsFor(locale?: string): { decimal: string; group: string } {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n return {\n decimal: parts.find(part => part.type === 'decimal')?.value ?? '.',\n group: parts.find(part => part.type === 'group')?.value ?? '',\n };\n}\n\n/**\n * Locale-formatted text back into a number — `null` when the text is empty or is\n * not a number at all.\n *\n * The same two-step the component parses with: drop the grouping character, then\n * normalise the decimal one to `.`. Doing it in that order matters, because in\n * half the world's locales the two are swapped and `Number('1.234,5')` is `NaN`\n * either way.\n */\nfunction parseNumber(text: string, locale?: string): number | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n\n const { decimal, group } = separatorsFor(locale);\n let normalised = group ? trimmed.split(group).join('') : trimmed;\n if (decimal !== '.') normalised = normalised.split(decimal).join('.');\n\n const parsed = Number(normalised);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\n/**\n * Test harness for `<wr-input-number>`.\n *\n * The control is a `type=\"text\"` field plus a ▲▼ stepper column — deliberately\n * NOT an `<input type=\"number\">` and NOT a `role=\"spinbutton\"`, because it renders\n * grouped, locale-formatted text that a numeric input cannot hold. Two things\n * follow, and they shape this whole harness:\n *\n * - There is no `aria-valuenow` / `aria-valuemin` / `aria-valuemax` trio, and no\n * `min` / `max` / `step` attribute, anywhere in the DOM. So the value is read\n * out of the field's TEXT, and the bounds are read off the stepper buttons'\n * disabled state — which is precisely what a user is given, and the reason\n * {@link isIncrementDisabled} is named after the button rather than after \"at\n * the maximum\".\n * - The text and the value are different things, and the gap between them is this\n * control's trap. {@link getValueText} answers the raw characters;\n * {@link getValue} answers the number they spell. Mid-type the two disagree\n * with the MODEL as well: the component clamps and rounds on every keystroke\n * but leaves the typed text alone until blur, so a field showing `999` under\n * `[max]=\"10\"` has already committed `10`. Call {@link blur} — which is also\n * what emits `touch` — to make the field re-render from the committed value,\n * and assert the host's own signal when what you mean is \"the model\".\n *\n * Stepping has two paths and both are here: {@link increment} / {@link decrement}\n * click the buttons, {@link stepUp} / {@link stepDown} press ArrowUp / ArrowDown\n * on the field. The keyboard is the accessible path, and in a unit test it is\n * also the honest one — jsdom has no layout, so nothing that needs a hit test can\n * be driven by coordinates. `{ shift: true }` is this control's page-up: it steps\n * by ten. There is deliberately no `pageUp()` / `home()` / `end()`, because the\n * component handles the two arrow keys and nothing else.\n *\n * @example\n * ```ts\n * const qty = await loader.getHarness(WrInputNumberHarness.with({ placeholder: 'Quantity' }));\n *\n * await qty.setValue(4);\n * await qty.increment();\n * expect(await qty.getValue()).toBe(5);\n *\n * await qty.stepUp({ shift: true });\n * expect(await qty.isIncrementDisabled()).toBe(true); // [max]=\"15\"\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputNumberHarness extends ComponentHarness {\n static hostSelector = 'wr-input-number';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrInputNumberHarnessFilters = {}): HarnessPredicate<WrInputNumberHarness> {\n return new HarnessPredicate(WrInputNumberHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getValueText(), text))\n .addOption('value', options.value, async (harness, value) => (await harness.parsedValue()) === value)\n .addOption('placeholder', options.placeholder, (harness, placeholder) =>\n HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder)\n )\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly field = this.locatorFor('input[wrInput]');\n\n /** The raw characters in the field — mid-typed, ungrouped or nonsense included. */\n async getValueText(): Promise<string> {\n return (await this.field()).getProperty<string>('value');\n }\n\n /**\n * The number the field is SHOWING, or `null` when it is empty.\n *\n * Empty is `null` rather than `0` because the control draws that distinction\n * too — a required-field check has to be able to tell \"nothing entered\" from \"a\n * deliberate zero\". Text that is not a number throws instead: answering `null`\n * there would report a half-typed `-` as an empty field.\n *\n * `locale` defaults to the test runtime's, which for a stock Node run is the\n * same `en-US` Angular's `LOCALE_ID` defaults to. The component formats with\n * `LOCALE_ID` and a harness cannot reach that injector, so an app that provides\n * its own must pass it: `getValue('de-DE')`. Reading `'1.234,5'` as en-US\n * silently answers `1.2345`.\n */\n async getValue(locale?: string): Promise<number | null> {\n const text = await this.getValueText();\n if (!text.trim()) return null;\n\n const parsed = parseNumber(text, locale);\n if (parsed === null) {\n throw new Error(\n `WrInputNumberHarness.getValue(): the field is showing ${JSON.stringify(text)}, which is not a number in ` +\n `${locale ? `locale \"${locale}\"` : \"the test runtime's locale\"}. Mid-type the field holds text the ` +\n 'control has not accepted — read getValueText() for the characters, or blur() to make it re-render from ' +\n 'the committed value. If the app provides a LOCALE_ID of its own, pass it: getValue(\"de-DE\").'\n );\n }\n\n return parsed;\n }\n\n /**\n * Type a number into the field, replacing whatever is there.\n *\n * Written with the LOCALE's decimal separator rather than with `String(value)`:\n * in a comma-decimal locale `'1.5'` is read as `15`, because `.` is that\n * locale's THOUSANDS separator. Grouping is deliberately not added — nothing\n * requires it, and the field regroups itself on blur anyway.\n *\n * The field is cleared first, so the value passes through `null` on the way, as\n * it does for a user who selects all and types over it. The control parses every\n * keystroke, so `min` / `max` / `decimals` are applied AS YOU TYPE while the\n * text is left exactly as typed — see {@link getValue}.\n */\n async setValue(value: number, locale?: string): Promise<void> {\n if (!Number.isFinite(value)) {\n throw new Error(\n `WrInputNumberHarness.setValue(${value}): a field can only be typed a finite number. Use clear() to empty it.`\n );\n }\n\n const { decimal } = separatorsFor(locale);\n await this.setValueText(decimal === '.' ? String(value) : String(value).replace('.', decimal));\n }\n\n /**\n * Type raw characters into the field, replacing whatever is there.\n *\n * For everything a number cannot express: a partial entry (`'-'`, `'1.'`), an\n * already-grouped string, or plain nonsense. Nothing is normalised on the way\n * in, and — as with {@link setValue} — the field is cleared first.\n */\n async setValueText(text: string): Promise<void> {\n const field = await this.field();\n await field.clear();\n // `sendKeys('')` throws — clearing was the whole request.\n if (text) await field.sendKeys(text);\n }\n\n /** Empty the field, which sets the value to `null` — not to `0`. */\n async clear(): Promise<void> {\n await (await this.field()).clear();\n }\n\n /** Whether the ▲▼ column is rendered at all — `[showSteppers]=\"false\"` drops it. */\n async hasSteppers(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-input-number__steppers')()) !== null;\n }\n\n /**\n * Click ▲ once: up by `step`, clamped to `max`.\n *\n * A button at its bound — or on a disabled / readonly control — is `disabled`,\n * and the CDK does not deliver a click to a disabled element, so this is a\n * quiet no-op there rather than a throw. Ask {@link isIncrementDisabled} rather\n * than inferring the bound from a value that did not move. It throws only when\n * there is no stepper column to click.\n */\n async increment(): Promise<void> {\n await (await this.stepButton(1, 'increment')).click();\n }\n\n /** Click ▼ once: down by `step`, clamped to `min`. See {@link increment}. */\n async decrement(): Promise<void> {\n await (await this.stepButton(-1, 'decrement')).click();\n }\n\n /**\n * Whether ▲ refuses to step.\n *\n * Deliberately not named `isAtMax()`: the button is disabled at the maximum AND\n * on a disabled or readonly control, and it cannot tell you which. That\n * conflation is the control's own — it is what the user is offered — and the\n * name says so rather than promising a bound the DOM never publishes.\n */\n async isIncrementDisabled(): Promise<boolean> {\n return (await this.stepButton(1, 'isIncrementDisabled')).getProperty<boolean>('disabled');\n }\n\n /** Whether ▼ refuses to step — at the minimum, or on a locked control. */\n async isDecrementDisabled(): Promise<boolean> {\n return (await this.stepButton(-1, 'isDecrementDisabled')).getProperty<boolean>('disabled');\n }\n\n /**\n * Press ArrowUp on the field: up by `step`, or by TEN steps with\n * `{ shift: true }` — the control's page-up, since it handles no other keys.\n */\n async stepUp(options: { shift?: boolean } = {}): Promise<void> {\n await (await this.field()).sendKeys({ shift: options.shift === true }, TestKey.UP_ARROW);\n }\n\n /** Press ArrowDown on the field. See {@link stepUp} for the `shift` multiplier. */\n async stepDown(options: { shift?: boolean } = {}): Promise<void> {\n await (await this.field()).sendKeys({ shift: options.shift === true }, TestKey.DOWN_ARROW);\n }\n\n /** The `[prefix]` label (`'$'`), or `null` when the field has none. */\n async getPrefix(): Promise<string | null> {\n const prefix = await this.locatorForOptional('.wr-input-group__affix--prefix')();\n return prefix ? prefix.text() : null;\n }\n\n /**\n * The `[suffix]` label (`'kg'`), or `null` when the field has none.\n *\n * The stepper column rides in the same suffix slot and carries the same affix\n * class, so it is excluded here by name: a bare `--suffix` query answers with\n * the buttons' (empty) text for every field that has steppers and no suffix.\n */\n async getSuffix(): Promise<string | null> {\n const suffix = await this.locatorForOptional('.wr-input-group__affix--suffix:not(.wr-input-number__steppers)')();\n return suffix ? suffix.text() : null;\n }\n\n /** The field's placeholder. */\n async getPlaceholder(): Promise<string> {\n return (await this.field()).getProperty<string>('placeholder');\n }\n\n /**\n * The field's `aria-label`, or `null` when it carries none.\n *\n * Read from the native input, not from the host: an `aria-label` on a component\n * element does not reach the control inside it, which is why the component\n * takes an `ariaLabel` input and forwards it here.\n *\n * NOT the accessible name — `null` is the usual answer and usually correct.\n * Nothing is forwarded unless `[ariaLabel]` is bound: inside a\n * `<wr-form-field>` the name comes from the label's `for`, and standalone it\n * comes from the `placeholder` (see {@link getPlaceholder}), neither of which\n * is an attribute on this element.\n */\n async getAriaLabel(): Promise<string | null> {\n return (await this.field()).getAttribute('aria-label');\n }\n\n /**\n * Whether the field refuses interaction.\n *\n * The native control's `disabled` property, not the host's\n * `wr-input-number--disabled` modifier: the modifier is the styling hook, the\n * property is what actually blocks typing, drops the field out of the tab order\n * and is reported to assistive technology.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.field()).getProperty<boolean>('disabled');\n }\n\n /** Whether the field refuses new values while staying focusable and readable. */\n async isReadonly(): Promise<boolean> {\n return (await this.field()).getProperty<boolean>('readOnly');\n }\n\n /** Move focus to the field. While it is focused the control stops reformatting the text. */\n async focus(): Promise<void> {\n return (await this.field()).focus();\n }\n\n /**\n * Blur the field, which is what commits the display: the control re-renders the\n * text from its value — grouping it and applying `decimals` — and emits `touch`\n * so a bound `[formField]` can mark itself touched.\n */\n async blur(): Promise<void> {\n return (await this.field()).blur();\n }\n\n /** Whether the field currently has focus. */\n async isFocused(): Promise<boolean> {\n return (await this.field()).isFocused();\n }\n\n /**\n * The shown value for predicates: `null` for an empty field AND for text that is\n * not a number, so a mid-typed field matches no `value` filter instead of\n * throwing the whole query.\n */\n private async parsedValue(): Promise<number | null> {\n return parseNumber(await this.getValueText());\n }\n\n /**\n * One of the two stepper buttons, BY POSITION — ▲ first, ▼ second.\n *\n * Not by `aria-label`: those strings resolve through the i18n catalog\n * (`inputNumber.increment` / `.decrement`) and an `[incrementLabel]` /\n * `[decrementLabel]` binding overrides them, so keying on them would make this\n * harness answer in whatever locale the app under test is configured for — the\n * same call `WrTimePanelHarness` makes about its own columns. Order is the\n * layout contract, and the spec pins it by asserting which way each button\n * moves the value.\n */\n private async stepButton(direction: 1 | -1, method: string): Promise<TestElement> {\n const buttons = await this.locatorForAll('.wr-input-number__step')();\n const button = buttons[direction === 1 ? 0 : 1];\n if (!button) {\n throw new Error(\n `WrInputNumberHarness.${method}(): this field has no stepper column — it was rendered with ` +\n '[showSteppers]=\"false\". Type a value with setValue(), or step it from the keyboard with stepUp() / ' +\n 'stepDown(), which work either way.'\n );\n }\n\n return button;\n }\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-input-number-testing.mjs","sources":["../../../projects/lib/input-number/testing/wr-input-number-harness.ts","../../../projects/lib/input-number/testing/ngwr-input-number-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, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrInputNumberHarnessFilters } from './interfaces';\n\n/** The decimal + grouping characters `Intl` formats `locale` with. */\nfunction separatorsFor(locale?: string): { decimal: string; group: string } {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n return {\n decimal: parts.find(part => part.type === 'decimal')?.value ?? '.',\n group: parts.find(part => part.type === 'group')?.value ?? '',\n };\n}\n\n/**\n * Locale-formatted text back into a number — `null` when the text is empty or is\n * not a number at all.\n *\n * The same two-step the component parses with: drop the grouping character, then\n * normalise the decimal one to `.`. Doing it in that order matters, because in\n * half the world's locales the two are swapped and `Number('1.234,5')` is `NaN`\n * either way.\n */\nfunction parseNumber(text: string, locale?: string): number | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n\n const { decimal, group } = separatorsFor(locale);\n let normalised = group ? trimmed.split(group).join('') : trimmed;\n if (decimal !== '.') normalised = normalised.split(decimal).join('.');\n\n const parsed = Number(normalised);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\n/**\n * Test harness for `<wr-input-number>`.\n *\n * The control is a `type=\"text\"` field plus a ▲▼ stepper column — deliberately\n * NOT an `<input type=\"number\">` and NOT a `role=\"spinbutton\"`, because it renders\n * grouped, locale-formatted text that a numeric input cannot hold. Two things\n * follow, and they shape this whole harness:\n *\n * - There is no `aria-valuenow` / `aria-valuemin` / `aria-valuemax` trio, and no\n * `min` / `max` / `step` attribute, anywhere in the DOM. So the value is read\n * out of the field's TEXT, and the bounds are read off the stepper buttons'\n * disabled state — which is precisely what a user is given, and the reason\n * {@link isIncrementDisabled} is named after the button rather than after \"at\n * the maximum\".\n * - The text and the value are different things, and the gap between them is this\n * control's trap. {@link getValueText} answers the raw characters;\n * {@link getValue} answers the number they spell. Mid-type the two disagree\n * with the MODEL as well: the component clamps and rounds on every keystroke\n * but leaves the typed text alone until blur, so a field showing `999` under\n * `[max]=\"10\"` has already committed `10`. Call {@link blur} — which is also\n * what emits `touch` — to make the field re-render from the committed value,\n * and assert the host's own signal when what you mean is \"the model\".\n *\n * Stepping has two paths and both are here: {@link increment} / {@link decrement}\n * click the buttons, {@link stepUp} / {@link stepDown} press ArrowUp / ArrowDown\n * on the field. The keyboard is the accessible path, and in a unit test it is\n * also the honest one — jsdom has no layout, so nothing that needs a hit test can\n * be driven by coordinates. `{ shift: true }` is this control's page-up: it steps\n * by ten. There is deliberately no `pageUp()` / `home()` / `end()`, because the\n * component handles the two arrow keys and nothing else.\n *\n * @example\n * ```ts\n * const qty = await loader.getHarness(WrInputNumberHarness.with({ placeholder: 'Quantity' }));\n *\n * await qty.setValue(4);\n * await qty.increment();\n * expect(await qty.getValue()).toBe(5);\n *\n * await qty.stepUp({ shift: true });\n * expect(await qty.isIncrementDisabled()).toBe(true); // [max]=\"15\"\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputNumberHarness extends ComponentHarness {\n static hostSelector = 'wr-input-number';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrInputNumberHarnessFilters = {}): HarnessPredicate<WrInputNumberHarness> {\n return new HarnessPredicate(WrInputNumberHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getValueText(), text))\n .addOption('value', options.value, async (harness, value) => (await harness.parsedValue()) === value)\n .addOption('placeholder', options.placeholder, (harness, placeholder) =>\n HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder)\n )\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly field = this.locatorFor('input[wrInput]');\n\n /** The raw characters in the field — mid-typed, ungrouped or nonsense included. */\n async getValueText(): Promise<string> {\n return (await this.field()).getProperty<string>('value');\n }\n\n /**\n * The number the field is SHOWING, or `null` when it is empty.\n *\n * Empty is `null` rather than `0` because the control draws that distinction\n * too — a required-field check has to be able to tell \"nothing entered\" from \"a\n * deliberate zero\". Text that is not a number throws instead: answering `null`\n * there would report a half-typed `-` as an empty field.\n *\n * `locale` defaults to the test runtime's, which for a stock Node run is the\n * same `en-US` Angular's `LOCALE_ID` defaults to. The component formats with\n * `LOCALE_ID` and a harness cannot reach that injector, so an app that provides\n * its own must pass it: `getValue('de-DE')`. Reading `'1.234,5'` as en-US\n * silently answers `1.2345`.\n */\n async getValue(locale?: string): Promise<number | null> {\n const text = await this.getValueText();\n if (!text.trim()) return null;\n\n const parsed = parseNumber(text, locale);\n if (parsed === null) {\n throw new Error(\n `WrInputNumberHarness.getValue(): the field is showing ${JSON.stringify(text)}, which is not a number in ` +\n `${locale ? `locale \"${locale}\"` : \"the test runtime's locale\"}. Mid-type the field holds text the ` +\n 'control has not accepted — read getValueText() for the characters, or blur() to make it re-render from ' +\n 'the committed value. If the app provides a LOCALE_ID of its own, pass it: getValue(\"de-DE\").'\n );\n }\n\n return parsed;\n }\n\n /**\n * Type a number into the field, replacing whatever is there.\n *\n * Written with the LOCALE's decimal separator rather than with `String(value)`:\n * in a comma-decimal locale `'1.5'` is read as `15`, because `.` is that\n * locale's THOUSANDS separator. Grouping is deliberately not added — nothing\n * requires it, and the field regroups itself on blur anyway.\n *\n * The field is cleared first, so the value passes through `null` on the way, as\n * it does for a user who selects all and types over it. The control parses every\n * keystroke, so `min` / `max` / `decimals` are applied AS YOU TYPE while the\n * text is left exactly as typed — see {@link getValue}.\n */\n async setValue(value: number, locale?: string): Promise<void> {\n if (!Number.isFinite(value)) {\n throw new Error(\n `WrInputNumberHarness.setValue(${value}): a field can only be typed a finite number. Use clear() to empty it.`\n );\n }\n\n const { decimal } = separatorsFor(locale);\n await this.setValueText(decimal === '.' ? String(value) : String(value).replace('.', decimal));\n }\n\n /**\n * Type raw characters into the field, replacing whatever is there.\n *\n * For everything a number cannot express: a partial entry (`'-'`, `'1.'`), an\n * already-grouped string, or plain nonsense. Nothing is normalised on the way\n * in, and — as with {@link setValue} — the field is cleared first.\n */\n async setValueText(text: string): Promise<void> {\n const field = await this.field();\n await field.clear();\n // `sendKeys('')` throws — clearing was the whole request.\n if (text) await field.sendKeys(text);\n }\n\n /** Empty the field, which sets the value to `null` — not to `0`. */\n async clear(): Promise<void> {\n await (await this.field()).clear();\n }\n\n /** Whether the ▲▼ column is rendered at all — `[showSteppers]=\"false\"` drops it. */\n async hasSteppers(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-input-number__steppers')()) !== null;\n }\n\n /**\n * Click ▲ once: up by `step`, clamped to `max`.\n *\n * A button at its bound — or on a disabled / readonly control — is `disabled`,\n * and the CDK does not deliver a click to a disabled element, so this is a\n * quiet no-op there rather than a throw. Ask {@link isIncrementDisabled} rather\n * than inferring the bound from a value that did not move. It throws only when\n * there is no stepper column to click.\n */\n async increment(): Promise<void> {\n await (await this.stepButton(1, 'increment')).click();\n }\n\n /** Click ▼ once: down by `step`, clamped to `min`. See {@link increment}. */\n async decrement(): Promise<void> {\n await (await this.stepButton(-1, 'decrement')).click();\n }\n\n /**\n * Whether ▲ refuses to step.\n *\n * Deliberately not named `isAtMax()`: the button is disabled at the maximum AND\n * on a disabled or readonly control, and it cannot tell you which. That\n * conflation is the control's own — it is what the user is offered — and the\n * name says so rather than promising a bound the DOM never publishes.\n */\n async isIncrementDisabled(): Promise<boolean> {\n return (await this.stepButton(1, 'isIncrementDisabled')).getProperty<boolean>('disabled');\n }\n\n /** Whether ▼ refuses to step — at the minimum, or on a locked control. */\n async isDecrementDisabled(): Promise<boolean> {\n return (await this.stepButton(-1, 'isDecrementDisabled')).getProperty<boolean>('disabled');\n }\n\n /**\n * Press ArrowUp on the field: up by `step`, or by TEN steps with\n * `{ shift: true }` — the control's page-up, since it handles no other keys.\n */\n async stepUp(options: { shift?: boolean } = {}): Promise<void> {\n await (await this.field()).sendKeys({ shift: options.shift === true }, TestKey.UP_ARROW);\n }\n\n /** Press ArrowDown on the field. See {@link stepUp} for the `shift` multiplier. */\n async stepDown(options: { shift?: boolean } = {}): Promise<void> {\n await (await this.field()).sendKeys({ shift: options.shift === true }, TestKey.DOWN_ARROW);\n }\n\n /** The `[prefix]` label (`'$'`), or `null` when the field has none. */\n async getPrefix(): Promise<string | null> {\n const prefix = await this.locatorForOptional('.wr-input-group__affix--prefix')();\n return prefix ? prefix.text() : null;\n }\n\n /**\n * The `[suffix]` label (`'kg'`), or `null` when the field has none.\n *\n * The stepper column rides in the same suffix slot and carries the same affix\n * class, so it is excluded here by name: a bare `--suffix` query answers with\n * the buttons' (empty) text for every field that has steppers and no suffix.\n */\n async getSuffix(): Promise<string | null> {\n const suffix = await this.locatorForOptional('.wr-input-group__affix--suffix:not(.wr-input-number__steppers)')();\n return suffix ? suffix.text() : null;\n }\n\n /** The field's placeholder. */\n async getPlaceholder(): Promise<string> {\n return (await this.field()).getProperty<string>('placeholder');\n }\n\n /**\n * The field's `aria-label`, or `null` when it carries none.\n *\n * Read from the native input, not from the host: an `aria-label` on a component\n * element does not reach the control inside it, which is why the component\n * takes an `ariaLabel` input and forwards it here.\n *\n * NOT the accessible name — `null` is the usual answer and usually correct.\n * Nothing is forwarded unless `[ariaLabel]` is bound: inside a\n * `<wr-form-field>` the name comes from the label's `for`, and standalone it\n * comes from the `placeholder` (see {@link getPlaceholder}), neither of which\n * is an attribute on this element.\n */\n async getAriaLabel(): Promise<string | null> {\n return (await this.field()).getAttribute('aria-label');\n }\n\n /**\n * Whether the field refuses interaction.\n *\n * The native control's `disabled` property, not the host's\n * `wr-input-number--disabled` modifier: the modifier is the styling hook, the\n * property is what actually blocks typing, drops the field out of the tab order\n * and is reported to assistive technology.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.field()).getProperty<boolean>('disabled');\n }\n\n /** Whether the field refuses new values while staying focusable and readable. */\n async isReadonly(): Promise<boolean> {\n return (await this.field()).getProperty<boolean>('readOnly');\n }\n\n /** Move focus to the field. While it is focused the control stops reformatting the text. */\n async focus(): Promise<void> {\n return (await this.field()).focus();\n }\n\n /**\n * Blur the field, which is what commits the display: the control re-renders the\n * text from its value — grouping it and applying `decimals` — and emits `touch`\n * so a bound `[formField]` can mark itself touched.\n */\n async blur(): Promise<void> {\n return (await this.field()).blur();\n }\n\n /** Whether the field currently has focus. */\n async isFocused(): Promise<boolean> {\n return (await this.field()).isFocused();\n }\n\n /**\n * The shown value for predicates: `null` for an empty field AND for text that is\n * not a number, so a mid-typed field matches no `value` filter instead of\n * throwing the whole query.\n */\n private async parsedValue(): Promise<number | null> {\n return parseNumber(await this.getValueText());\n }\n\n /**\n * One of the two stepper buttons, BY POSITION — ▲ first, ▼ second.\n *\n * Not by `aria-label`: those strings resolve through the i18n catalog\n * (`inputNumber.increment` / `.decrement`) and an `[incrementLabel]` /\n * `[decrementLabel]` binding overrides them, so keying on them would make this\n * harness answer in whatever locale the app under test is configured for — the\n * same call `WrTimePanelHarness` makes about its own columns. Order is the\n * layout contract, and the spec pins it by asserting which way each button\n * moves the value.\n */\n private async stepButton(direction: 1 | -1, method: string): Promise<TestElement> {\n const buttons = await this.locatorForAll('.wr-input-number__step')();\n const button = buttons[direction === 1 ? 0 : 1];\n if (!button) {\n throw new Error(\n `WrInputNumberHarness.${method}(): this field has no stepper column — it was rendered with ` +\n '[showSteppers]=\"false\". Type a value with setValue(), or step it from the keyboard with stepUp() / ' +\n 'stepDown(), which work either way.'\n );\n }\n\n return button;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -174,8 +174,20 @@ class WrInputNumber {
174
174
  return v !== null && v >= this.maxValue();
175
175
  }, /* @ts-ignore */
176
176
  ...(ngDevMode ? [{ debugName: "atMax" }] : /* istanbul ignore next */ []));
177
+ /**
178
+ * The size and pill modifiers mirror what is bound down to the chrome, because
179
+ * the stepper column belongs to neither half of it: the field carries
180
+ * `wr-input--<size>`, the column is its SIBLING, and the group carries no size
181
+ * at all. Without a modifier of its own the column drew one 10x6 chevron at
182
+ * every size and ran its dividers into a pill's rounded end.
183
+ */
177
184
  classes = computed(() => {
178
185
  const parts = ['wr-input-number'];
186
+ const size = this.resolvedSize();
187
+ if (size !== 'md')
188
+ parts.push(`wr-input-number--${size}`);
189
+ if (this.resolvedRounded())
190
+ parts.push('wr-input-number--rounded');
179
191
  if (this.disabled())
180
192
  parts.push('wr-input-number--disabled');
181
193
  return parts.join(' ');
@@ -337,10 +349,10 @@ class WrInputNumber {
337
349
  });
338
350
  return formatter.format(v);
339
351
  }
340
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrInputNumber, deps: [], target: i0.ɵɵFactoryTarget.Component });
341
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrInputNumber, isStandalone: true, selector: "wr-input-number", 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 }, decimals: { classPropertyName: "decimals", publicName: "decimals", isSignal: true, isRequired: false, transformFunction: null }, showSteppers: { classPropertyName: "showSteppers", publicName: "showSteppers", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, incrementLabel: { classPropertyName: "incrementLabel", publicName: "incrementLabel", isSignal: true, isRequired: false, transformFunction: null }, decrementLabel: { classPropertyName: "decrementLabel", publicName: "decrementLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", 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()" } }, ngImport: i0, template: "<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [size]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n", dependencies: [{ kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["size", "rounded"] }, { kind: "component", type: WrInputGroup, selector: "wr-input-group", inputs: ["rounded"] }, { kind: "directive", type: WrInputPrefix, selector: "[wrInputPrefix]" }, { kind: "directive", type: WrInputSuffix, selector: "[wrInputSuffix]" }], encapsulation: i0.ViewEncapsulation.None });
352
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrInputNumber, deps: [], target: i0.ɵɵFactoryTarget.Component });
353
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrInputNumber, isStandalone: true, selector: "wr-input-number", 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 }, decimals: { classPropertyName: "decimals", publicName: "decimals", isSignal: true, isRequired: false, transformFunction: null }, showSteppers: { classPropertyName: "showSteppers", publicName: "showSteppers", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, incrementLabel: { classPropertyName: "incrementLabel", publicName: "incrementLabel", isSignal: true, isRequired: false, transformFunction: null }, decrementLabel: { classPropertyName: "decrementLabel", publicName: "decrementLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", 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()" } }, ngImport: i0, template: "<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [size]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n", dependencies: [{ kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["size", "rounded"] }, { kind: "component", type: WrInputGroup, selector: "wr-input-group", inputs: ["rounded"] }, { kind: "directive", type: WrInputPrefix, selector: "[wrInputPrefix]" }, { kind: "directive", type: WrInputSuffix, selector: "[wrInputSuffix]" }], encapsulation: i0.ViewEncapsulation.None });
342
354
  }
343
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrInputNumber, decorators: [{
355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrInputNumber, decorators: [{
344
356
  type: Component,
345
357
  args: [{ selector: 'wr-input-number', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix], template: "<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [size]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n" }]
346
358
  }], 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 }] }], decimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "decimals", required: false }] }], showSteppers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSteppers", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], incrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementLabel", required: false }] }], decrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-input-number.mjs","sources":["../../../projects/lib/input-number/input-number.ts","../../../projects/lib/input-number/input-number.html","../../../projects/lib/input-number/ngwr-input-number.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 { type BooleanInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n LOCALE_ID,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix, type WrInputSize } from 'ngwr/input';\nimport { clamp, round } from 'ngwr/utils';\n\n/** Round to `decimals` places, avoiding common float artefacts (`1.005 → 1.01`). */\n\n/** Resolve the locale's decimal + grouping separators from `Intl.NumberFormat`. */\nfunction resolveSeparators(locale: string): { decimal: string; group: string } {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n const decimal = parts.find(p => p.type === 'decimal')?.value ?? '.';\n const group = parts.find(p => p.type === 'group')?.value ?? '';\n return { decimal, group };\n}\n\n/**\n * Numeric input with locale-aware formatting + ▲▼ stepper buttons.\n *\n * A signal-forms native control: it implements `FormValueControl<number | null>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Parses leniently while typing (allows partial input like `\"1.\"` or `\"-\"`),\n * re-formats with grouping + fixed decimals on blur.\n *\n * Locale comes from Angular's `LOCALE_ID` — input-number intentionally does\n * NOT depend on `ngwr/date` so it can be used in date-free apps.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-input-number [formField]=\"form.qty\" [min]=\"0\" [max]=\"100\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-input-number [(value)]=\"price\" [decimals]=\"2\" suffix=\"USD\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/input-number\n */\n@Component({\n selector: 'wr-input-number',\n templateUrl: './input-number.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix],\n})\nexport class WrInputNumber implements FormValueControl<number | null> {\n /** Minimum allowed value. @default -Infinity */\n readonly min = input<number | undefined>(Number.NEGATIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.NEGATIVE_INFINITY),\n });\n\n /** Maximum allowed value. @default Infinity */\n readonly max = input<number | undefined>(Number.POSITIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.POSITIVE_INFINITY),\n });\n\n private readonly minValue = computed(() => this.min() ?? Number.NEGATIVE_INFINITY);\n private readonly maxValue = computed(() => this.max() ?? Number.POSITIVE_INFINITY);\n\n /** Step used by stepper buttons + arrow keys. @default 1 */\n readonly step = input(1, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 1)) });\n\n /**\n * Fixed number of decimals shown on blur. `null` keeps the entered precision.\n *\n * Clamped to what `toFixed` accepts — 0 to 100 — because it is the one numeric\n * input on this component that had no transform, and the value goes straight\n * into `toFixed`, which THROWS a `RangeError` outside that range rather than\n * degrading. A `[decimals]=\"-1\"` bound from a config object took the whole\n * component down on blur.\n *\n * @default null\n */\n readonly decimals = input<number | null, unknown>(null, {\n transform: (v: unknown): number | null =>\n v === null || v === undefined ? null : Math.min(100, Math.max(0, Math.trunc(coerceNumberProperty(v, 0)))),\n });\n\n /** Render the ▲▼ stepper column. @default true */\n readonly showSteppers = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Control size — forwarded to the field, and shares the `--wr-control-*`\n * contract. Unset falls back to the `inputNumber.size` app default from\n * `provideWrConfig()`, then to the `input.size` one, then to `md`. @default 'md'\n */\n readonly size = input<WrInputSize | null>(null);\n\n /**\n * Pill-shaped corners. Unset falls back to the `inputNumber.rounded` app default\n * from `provideWrConfig()`, then to the `input.rounded` one;\n * `[rounded]=\"false\"` turns a configured `true` back off. @default false\n */\n readonly rounded = input<boolean | null, BooleanInput>(null, {\n // Null-preserving: the plain `coerceBooleanProperty` folds `null` into\n // `false`, which would make \"not set\" and \"set to false\" the same value —\n // and a config default nothing could ever supply.\n transform: (v: BooleanInput): boolean | null => (v == null ? null : coerceBooleanProperty(v)),\n });\n\n /**\n * `size`, then the app config, then `md` — bound to the field's `[size]`.\n *\n * The chrome this control is built from resolves the same pair for itself, so\n * the config lookup falls through to `input.*` rather than stopping at\n * `inputNumber.*`: binding a value down always wins there, and an unchained\n * lookup would therefore SUPPRESS an `input.size` an app had set — the field\n * inside a number input would be the one `[wrInput]` in the app that ignored it.\n * Chained, the specific key wins where it is set and the general one still\n * applies where it is not.\n */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.inputNumber?.size ?? c.input?.size, 'md');\n\n /** `rounded`, then the app config, then `false` — bound to the group AND the field. */\n protected readonly resolvedRounded = useConfigValue(\n this.rounded,\n c => c.inputNumber?.rounded ?? c.input?.rounded,\n false\n );\n\n /** Optional prefix label (e.g. `\"$\"`). */\n readonly prefix = input<string>('');\n\n /** Optional suffix label (e.g. `\"kg\"`). */\n readonly suffix = input<string>('');\n\n /** Placeholder shown when the input is empty. */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Unset, the field is named by whatever labels\n * it — there is deliberately no fallback here.\n *\n * It used to mirror the `placeholder`, and an `aria-label` OUTRANKS a\n * `<label>` in the accname order: inside a `<wr-form-field label=\"Quantity\">`\n * the very input that adopts the field's id announced the placeholder (\"0\")\n * instead, and a placeholder disappears as soon as the user types. Standalone\n * nothing was lost by dropping it — an input with no other name already falls\n * back to its own `placeholder` attribute, which is the name axe's `label`\n * rule accepts too.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = computed(() => {\n // Not `??`: an empty binding is no name at all, and an empty `aria-label` is\n // worse than none — it still wins the accname order, and announces nothing.\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n return null;\n });\n\n /** Increment button aria-label. Falls back to `inputNumber.increment`. */\n readonly incrementLabel = input<string | null>(null);\n\n /** Decrement button aria-label. Falls back to `inputNumber.decrement`. */\n readonly decrementLabel = input<string | null>(null);\n\n /**\n * The steppers are icon-only buttons, so these ARE their accessible names.\n * They were hard-coded English while the catalog carried the keys for them\n * unread — a Russian app announced \"Increment\".\n */\n protected readonly resolvedIncrement = useI18nText(this.incrementLabel, 'inputNumber.increment', 'Increment');\n protected readonly resolvedDecrement = useI18nText(this.decrementLabel, 'inputNumber.decrement', 'Decrement');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Read-only — values cannot be changed (steppers + typing disabled). @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Numeric value (`null` when empty). Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<number | null>(null);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n private readonly locale = inject(LOCALE_ID);\n private readonly separators = resolveSeparators(this.locale);\n\n /** Text currently in the input (may be partial / invalid mid-type). */\n protected readonly text = signal<string>('');\n\n /** Whether the input is focused. While true, the input handlers own `text`. */\n protected readonly focused = signal(false);\n\n protected readonly interactive = computed(() => !this.disabled() && !this.readonly());\n\n protected readonly atMin = computed(() => {\n const v = this.value();\n return v !== null && v <= this.minValue();\n });\n\n protected readonly atMax = computed(() => {\n const v = this.value();\n return v !== null && v >= this.maxValue();\n });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-input-number'];\n if (this.disabled()) parts.push('wr-input-number--disabled');\n return parts.join(' ');\n });\n\n /**\n * Fraction digits the stepper rounds its arithmetic to when `decimals` says\n * nothing. High enough to leave a real value alone, low enough to erase the\n * binary-float tail that repeated stepping accumulates.\n */\n private static readonly STEP_PRECISION = 10;\n\n /**\n * Above this, the anti-drift round is skipped — it would MANUFACTURE drift.\n *\n * `round(v, 10)` is `Math.round((v + EPSILON) * 1e10) / 1e10`, and the scale\n * factor is only exact while `v * 1e10` stays inside 2^53, i.e. |v| under\n * about 900,720. In practice the round-trip still recovers up to ~9.2e8 and\n * then breaks: `round(1000000001, 10)` is `1000000000.9999999`, and since\n * `format` prints up to 20 fraction digits that tail reaches the screen —\n * and compounds, because the next press starts from it. Integer stepping at\n * that magnitude is exact in IEEE754 and needed no help; the only thing this\n * rounding exists to defend is `0.1 + 0.1 + 0.1`.\n */\n private static readonly SAFE_ROUND_MAGNITUDE = 1e6;\n\n /** Steps finer than this would be rounded away by {@link STEP_PRECISION}. */\n private static readonly SAFE_ROUND_STEP = 1e-9;\n\n constructor() {\n // Keep the display `text` in sync with external writes to `value` (via\n // `[formField]` / `[(value)]`). While the user is editing, the input\n // handlers own `text`, so skip re-formatting to avoid clobbering partial\n // input (e.g. mid-typed grouping or a value that is still being clamped).\n effect(() => {\n const v = this.value();\n if (this.focused()) return;\n if (v === null || Number.isNaN(v)) {\n this.text.set('');\n return;\n }\n // SHOW WHAT THE MODEL HOLDS. It used to format `constrain(v)`, so a\n // `[max]=\"10\"` field handed 500 drew \"10\" over a model that still said\n // 500 — the display was the thing lying, not the model.\n //\n // The tempting repair is to write the clamp back so the two agree, and\n // that is worse: under `[formField]` the bounds arrive ONLY through the\n // schema's `min()` / `max()` rules (binding `[min]` next to `[formField]`\n // is an NG8022 compile error), so overwriting the value ERASES the very\n // error those rules exist to raise — and the write marks a pristine form\n // dirty, tripping unsaved-changes guards and opening every other\n // validator's message on first paint.\n //\n // So: `min` / `max` bound what this component's own edits may produce,\n // and a value written from outside is displayed as it is and left for\n // validation to judge.\n this.text.set(this.format(v));\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value;\n this.text.set(raw);\n if (!raw.trim()) {\n this.value.set(null);\n return;\n }\n const parsed = this.parse(raw);\n if (parsed === null) return; // partial input — wait for more\n const next = this.constrain(parsed);\n this.value.set(next);\n }\n\n protected onFocus(): void {\n this.focused.set(true);\n }\n\n protected onBlur(): void {\n // Flipping `focused` false lets the sync effect re-format `text` from the\n // committed `value` (e.g. `\"1.\"` → `\"1\"`, or grouping the final number).\n this.focused.set(false);\n this.touch.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;\n event.preventDefault();\n const direction = event.key === 'ArrowUp' ? 1 : -1;\n const multiplier = event.shiftKey ? 10 : 1;\n this.bump(direction * multiplier);\n }\n\n protected stepBy(direction: 1 | -1): void {\n if (!this.interactive()) return;\n this.bump(direction);\n }\n\n // Internals\n\n private bump(units: number): void {\n const stepSize = this.step() || 1;\n const base = this.value() ?? this.fallbackBase();\n // `round` before `constrain`, and only on the stepped ARITHMETIC. Binary\n // floats do not add: three presses of a `step=\"0.1\"` stepper produced\n // 0.30000000000000004, `constrain` only rounds when `decimals` is set, and\n // `format` prints up to 20 fraction digits — so the drift went into the\n // model AND onto the screen. Ten digits erases IEEE noise without\n // quantising a value the user typed (0.15000000000000002 stays 0.15, it\n // does not snap to the step grid).\n //\n // Guarded on BOTH ends, because outside them the correction is the thing\n // that corrupts: see SAFE_ROUND_MAGNITUDE, and a step finer than 1e-9 would\n // simply be rounded away to nothing.\n const raw = base + units * stepSize;\n const correctable =\n Math.abs(raw) < WrInputNumber.SAFE_ROUND_MAGNITUDE && Math.abs(stepSize) >= WrInputNumber.SAFE_ROUND_STEP;\n const next = this.constrain(correctable ? round(raw, WrInputNumber.STEP_PRECISION) : raw);\n this.value.set(next);\n this.text.set(this.format(next));\n }\n\n /** Choose a sensible starting point when bumping from `null`. */\n private fallbackBase(): number {\n if (Number.isFinite(this.minValue()) && this.minValue() > 0) return this.minValue();\n if (Number.isFinite(this.maxValue()) && this.maxValue() < 0) return this.maxValue();\n return 0;\n }\n\n private constrain(v: number): number {\n let next = clamp(v, this.minValue(), this.maxValue());\n const d = this.decimals();\n if (d !== null) next = round(next, d);\n return next;\n }\n\n /** Parse a locale-formatted string. Returns `null` for partial / not-yet-numeric input. */\n private parse(raw: string): number | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n // Strip group separators, normalise the decimal separator to `.`.\n let normalised = trimmed;\n if (this.separators.group) {\n normalised = normalised.split(this.separators.group).join('');\n }\n if (this.separators.decimal !== '.') {\n normalised = normalised.split(this.separators.decimal).join('.');\n }\n // Partial inputs we don't want to clobber while the user is still typing:\n // `-`, `-.`, `.`, `1.`, `-1.` etc.\n if (/^-?\\.?$/.test(normalised) || normalised.endsWith('.')) return null;\n const n = Number(normalised);\n return Number.isFinite(n) ? n : null;\n }\n\n /** Format a value using `Intl.NumberFormat` for the active locale. */\n private format(v: number): string {\n const d = this.decimals();\n const formatter = new Intl.NumberFormat(this.locale, {\n minimumFractionDigits: d ?? 0,\n maximumFractionDigits: d ?? 20,\n useGrouping: true,\n });\n return formatter.format(v);\n }\n}\n","<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [size]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;AAAA;;;;;AAKG;AAsBH;AAEA;AACA,SAAS,iBAAiB,CAAC,MAAc,EAAA;AACvC,IAAA,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,KAAK,IAAI,GAAG;IACnE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,KAAK,IAAI,EAAE;AAC9D,IAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AAC3B;AAEA;;;;;;;;;;;;;;;;;;;;;;;AAuBG;MAQU,aAAa,CAAA;;IAEf,GAAG,GAAG,KAAK,CAAqB,MAAM,CAAC,iBAAiB,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,KAAA,EAAA,8BAAA,EAAA,CAAA,EAC/D,SAAS,EAAE,CAAC,CAAU,KAAa,oBAAoB,CAAC,CAAC,EAAE,MAAM,CAAC,iBAAiB,CAAC,EAAA,CACpF;;IAGO,GAAG,GAAG,KAAK,CAAqB,MAAM,CAAC,iBAAiB,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,KAAA,EAAA,8BAAA,EAAA,CAAA,EAC/D,SAAS,EAAE,CAAC,CAAU,KAAa,oBAAoB,CAAC,CAAC,EAAE,MAAM,CAAC,iBAAiB,CAAC,EAAA,CACpF;AAEe,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,iBAAiB;iFAAC;AACjE,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,iBAAiB;iFAAC;;IAGzE,IAAI,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,MAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAA,CAAG;AAExG;;;;;;;;;;AAUG;IACM,QAAQ,GAAG,KAAK,CAAyB,IAAI,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,UAAA,EAAA,8BAAA,EAAA,CAAA,EACpD,SAAS,EAAE,CAAC,CAAU,KACpB,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAA,CAC3G;;IAGO,YAAY,GAAG,KAAK,CAAC,IAAI,oFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAEzE;;;;AAIG;IACM,IAAI,GAAG,KAAK,CAAqB,IAAI;6EAAC;AAE/C;;;;AAIG;IACM,OAAO,GAAG,KAAK,CAA+B,IAAI,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,SAAA,EAAA,8BAAA,EAAA,CAAA;;;;QAIzD,SAAS,EAAE,CAAC,CAAe,MAAsB,CAAC,IAAI,IAAI,GAAG,IAAI,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAAA,CAC7F;AAEF;;;;;;;;;;AAUG;IACgB,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;;IAGzF,eAAe,GAAG,cAAc,CACjD,IAAI,CAAC,OAAO,EACZ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,OAAO,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,EAC/C,KAAK,CACN;;IAGQ,MAAM,GAAG,KAAK,CAAS,EAAE;+EAAC;;IAG1B,MAAM,GAAG,KAAK,CAAS,EAAE;+EAAC;;IAG1B,WAAW,GAAG,KAAK,CAAS,EAAE;oFAAC;AAExC;;;;;;;;;;;AAWG;IACM,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;AAE5B,IAAA,iBAAiB,GAAG,QAAQ,CAAC,MAAK;;;AAGnD,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE;AACjC,QAAA,IAAI,QAAQ;AAAE,YAAA,OAAO,QAAQ;AAC7B,QAAA,OAAO,IAAI;IACb,CAAC;0FAAC;;IAGO,cAAc,GAAG,KAAK,CAAgB,IAAI;uFAAC;;IAG3C,cAAc,GAAG,KAAK,CAAgB,IAAI;uFAAC;AAEpD;;;;AAIG;IACgB,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,uBAAuB,EAAE,WAAW,CAAC;IAC1F,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,uBAAuB,EAAE,WAAW,CAAC;;IAGpG,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG7D,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG7D,KAAK,GAAG,KAAK,CAAgB,IAAI;8EAAC;;IAGlC,KAAK,GAAG,MAAM,EAAQ;AAEd,IAAA,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;AAC1B,IAAA,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;;IAGzC,IAAI,GAAG,MAAM,CAAS,EAAE;6EAAC;;IAGzB,OAAO,GAAG,MAAM,CAAC,KAAK;gFAAC;AAEvB,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;oFAAC;AAElE,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAK;AACvC,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;QACtB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE;IAC3C,CAAC;8EAAC;AAEiB,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAK;AACvC,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;QACtB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE;IAC3C,CAAC;8EAAC;AAEiB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,MAAM,KAAK,GAAG,CAAC,iBAAiB,CAAC;QACjC,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC;AAC5D,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACxB,CAAC;gFAAC;AAEF;;;;AAIG;AACK,IAAA,OAAgB,cAAc,GAAG,EAAE;AAE3C;;;;;;;;;;;AAWG;AACK,IAAA,OAAgB,oBAAoB,GAAG,GAAG;;AAG1C,IAAA,OAAgB,eAAe,GAAG,IAAI;AAE9C,IAAA,WAAA,GAAA;;;;;QAKE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;YACtB,IAAI,IAAI,CAAC,OAAO,EAAE;gBAAE;YACpB,IAAI,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;AACjC,gBAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjB;YACF;;;;;;;;;;;;;;;;AAgBA,YAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC/B,QAAA,CAAC,CAAC;IACJ;;AAIU,IAAA,OAAO,CAAC,KAAY,EAAA;AAC5B,QAAA,MAAM,GAAG,GAAI,KAAK,CAAC,MAA2B,CAAC,KAAK;AACpD,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClB,QAAA,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE;AACf,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YACpB;QACF;QACA,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAC9B,IAAI,MAAM,KAAK,IAAI;AAAE,YAAA,OAAO;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;AACnC,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;IACtB;IAEU,OAAO,GAAA;AACf,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IACxB;IAEU,MAAM,GAAA;;;AAGd,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACvB,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;IACnB;AAEU,IAAA,SAAS,CAAC,KAAoB,EAAA;AACtC,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE;QACzB,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW;YAAE;QAC1D,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS,GAAG,CAAC,GAAG,CAAC,CAAC;AAClD,QAAA,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,GAAG,EAAE,GAAG,CAAC;AAC1C,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC;IACnC;AAEU,IAAA,MAAM,CAAC,SAAiB,EAAA;AAChC,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE;AACzB,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;IACtB;;AAIQ,IAAA,IAAI,CAAC,KAAa,EAAA;QACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,YAAY,EAAE;;;;;;;;;;;;AAYhD,QAAA,MAAM,GAAG,GAAG,IAAI,GAAG,KAAK,GAAG,QAAQ;QACnC,MAAM,WAAW,GACf,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,oBAAoB,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,eAAe;QAC3G,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC;AACzF,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClC;;IAGQ,YAAY,GAAA;AAClB,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,QAAQ,EAAE;AACnF,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,QAAQ,EAAE;AACnF,QAAA,OAAO,CAAC;IACV;AAEQ,IAAA,SAAS,CAAC,CAAS,EAAA;AACzB,QAAA,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;AACrD,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE;QACzB,IAAI,CAAC,KAAK,IAAI;AAAE,YAAA,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,QAAA,OAAO,IAAI;IACb;;AAGQ,IAAA,KAAK,CAAC,GAAW,EAAA;AACvB,QAAA,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE;AAC1B,QAAA,IAAI,CAAC,OAAO;AAAE,YAAA,OAAO,IAAI;;QAEzB,IAAI,UAAU,GAAG,OAAO;AACxB,QAAA,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE;AACzB,YAAA,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D;QACA,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,KAAK,GAAG,EAAE;AACnC,YAAA,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAClE;;;AAGA,QAAA,IAAI,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;AAAE,YAAA,OAAO,IAAI;AACvE,QAAA,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;AAC5B,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI;IACtC;;AAGQ,IAAA,MAAM,CAAC,CAAS,EAAA;AACtB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE;QACzB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE;YACnD,qBAAqB,EAAE,CAAC,IAAI,CAAC;YAC7B,qBAAqB,EAAE,CAAC,IAAI,EAAE;AAC9B,YAAA,WAAW,EAAE,IAAI;AAClB,SAAA,CAAC;AACF,QAAA,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5B;uGAhUW,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAb,aAAa,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,KAAA,EAAA,aAAA,EAAA,KAAA,EAAA,OAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,WAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECpE1B,uoDAuDA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDWY,OAAO,2GAAE,YAAY,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,aAAa,EAAA,QAAA,EAAA,iBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,aAAa,EAAA,QAAA,EAAA,iBAAA,EAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAElD,aAAa,EAAA,UAAA,EAAA,CAAA;kBAPzB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,iBAEZ,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B,EAAE,SAAS,EAAE,WAAW,EAAE,EAAA,OAAA,EACvB,CAAC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,CAAC,EAAA,QAAA,EAAA,uoDAAA,EAAA;;;AElEhE;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-input-number.mjs","sources":["../../../projects/lib/input-number/input-number.ts","../../../projects/lib/input-number/input-number.html","../../../projects/lib/input-number/ngwr-input-number.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 { type BooleanInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n LOCALE_ID,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix, type WrInputSize } from 'ngwr/input';\nimport { clamp, round } from 'ngwr/utils';\n\n/** Round to `decimals` places, avoiding common float artefacts (`1.005 → 1.01`). */\n\n/** Resolve the locale's decimal + grouping separators from `Intl.NumberFormat`. */\nfunction resolveSeparators(locale: string): { decimal: string; group: string } {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n const decimal = parts.find(p => p.type === 'decimal')?.value ?? '.';\n const group = parts.find(p => p.type === 'group')?.value ?? '';\n return { decimal, group };\n}\n\n/**\n * Numeric input with locale-aware formatting + ▲▼ stepper buttons.\n *\n * A signal-forms native control: it implements `FormValueControl<number | null>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Parses leniently while typing (allows partial input like `\"1.\"` or `\"-\"`),\n * re-formats with grouping + fixed decimals on blur.\n *\n * Locale comes from Angular's `LOCALE_ID` — input-number intentionally does\n * NOT depend on `ngwr/date` so it can be used in date-free apps.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-input-number [formField]=\"form.qty\" [min]=\"0\" [max]=\"100\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-input-number [(value)]=\"price\" [decimals]=\"2\" suffix=\"USD\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/input-number\n */\n@Component({\n selector: 'wr-input-number',\n templateUrl: './input-number.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix],\n})\nexport class WrInputNumber implements FormValueControl<number | null> {\n /** Minimum allowed value. @default -Infinity */\n readonly min = input<number | undefined>(Number.NEGATIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.NEGATIVE_INFINITY),\n });\n\n /** Maximum allowed value. @default Infinity */\n readonly max = input<number | undefined>(Number.POSITIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.POSITIVE_INFINITY),\n });\n\n private readonly minValue = computed(() => this.min() ?? Number.NEGATIVE_INFINITY);\n private readonly maxValue = computed(() => this.max() ?? Number.POSITIVE_INFINITY);\n\n /** Step used by stepper buttons + arrow keys. @default 1 */\n readonly step = input(1, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 1)) });\n\n /**\n * Fixed number of decimals shown on blur. `null` keeps the entered precision.\n *\n * Clamped to what `toFixed` accepts — 0 to 100 — because it is the one numeric\n * input on this component that had no transform, and the value goes straight\n * into `toFixed`, which THROWS a `RangeError` outside that range rather than\n * degrading. A `[decimals]=\"-1\"` bound from a config object took the whole\n * component down on blur.\n *\n * @default null\n */\n readonly decimals = input<number | null, unknown>(null, {\n transform: (v: unknown): number | null =>\n v === null || v === undefined ? null : Math.min(100, Math.max(0, Math.trunc(coerceNumberProperty(v, 0)))),\n });\n\n /** Render the ▲▼ stepper column. @default true */\n readonly showSteppers = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Control size — forwarded to the field, and shares the `--wr-control-*`\n * contract. Unset falls back to the `inputNumber.size` app default from\n * `provideWrConfig()`, then to the `input.size` one, then to `md`. @default 'md'\n */\n readonly size = input<WrInputSize | null>(null);\n\n /**\n * Pill-shaped corners. Unset falls back to the `inputNumber.rounded` app default\n * from `provideWrConfig()`, then to the `input.rounded` one;\n * `[rounded]=\"false\"` turns a configured `true` back off. @default false\n */\n readonly rounded = input<boolean | null, BooleanInput>(null, {\n // Null-preserving: the plain `coerceBooleanProperty` folds `null` into\n // `false`, which would make \"not set\" and \"set to false\" the same value —\n // and a config default nothing could ever supply.\n transform: (v: BooleanInput): boolean | null => (v == null ? null : coerceBooleanProperty(v)),\n });\n\n /**\n * `size`, then the app config, then `md` — bound to the field's `[size]`.\n *\n * The chrome this control is built from resolves the same pair for itself, so\n * the config lookup falls through to `input.*` rather than stopping at\n * `inputNumber.*`: binding a value down always wins there, and an unchained\n * lookup would therefore SUPPRESS an `input.size` an app had set — the field\n * inside a number input would be the one `[wrInput]` in the app that ignored it.\n * Chained, the specific key wins where it is set and the general one still\n * applies where it is not.\n */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.inputNumber?.size ?? c.input?.size, 'md');\n\n /** `rounded`, then the app config, then `false` — bound to the group AND the field. */\n protected readonly resolvedRounded = useConfigValue(\n this.rounded,\n c => c.inputNumber?.rounded ?? c.input?.rounded,\n false\n );\n\n /** Optional prefix label (e.g. `\"$\"`). */\n readonly prefix = input<string>('');\n\n /** Optional suffix label (e.g. `\"kg\"`). */\n readonly suffix = input<string>('');\n\n /** Placeholder shown when the input is empty. */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Unset, the field is named by whatever labels\n * it — there is deliberately no fallback here.\n *\n * It used to mirror the `placeholder`, and an `aria-label` OUTRANKS a\n * `<label>` in the accname order: inside a `<wr-form-field label=\"Quantity\">`\n * the very input that adopts the field's id announced the placeholder (\"0\")\n * instead, and a placeholder disappears as soon as the user types. Standalone\n * nothing was lost by dropping it — an input with no other name already falls\n * back to its own `placeholder` attribute, which is the name axe's `label`\n * rule accepts too.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = computed(() => {\n // Not `??`: an empty binding is no name at all, and an empty `aria-label` is\n // worse than none — it still wins the accname order, and announces nothing.\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n return null;\n });\n\n /** Increment button aria-label. Falls back to `inputNumber.increment`. */\n readonly incrementLabel = input<string | null>(null);\n\n /** Decrement button aria-label. Falls back to `inputNumber.decrement`. */\n readonly decrementLabel = input<string | null>(null);\n\n /**\n * The steppers are icon-only buttons, so these ARE their accessible names.\n * They were hard-coded English while the catalog carried the keys for them\n * unread — a Russian app announced \"Increment\".\n */\n protected readonly resolvedIncrement = useI18nText(this.incrementLabel, 'inputNumber.increment', 'Increment');\n protected readonly resolvedDecrement = useI18nText(this.decrementLabel, 'inputNumber.decrement', 'Decrement');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Read-only — values cannot be changed (steppers + typing disabled). @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Numeric value (`null` when empty). Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<number | null>(null);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n private readonly locale = inject(LOCALE_ID);\n private readonly separators = resolveSeparators(this.locale);\n\n /** Text currently in the input (may be partial / invalid mid-type). */\n protected readonly text = signal<string>('');\n\n /** Whether the input is focused. While true, the input handlers own `text`. */\n protected readonly focused = signal(false);\n\n protected readonly interactive = computed(() => !this.disabled() && !this.readonly());\n\n protected readonly atMin = computed(() => {\n const v = this.value();\n return v !== null && v <= this.minValue();\n });\n\n protected readonly atMax = computed(() => {\n const v = this.value();\n return v !== null && v >= this.maxValue();\n });\n\n /**\n * The size and pill modifiers mirror what is bound down to the chrome, because\n * the stepper column belongs to neither half of it: the field carries\n * `wr-input--<size>`, the column is its SIBLING, and the group carries no size\n * at all. Without a modifier of its own the column drew one 10x6 chevron at\n * every size and ran its dividers into a pill's rounded end.\n */\n protected readonly classes = computed(() => {\n const parts = ['wr-input-number'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-input-number--${size}`);\n if (this.resolvedRounded()) parts.push('wr-input-number--rounded');\n if (this.disabled()) parts.push('wr-input-number--disabled');\n return parts.join(' ');\n });\n\n /**\n * Fraction digits the stepper rounds its arithmetic to when `decimals` says\n * nothing. High enough to leave a real value alone, low enough to erase the\n * binary-float tail that repeated stepping accumulates.\n */\n private static readonly STEP_PRECISION = 10;\n\n /**\n * Above this, the anti-drift round is skipped — it would MANUFACTURE drift.\n *\n * `round(v, 10)` is `Math.round((v + EPSILON) * 1e10) / 1e10`, and the scale\n * factor is only exact while `v * 1e10` stays inside 2^53, i.e. |v| under\n * about 900,720. In practice the round-trip still recovers up to ~9.2e8 and\n * then breaks: `round(1000000001, 10)` is `1000000000.9999999`, and since\n * `format` prints up to 20 fraction digits that tail reaches the screen —\n * and compounds, because the next press starts from it. Integer stepping at\n * that magnitude is exact in IEEE754 and needed no help; the only thing this\n * rounding exists to defend is `0.1 + 0.1 + 0.1`.\n */\n private static readonly SAFE_ROUND_MAGNITUDE = 1e6;\n\n /** Steps finer than this would be rounded away by {@link STEP_PRECISION}. */\n private static readonly SAFE_ROUND_STEP = 1e-9;\n\n constructor() {\n // Keep the display `text` in sync with external writes to `value` (via\n // `[formField]` / `[(value)]`). While the user is editing, the input\n // handlers own `text`, so skip re-formatting to avoid clobbering partial\n // input (e.g. mid-typed grouping or a value that is still being clamped).\n effect(() => {\n const v = this.value();\n if (this.focused()) return;\n if (v === null || Number.isNaN(v)) {\n this.text.set('');\n return;\n }\n // SHOW WHAT THE MODEL HOLDS. It used to format `constrain(v)`, so a\n // `[max]=\"10\"` field handed 500 drew \"10\" over a model that still said\n // 500 — the display was the thing lying, not the model.\n //\n // The tempting repair is to write the clamp back so the two agree, and\n // that is worse: under `[formField]` the bounds arrive ONLY through the\n // schema's `min()` / `max()` rules (binding `[min]` next to `[formField]`\n // is an NG8022 compile error), so overwriting the value ERASES the very\n // error those rules exist to raise — and the write marks a pristine form\n // dirty, tripping unsaved-changes guards and opening every other\n // validator's message on first paint.\n //\n // So: `min` / `max` bound what this component's own edits may produce,\n // and a value written from outside is displayed as it is and left for\n // validation to judge.\n this.text.set(this.format(v));\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value;\n this.text.set(raw);\n if (!raw.trim()) {\n this.value.set(null);\n return;\n }\n const parsed = this.parse(raw);\n if (parsed === null) return; // partial input — wait for more\n const next = this.constrain(parsed);\n this.value.set(next);\n }\n\n protected onFocus(): void {\n this.focused.set(true);\n }\n\n protected onBlur(): void {\n // Flipping `focused` false lets the sync effect re-format `text` from the\n // committed `value` (e.g. `\"1.\"` → `\"1\"`, or grouping the final number).\n this.focused.set(false);\n this.touch.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;\n event.preventDefault();\n const direction = event.key === 'ArrowUp' ? 1 : -1;\n const multiplier = event.shiftKey ? 10 : 1;\n this.bump(direction * multiplier);\n }\n\n protected stepBy(direction: 1 | -1): void {\n if (!this.interactive()) return;\n this.bump(direction);\n }\n\n // Internals\n\n private bump(units: number): void {\n const stepSize = this.step() || 1;\n const base = this.value() ?? this.fallbackBase();\n // `round` before `constrain`, and only on the stepped ARITHMETIC. Binary\n // floats do not add: three presses of a `step=\"0.1\"` stepper produced\n // 0.30000000000000004, `constrain` only rounds when `decimals` is set, and\n // `format` prints up to 20 fraction digits — so the drift went into the\n // model AND onto the screen. Ten digits erases IEEE noise without\n // quantising a value the user typed (0.15000000000000002 stays 0.15, it\n // does not snap to the step grid).\n //\n // Guarded on BOTH ends, because outside them the correction is the thing\n // that corrupts: see SAFE_ROUND_MAGNITUDE, and a step finer than 1e-9 would\n // simply be rounded away to nothing.\n const raw = base + units * stepSize;\n const correctable =\n Math.abs(raw) < WrInputNumber.SAFE_ROUND_MAGNITUDE && Math.abs(stepSize) >= WrInputNumber.SAFE_ROUND_STEP;\n const next = this.constrain(correctable ? round(raw, WrInputNumber.STEP_PRECISION) : raw);\n this.value.set(next);\n this.text.set(this.format(next));\n }\n\n /** Choose a sensible starting point when bumping from `null`. */\n private fallbackBase(): number {\n if (Number.isFinite(this.minValue()) && this.minValue() > 0) return this.minValue();\n if (Number.isFinite(this.maxValue()) && this.maxValue() < 0) return this.maxValue();\n return 0;\n }\n\n private constrain(v: number): number {\n let next = clamp(v, this.minValue(), this.maxValue());\n const d = this.decimals();\n if (d !== null) next = round(next, d);\n return next;\n }\n\n /** Parse a locale-formatted string. Returns `null` for partial / not-yet-numeric input. */\n private parse(raw: string): number | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n // Strip group separators, normalise the decimal separator to `.`.\n let normalised = trimmed;\n if (this.separators.group) {\n normalised = normalised.split(this.separators.group).join('');\n }\n if (this.separators.decimal !== '.') {\n normalised = normalised.split(this.separators.decimal).join('.');\n }\n // Partial inputs we don't want to clobber while the user is still typing:\n // `-`, `-.`, `.`, `1.`, `-1.` etc.\n if (/^-?\\.?$/.test(normalised) || normalised.endsWith('.')) return null;\n const n = Number(normalised);\n return Number.isFinite(n) ? n : null;\n }\n\n /** Format a value using `Intl.NumberFormat` for the active locale. */\n private format(v: number): string {\n const d = this.decimals();\n const formatter = new Intl.NumberFormat(this.locale, {\n minimumFractionDigits: d ?? 0,\n maximumFractionDigits: d ?? 20,\n useGrouping: true,\n });\n return formatter.format(v);\n }\n}\n","<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [size]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-input-otp-testing.mjs","sources":["../../../projects/lib/input-otp/testing/wr-input-otp-box-harness.ts","../../../projects/lib/input-otp/testing/wr-input-otp-harness.ts","../../../projects/lib/input-otp/testing/ngwr-input-otp-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 { WrInputOtpBoxHarnessFilters } from './interfaces';\n\n/**\n * Test harness for ONE box of a `<wr-input-otp>` — the `<input>` that holds a\n * single character.\n *\n * A box is more than the string it shows: it carries its own accessible name\n * (`Digit 3` on a numeric strip, `Character 3` on the others), its own\n * emptiness, its own masking, and it is the thing that either has focus or does\n * not. The root harness answers the control's questions (the assembled code,\n * which index is focused); this one answers the box's, so a spec can hold on to\n * \"the box that has focus\" instead of zipping four parallel arrays by index.\n *\n * @example\n * ```ts\n * await otp.setValue('12');\n * // Two digits in, the first box still waiting is the third.\n * const [box] = await otp.getBoxes({ empty: true });\n * expect(await box.getLabel()).toBe('Digit 3');\n * await box.type('7');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpBoxHarness extends ComponentHarness {\n /**\n * The cell class, not `wr-input-otp input`: the class is public API, so it is\n * the stable handle on a cell, and matching on it keeps the harness usable on\n * its own — `getAllHarnesses(WrInputOtpBoxHarness)` finds cells without\n * collecting every other `<input>` on the page.\n */\n static hostSelector = 'input.wr-input-otp__cell';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrInputOtpBoxHarnessFilters = {}): HarnessPredicate<WrInputOtpBoxHarness> {\n return new HarnessPredicate(WrInputOtpBoxHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('empty', options.empty, async (harness, empty) => (await harness.isEmpty()) === empty)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label));\n }\n\n /**\n * The character the box shows, or `''`.\n *\n * Read from the `value` PROPERTY. The `value` attribute is never written at all\n * — Angular binds the property, and the component rewrites that property again\n * in its own `input` handler to keep one character per box — so\n * `getAttribute('value')` answers `null` for every box, full or empty.\n */\n async getValue(): Promise<string> {\n return (await this.host()).getProperty<string>('value');\n }\n\n /** Whether the box is still waiting for a character. */\n async isEmpty(): Promise<boolean> {\n return (await this.getValue()) === '';\n }\n\n /**\n * The box's accessible name — `Digit 1`, `Digit 2`, … on a numeric strip.\n *\n * This is the whole of a box's identity for a screen-reader user: the boxes\n * carry no visible label and no position in an `aria-*` set, so the name is\n * how someone knows which one they are in.\n *\n * The word depends on `mode`, so an assertion has to follow it: the\n * `alphanumeric` and `text` modes accept letters, and the component names\n * their boxes `Character 1`, `Character 2`, … rather than call an `A` a digit.\n * Both come from the `inputOtp.digit` / `inputOtp.character` catalog entries,\n * so a localized app answers here in its own language.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** Whether the box hides what was typed, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await (await this.host()).getProperty<string>('type')) === 'password';\n }\n\n async isDisabled(): Promise<boolean> {\n return (await this.host()).getProperty<boolean>('disabled');\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /** Put the caret in this box. Throws when the control is disabled. */\n async focus(): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.host()).focus();\n }\n\n /**\n * Type one character into this box.\n *\n * Focus advances to the next box when the character is accepted — the control\n * moves it in the `input` handler, so it has already happened when this\n * resolves. A character the box's `mode` rejects leaves the box EMPTY and\n * focus where it was, which is what a browser shows too.\n */\n async type(char: string): Promise<void> {\n await this.assertEnabled('type');\n if ([...char].length !== 1) {\n throw new Error(\n `WrInputOtpBoxHarness.type('${char}'): a box holds exactly one character (maxlength=\"1\") — ` +\n 'use WrInputOtpHarness.setValue() to enter a whole code.'\n );\n }\n await (await this.host()).sendKeys(char);\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpBoxHarness.${method}(): this box is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\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, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrInputOtpSize } from 'ngwr/input-otp';\n\nimport type { WrInputOtpBoxHarnessFilters, WrInputOtpHarnessFilters } from './interfaces';\nimport { WrInputOtpBoxHarness } from './wr-input-otp-box-harness';\n\n/** Which box a keyboard move aims at, and the key that gets it there. */\nconst MOVE_KEYS = {\n previous: TestKey.LEFT_ARROW,\n next: TestKey.RIGHT_ARROW,\n first: TestKey.HOME,\n last: TestKey.END,\n} as const;\n\n/**\n * Test harness for `<wr-input-otp>` — one code spread over N single-character\n * boxes, with {@link WrInputOtpBoxHarness} for a box.\n *\n * Everything here is driven from the KEYBOARD and from `input` / `paste` events,\n * never by clicking a box. The control binds no `(click)` handler at all — what a\n * click gives you in a browser is FOCUS, and a unit-test DOM moves no focus on\n * click, so a click-driven method would look like it worked and change nothing.\n * The keyboard is the path this control is designed around anyway.\n *\n * Two things about this control regularly surprise people, and the harness\n * reports them rather than smoothing them over:\n * - {@link getValue} is the boxes JOINED, which is what the component publishes\n * for every edit made inside it. Emptying a middle box therefore SHORTENS the\n * code instead of leaving a gap in it — `1 2 _ 4 5 6` publishes `'12456'`.\n * {@link getBoxValues} is the only place that hole is visible, and\n * {@link isComplete} is the only safe answer to \"can this be submitted\".\n * - {@link paste} always fills from the FIRST box, whichever box the paste\n * landed on.\n *\n * @example\n * ```ts\n * const otp = await loader.getHarness(WrInputOtpHarness);\n *\n * await otp.setValue('482913');\n * expect(await otp.isComplete()).toBe(true);\n * expect(await otp.getValue()).toBe('482913');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpHarness extends ComponentHarness {\n static hostSelector = 'wr-input-otp';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrInputOtpHarnessFilters = {}): HarnessPredicate<WrInputOtpHarness> {\n return new HarnessPredicate(WrInputOtpHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('length', options.length, async (harness, length) => (await harness.getLength()) === length)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('complete', options.complete, async (harness, complete) => (await harness.isComplete()) === complete);\n }\n\n /** The boxes, in order. A box is a whole harness — see {@link WrInputOtpBoxHarness}. */\n async getBoxes(filters: WrInputOtpBoxHarnessFilters = {}): Promise<WrInputOtpBoxHarness[]> {\n return this.locatorForAll(WrInputOtpBoxHarness.with(filters))();\n }\n\n /** One box by index. Throws rather than returning `undefined` for an index that is not there. */\n async getBox(index: number): Promise<WrInputOtpBoxHarness> {\n const boxes = await this.getBoxes();\n const box = boxes[index];\n if (!box) {\n throw new Error(\n `WrInputOtpHarness.getBox(${index}): out of range — this control renders ${boxes.length} ` +\n `boxes, so the valid indexes are 0 to ${boxes.length - 1}.`\n );\n }\n return box;\n }\n\n /** How many boxes are rendered. `length` is clamped to `[1, 20]`, so this is the real count. */\n async getLength(): Promise<number> {\n return (await this.getBoxes()).length;\n }\n\n /** The character in each box, in order, `''` for an empty one. */\n async getBoxValues(): Promise<string[]> {\n const boxes = await this.getBoxes();\n return Promise.all(boxes.map(box => box.getValue()));\n }\n\n /**\n * The assembled code — the boxes joined. For anything typed, pasted or deleted\n * inside the control this is exactly the string it publishes through its\n * `value` model.\n *\n * An emptied middle box closes up here; {@link getBoxValues} keeps the hole.\n *\n * A write from OUTSIDE can leave the two disagreeing, and the model is the one\n * that keeps the surplus: the control splits the incoming value into boxes,\n * dropping what its `mode` rejects and anything past `length`, without writing\n * the shortened code back. Writing `'12a456'` in `numeric` mode shows `12_456`\n * and reads `'12456'` here while the bound model still holds `'12a456'`.\n */\n async getValue(): Promise<string> {\n return (await this.getBoxValues()).join('');\n }\n\n /** Whether every box holds a character — the condition the `completed` output fires on. */\n async isComplete(): Promise<boolean> {\n const values = await this.getBoxValues();\n return values.length > 0 && values.every(value => value !== '');\n }\n\n /**\n * Whether the control refuses interaction.\n *\n * Read from the boxes' `disabled` property rather than the host's\n * `wr-input-otp--disabled` class: the property is what actually stops the\n * events and the focus, the class only paints.\n */\n async isDisabled(): Promise<boolean> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isDisabled()));\n return flags.every(Boolean);\n }\n\n /** Whether the typed characters are hidden, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await this.getBox(0)).isMasked();\n }\n\n /**\n * The keyboard a mobile browser is asked for.\n *\n * `numeric` mode publishes `inputmode=\"numeric\"`; `alphanumeric` and `text`\n * BOTH publish `text`, so the two cannot be told apart from the DOM. What\n * separates them is only observable by typing: `alphanumeric` drops anything\n * that is not a letter or a digit.\n */\n async getInputMode(): Promise<'numeric' | 'text'> {\n const mode = await (await this.boxElement(0)).getAttribute('inputmode');\n return mode === 'numeric' ? 'numeric' : 'text';\n }\n\n /** The control size, from the host modifier. `md` is the default and emits no modifier. */\n async getSize(): Promise<WrInputOtpSize> {\n const host = await this.host();\n if (await host.hasClass('wr-input-otp--sm')) return 'sm';\n if (await host.hasClass('wr-input-otp--lg')) return 'lg';\n return 'md';\n }\n\n /** The character shown in an empty box. */\n async getPlaceholder(): Promise<string> {\n return (await this.boxElement(0)).getProperty<string>('placeholder');\n }\n\n /**\n * The name the group announces.\n *\n * The boxes are separate inputs; the host's `role=\"group\"` plus this label are\n * what make them one control to a screen reader.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** The index of the box with focus, or `null` when focus is outside the control. */\n async getFocusedIndex(): Promise<number | null> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isFocused()));\n const index = flags.indexOf(true);\n return index === -1 ? null : index;\n }\n\n /** Put the caret in a box — the first one unless asked otherwise. */\n async focus(index = 0): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.getBox(index)).focus();\n }\n\n /**\n * Take focus off the control, which is what makes it emit `touch` so a bound\n * field can mark itself touched. The box that HAS focus is the one blurred:\n * blurring any other one still emits `touch` (a unit-test DOM dispatches a blur\n * event even on an element that never had focus), which would report a field as\n * touched while the caret sits in it. Throws when no box has focus at all, for\n * the same reason.\n */\n async blur(): Promise<void> {\n await this.assertEnabled('blur');\n const index = await this.focusedIndexOrThrow('blur');\n await (await this.boxElement(index)).blur();\n }\n\n /**\n * Enter a whole code, the way a person does: one character per box, into\n * whichever box the control has moved focus to.\n *\n * Every box is emptied first, so a shorter code cannot leave the tail of a\n * previous one behind.\n *\n * The control is left to decide what it accepts: a character its `mode`\n * rejects lands nowhere and focus stays put, so the next character overwrites\n * the same box — which is exactly what a browser produces. Read {@link getValue}\n * back rather than assuming the argument arrived.\n */\n async setValue(code: string): Promise<void> {\n await this.assertEnabled('setValue');\n\n const chars = [...code];\n const length = await this.getLength();\n if (chars.length > length) {\n throw new Error(\n `WrInputOtpHarness.setValue('${code}'): ${chars.length} characters for ${length} boxes — ` +\n 'the extra ones would pile into the last box. Trim the code, or raise `length`.'\n );\n }\n\n await this.clear();\n await this.typeFrom(0, chars);\n }\n\n /**\n * Type into the control from where it already is — the focused box, or the\n * first box when focus is elsewhere. Unlike {@link setValue} nothing is cleared,\n * so this appends the way a half-finished code is finished.\n */\n async type(chars: string): Promise<void> {\n await this.assertEnabled('type');\n await this.typeFrom((await this.getFocusedIndex()) ?? 0, [...chars]);\n }\n\n /**\n * Paste a code in.\n *\n * The event goes to the focused box (the first one when focus is elsewhere),\n * because that is where a browser delivers it — but the control fills from the\n * FIRST box regardless, replacing everything, dropping characters its `mode`\n * rejects and trimming what is left to `length`. A paste with nothing usable in\n * it is ignored outright.\n *\n * jsdom implements neither `ClipboardEvent` nor `DataTransfer`, so the payload\n * is attached to a plain event; `getData('text')` is what the control asks for,\n * aliased to `text/plain` the way a browser does.\n */\n async paste(text: string): Promise<void> {\n await this.assertEnabled('paste');\n const target = await this.boxElement((await this.getFocusedIndex()) ?? 0);\n await target.dispatchEvent('paste', {\n clipboardData: { getData: (type: string) => (type === 'text' || type === 'text/plain' ? text : '') },\n });\n }\n\n /**\n * Press Backspace in the focused box.\n *\n * The control only handles the key itself when the box is ALREADY empty — then\n * it clears the box behind and steps focus back, which is what makes fixing a\n * typo one keystroke. On a filled box it defers to the browser's own editing,\n * and jsdom performs none, so the harness supplies that half: the character is\n * removed and an `input` event is dispatched, exactly as a real delete would.\n * Focus stays where it is, and the emptied box becomes a hole in\n * {@link getBoxValues} while {@link getValue} closes up.\n */\n async backspace(): Promise<void> {\n await this.assertEnabled('backspace');\n const index = await this.focusedIndexOrThrow('backspace');\n const box = await this.boxElement(index);\n const filled = (await box.getProperty<string>('value')) !== '';\n\n await box.sendKeys(TestKey.BACKSPACE);\n if (filled) await box.clear();\n }\n\n /**\n * Move focus with the arrow keys, `Home` or `End` — the control's own\n * navigation, and the only one available in a unit test, where nothing has a\n * position to click.\n *\n * Throws when no box has focus: the keys are handled by the box that receives\n * them, so there is no move to make.\n */\n async moveFocus(to: 'previous' | 'next' | 'first' | 'last'): Promise<void> {\n await this.assertEnabled('moveFocus');\n const index = await this.focusedIndexOrThrow('moveFocus');\n await (await this.boxElement(index)).sendKeys(MOVE_KEYS[to]);\n }\n\n /**\n * Empty every box and leave focus on the first, ready to be typed into.\n *\n * Each box is cleared through an `input` event rather than by writing the\n * model, so the control publishes the change the same way it would for a user.\n */\n async clear(): Promise<void> {\n await this.assertEnabled('clear');\n for (const box of await this.getBoxes()) {\n // An already-empty box is skipped: clearing it would re-publish the same\n // code, and drag focus through the control on the way.\n if (!(await box.isEmpty())) await (await box.host()).clear();\n }\n await (await this.boxElement(0)).focus();\n }\n\n /** Type `chars` starting at `index`, following the control's focus as it advances. */\n private async typeFrom(index: number, chars: readonly string[]): Promise<void> {\n let cursor = index;\n for (const char of chars) {\n await (await this.getBox(cursor)).type(char);\n // The control advances focus itself, and only for a character it accepted.\n // Following it — rather than incrementing — is what keeps a rejected\n // character from silently shifting the rest of the code one box along.\n cursor = (await this.getFocusedIndex()) ?? cursor;\n }\n }\n\n private async boxElement(index: number): Promise<TestElement> {\n return (await this.getBox(index)).host();\n }\n\n private async focusedIndexOrThrow(method: string): Promise<number> {\n const index = await this.getFocusedIndex();\n if (index === null) {\n throw new Error(\n `WrInputOtpHarness.${method}(): no box has focus, and this acts on the focused box. ` +\n 'Call focus() (or type into the control) first.'\n );\n }\n return index;\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpHarness.${method}(): the control is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\n }\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-input-otp-testing.mjs","sources":["../../../projects/lib/input-otp/testing/wr-input-otp-box-harness.ts","../../../projects/lib/input-otp/testing/wr-input-otp-harness.ts","../../../projects/lib/input-otp/testing/ngwr-input-otp-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 { WrInputOtpBoxHarnessFilters } from './interfaces';\n\n/**\n * Test harness for ONE box of a `<wr-input-otp>` — the `<input>` that holds a\n * single character.\n *\n * A box is more than the string it shows: it carries its own accessible name\n * (`Digit 3` on a numeric strip, `Character 3` on the others), its own\n * emptiness, its own masking, and it is the thing that either has focus or does\n * not. The root harness answers the control's questions (the assembled code,\n * which index is focused); this one answers the box's, so a spec can hold on to\n * \"the box that has focus\" instead of zipping four parallel arrays by index.\n *\n * @example\n * ```ts\n * await otp.setValue('12');\n * // Two digits in, the first box still waiting is the third.\n * const [box] = await otp.getBoxes({ empty: true });\n * expect(await box.getLabel()).toBe('Digit 3');\n * await box.type('7');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpBoxHarness extends ComponentHarness {\n /**\n * The cell class, not `wr-input-otp input`: the class is public API, so it is\n * the stable handle on a cell, and matching on it keeps the harness usable on\n * its own — `getAllHarnesses(WrInputOtpBoxHarness)` finds cells without\n * collecting every other `<input>` on the page.\n */\n static hostSelector = 'input.wr-input-otp__cell';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrInputOtpBoxHarnessFilters = {}): HarnessPredicate<WrInputOtpBoxHarness> {\n return new HarnessPredicate(WrInputOtpBoxHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('empty', options.empty, async (harness, empty) => (await harness.isEmpty()) === empty)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label));\n }\n\n /**\n * The character the box shows, or `''`.\n *\n * Read from the `value` PROPERTY. The `value` attribute is never written at all\n * — Angular binds the property, and the component rewrites that property again\n * in its own `input` handler to keep one character per box — so\n * `getAttribute('value')` answers `null` for every box, full or empty.\n */\n async getValue(): Promise<string> {\n return (await this.host()).getProperty<string>('value');\n }\n\n /** Whether the box is still waiting for a character. */\n async isEmpty(): Promise<boolean> {\n return (await this.getValue()) === '';\n }\n\n /**\n * The box's accessible name — `Digit 1`, `Digit 2`, … on a numeric strip.\n *\n * This is the whole of a box's identity for a screen-reader user: the boxes\n * carry no visible label and no position in an `aria-*` set, so the name is\n * how someone knows which one they are in.\n *\n * The word depends on `mode`, so an assertion has to follow it: the\n * `alphanumeric` and `text` modes accept letters, and the component names\n * their boxes `Character 1`, `Character 2`, … rather than call an `A` a digit.\n * Both come from the `inputOtp.digit` / `inputOtp.character` catalog entries,\n * so a localized app answers here in its own language.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** Whether the box hides what was typed, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await (await this.host()).getProperty<string>('type')) === 'password';\n }\n\n async isDisabled(): Promise<boolean> {\n return (await this.host()).getProperty<boolean>('disabled');\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /** Put the caret in this box. Throws when the control is disabled. */\n async focus(): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.host()).focus();\n }\n\n /**\n * Type one character into this box.\n *\n * Focus advances to the next box when the character is accepted — the control\n * moves it in the `input` handler, so it has already happened when this\n * resolves. A character the box's `mode` rejects leaves the box EMPTY and\n * focus where it was, which is what a browser shows too.\n */\n async type(char: string): Promise<void> {\n await this.assertEnabled('type');\n if ([...char].length !== 1) {\n throw new Error(\n `WrInputOtpBoxHarness.type('${char}'): a box holds exactly one character (maxlength=\"1\") — ` +\n 'use WrInputOtpHarness.setValue() to enter a whole code.'\n );\n }\n await (await this.host()).sendKeys(char);\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpBoxHarness.${method}(): this box is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\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, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrInputOtpSize } from 'ngwr/input-otp';\n\nimport type { WrInputOtpBoxHarnessFilters, WrInputOtpHarnessFilters } from './interfaces';\nimport { WrInputOtpBoxHarness } from './wr-input-otp-box-harness';\n\n/** Which box a keyboard move aims at, and the key that gets it there. */\nconst MOVE_KEYS = {\n previous: TestKey.LEFT_ARROW,\n next: TestKey.RIGHT_ARROW,\n first: TestKey.HOME,\n last: TestKey.END,\n} as const;\n\n/**\n * Test harness for `<wr-input-otp>` — one code spread over N single-character\n * boxes, with {@link WrInputOtpBoxHarness} for a box.\n *\n * Everything here is driven from the KEYBOARD and from `input` / `paste` events,\n * never by clicking a box. The control binds no `(click)` handler at all — what a\n * click gives you in a browser is FOCUS, and a unit-test DOM moves no focus on\n * click, so a click-driven method would look like it worked and change nothing.\n * The keyboard is the path this control is designed around anyway.\n *\n * Two things about this control regularly surprise people, and the harness\n * reports them rather than smoothing them over:\n * - {@link getValue} is the boxes JOINED, which is what the component publishes\n * for every edit made inside it. Emptying a middle box therefore SHORTENS the\n * code instead of leaving a gap in it — `1 2 _ 4 5 6` publishes `'12456'`.\n * {@link getBoxValues} is the only place that hole is visible, and\n * {@link isComplete} is the only safe answer to \"can this be submitted\".\n * - {@link paste} always fills from the FIRST box, whichever box the paste\n * landed on.\n *\n * @example\n * ```ts\n * const otp = await loader.getHarness(WrInputOtpHarness);\n *\n * await otp.setValue('482913');\n * expect(await otp.isComplete()).toBe(true);\n * expect(await otp.getValue()).toBe('482913');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpHarness extends ComponentHarness {\n static hostSelector = 'wr-input-otp';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrInputOtpHarnessFilters = {}): HarnessPredicate<WrInputOtpHarness> {\n return new HarnessPredicate(WrInputOtpHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('length', options.length, async (harness, length) => (await harness.getLength()) === length)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('complete', options.complete, async (harness, complete) => (await harness.isComplete()) === complete);\n }\n\n /** The boxes, in order. A box is a whole harness — see {@link WrInputOtpBoxHarness}. */\n async getBoxes(filters: WrInputOtpBoxHarnessFilters = {}): Promise<WrInputOtpBoxHarness[]> {\n return this.locatorForAll(WrInputOtpBoxHarness.with(filters))();\n }\n\n /** One box by index. Throws rather than returning `undefined` for an index that is not there. */\n async getBox(index: number): Promise<WrInputOtpBoxHarness> {\n const boxes = await this.getBoxes();\n const box = boxes[index];\n if (!box) {\n throw new Error(\n `WrInputOtpHarness.getBox(${index}): out of range — this control renders ${boxes.length} ` +\n `boxes, so the valid indexes are 0 to ${boxes.length - 1}.`\n );\n }\n return box;\n }\n\n /** How many boxes are rendered. `length` is clamped to `[1, 20]`, so this is the real count. */\n async getLength(): Promise<number> {\n return (await this.getBoxes()).length;\n }\n\n /** The character in each box, in order, `''` for an empty one. */\n async getBoxValues(): Promise<string[]> {\n const boxes = await this.getBoxes();\n return Promise.all(boxes.map(box => box.getValue()));\n }\n\n /**\n * The assembled code — the boxes joined. For anything typed, pasted or deleted\n * inside the control this is exactly the string it publishes through its\n * `value` model.\n *\n * An emptied middle box closes up here; {@link getBoxValues} keeps the hole.\n *\n * A write from OUTSIDE can leave the two disagreeing, and the model is the one\n * that keeps the surplus: the control splits the incoming value into boxes,\n * dropping what its `mode` rejects and anything past `length`, without writing\n * the shortened code back. Writing `'12a456'` in `numeric` mode shows `12_456`\n * and reads `'12456'` here while the bound model still holds `'12a456'`.\n */\n async getValue(): Promise<string> {\n return (await this.getBoxValues()).join('');\n }\n\n /** Whether every box holds a character — the condition the `completed` output fires on. */\n async isComplete(): Promise<boolean> {\n const values = await this.getBoxValues();\n return values.length > 0 && values.every(value => value !== '');\n }\n\n /**\n * Whether the control refuses interaction.\n *\n * Read from the boxes' `disabled` property rather than the host's\n * `wr-input-otp--disabled` class: the property is what actually stops the\n * events and the focus, the class only paints.\n */\n async isDisabled(): Promise<boolean> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isDisabled()));\n return flags.every(Boolean);\n }\n\n /** Whether the typed characters are hidden, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await this.getBox(0)).isMasked();\n }\n\n /**\n * The keyboard a mobile browser is asked for.\n *\n * `numeric` mode publishes `inputmode=\"numeric\"`; `alphanumeric` and `text`\n * BOTH publish `text`, so the two cannot be told apart from the DOM. What\n * separates them is only observable by typing: `alphanumeric` drops anything\n * that is not a letter or a digit.\n */\n async getInputMode(): Promise<'numeric' | 'text'> {\n const mode = await (await this.boxElement(0)).getAttribute('inputmode');\n return mode === 'numeric' ? 'numeric' : 'text';\n }\n\n /** The control size, from the host modifier. `md` is the default and emits no modifier. */\n async getSize(): Promise<WrInputOtpSize> {\n const host = await this.host();\n if (await host.hasClass('wr-input-otp--sm')) return 'sm';\n if (await host.hasClass('wr-input-otp--lg')) return 'lg';\n return 'md';\n }\n\n /** The character shown in an empty box. */\n async getPlaceholder(): Promise<string> {\n return (await this.boxElement(0)).getProperty<string>('placeholder');\n }\n\n /**\n * The name the group announces.\n *\n * The boxes are separate inputs; the host's `role=\"group\"` plus this label are\n * what make them one control to a screen reader.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** The index of the box with focus, or `null` when focus is outside the control. */\n async getFocusedIndex(): Promise<number | null> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isFocused()));\n const index = flags.indexOf(true);\n return index === -1 ? null : index;\n }\n\n /** Put the caret in a box — the first one unless asked otherwise. */\n async focus(index = 0): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.getBox(index)).focus();\n }\n\n /**\n * Take focus off the control, which is what makes it emit `touch` so a bound\n * field can mark itself touched. The box that HAS focus is the one blurred:\n * blurring any other one still emits `touch` (a unit-test DOM dispatches a blur\n * event even on an element that never had focus), which would report a field as\n * touched while the caret sits in it. Throws when no box has focus at all, for\n * the same reason.\n */\n async blur(): Promise<void> {\n await this.assertEnabled('blur');\n const index = await this.focusedIndexOrThrow('blur');\n await (await this.boxElement(index)).blur();\n }\n\n /**\n * Enter a whole code, the way a person does: one character per box, into\n * whichever box the control has moved focus to.\n *\n * Every box is emptied first, so a shorter code cannot leave the tail of a\n * previous one behind.\n *\n * The control is left to decide what it accepts: a character its `mode`\n * rejects lands nowhere and focus stays put, so the next character overwrites\n * the same box — which is exactly what a browser produces. Read {@link getValue}\n * back rather than assuming the argument arrived.\n */\n async setValue(code: string): Promise<void> {\n await this.assertEnabled('setValue');\n\n const chars = [...code];\n const length = await this.getLength();\n if (chars.length > length) {\n throw new Error(\n `WrInputOtpHarness.setValue('${code}'): ${chars.length} characters for ${length} boxes — ` +\n 'the extra ones would pile into the last box. Trim the code, or raise `length`.'\n );\n }\n\n await this.clear();\n await this.typeFrom(0, chars);\n }\n\n /**\n * Type into the control from where it already is — the focused box, or the\n * first box when focus is elsewhere. Unlike {@link setValue} nothing is cleared,\n * so this appends the way a half-finished code is finished.\n */\n async type(chars: string): Promise<void> {\n await this.assertEnabled('type');\n await this.typeFrom((await this.getFocusedIndex()) ?? 0, [...chars]);\n }\n\n /**\n * Paste a code in.\n *\n * The event goes to the focused box (the first one when focus is elsewhere),\n * because that is where a browser delivers it — but the control fills from the\n * FIRST box regardless, replacing everything, dropping characters its `mode`\n * rejects and trimming what is left to `length`. A paste with nothing usable in\n * it is ignored outright.\n *\n * jsdom implements neither `ClipboardEvent` nor `DataTransfer`, so the payload\n * is attached to a plain event; `getData('text')` is what the control asks for,\n * aliased to `text/plain` the way a browser does.\n */\n async paste(text: string): Promise<void> {\n await this.assertEnabled('paste');\n const target = await this.boxElement((await this.getFocusedIndex()) ?? 0);\n await target.dispatchEvent('paste', {\n clipboardData: { getData: (type: string) => (type === 'text' || type === 'text/plain' ? text : '') },\n });\n }\n\n /**\n * Press Backspace in the focused box.\n *\n * The control only handles the key itself when the box is ALREADY empty — then\n * it clears the box behind and steps focus back, which is what makes fixing a\n * typo one keystroke. On a filled box it defers to the browser's own editing,\n * and jsdom performs none, so the harness supplies that half: the character is\n * removed and an `input` event is dispatched, exactly as a real delete would.\n * Focus stays where it is, and the emptied box becomes a hole in\n * {@link getBoxValues} while {@link getValue} closes up.\n */\n async backspace(): Promise<void> {\n await this.assertEnabled('backspace');\n const index = await this.focusedIndexOrThrow('backspace');\n const box = await this.boxElement(index);\n const filled = (await box.getProperty<string>('value')) !== '';\n\n await box.sendKeys(TestKey.BACKSPACE);\n if (filled) await box.clear();\n }\n\n /**\n * Move focus with the arrow keys, `Home` or `End` — the control's own\n * navigation, and the only one available in a unit test, where nothing has a\n * position to click.\n *\n * Throws when no box has focus: the keys are handled by the box that receives\n * them, so there is no move to make.\n */\n async moveFocus(to: 'previous' | 'next' | 'first' | 'last'): Promise<void> {\n await this.assertEnabled('moveFocus');\n const index = await this.focusedIndexOrThrow('moveFocus');\n await (await this.boxElement(index)).sendKeys(MOVE_KEYS[to]);\n }\n\n /**\n * Empty every box and leave focus on the first, ready to be typed into.\n *\n * Each box is cleared through an `input` event rather than by writing the\n * model, so the control publishes the change the same way it would for a user.\n */\n async clear(): Promise<void> {\n await this.assertEnabled('clear');\n for (const box of await this.getBoxes()) {\n // An already-empty box is skipped: clearing it would re-publish the same\n // code, and drag focus through the control on the way.\n if (!(await box.isEmpty())) await (await box.host()).clear();\n }\n await (await this.boxElement(0)).focus();\n }\n\n /** Type `chars` starting at `index`, following the control's focus as it advances. */\n private async typeFrom(index: number, chars: readonly string[]): Promise<void> {\n let cursor = index;\n for (const char of chars) {\n await (await this.getBox(cursor)).type(char);\n // The control advances focus itself, and only for a character it accepted.\n // Following it — rather than incrementing — is what keeps a rejected\n // character from silently shifting the rest of the code one box along.\n cursor = (await this.getFocusedIndex()) ?? cursor;\n }\n }\n\n private async boxElement(index: number): Promise<TestElement> {\n return (await this.getBox(index)).host();\n }\n\n private async focusedIndexOrThrow(method: string): Promise<number> {\n const index = await this.getFocusedIndex();\n if (index === null) {\n throw new Error(\n `WrInputOtpHarness.${method}(): no box has focus, and this acts on the focused box. ` +\n 'Call focus() (or type into the control) first.'\n );\n }\n return index;\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpHarness.${method}(): the control is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -333,10 +333,10 @@ class WrInputOtp {
333
333
  return 'ArrowRight';
334
334
  return key;
335
335
  }
336
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrInputOtp, deps: [], target: i0.ɵɵFactoryTarget.Component });
337
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrInputOtp, isStandalone: true, selector: "wr-input-otp", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", completed: "completed", touch: "touch" }, host: { attributes: { "role": "group" }, properties: { "class": "classes()", "attr.aria-label": "groupName()", "attr.aria-labelledby": "labelledBy()" } }, viewQueries: [{ propertyName: "cellRefs", predicate: ["cell"], descendants: true, isSignal: true }], ngImport: i0, template: "@for (cell of cells(); track trackIndex($index)) {\n <input\n #cell\n class=\"wr-input-otp__cell\"\n [type]=\"cellType()\"\n [attr.inputmode]=\"cellInputMode()\"\n maxlength=\"1\"\n autocomplete=\"one-time-code\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [value]=\"cell\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"cellLabel($index)\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n (input)=\"onInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (compositionend)=\"onCompositionEnd($event, $index)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n}\n", encapsulation: i0.ViewEncapsulation.None });
336
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrInputOtp, deps: [], target: i0.ɵɵFactoryTarget.Component });
337
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrInputOtp, isStandalone: true, selector: "wr-input-otp", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", completed: "completed", touch: "touch" }, host: { attributes: { "role": "group" }, properties: { "class": "classes()", "attr.aria-label": "groupName()", "attr.aria-labelledby": "labelledBy()" } }, viewQueries: [{ propertyName: "cellRefs", predicate: ["cell"], descendants: true, isSignal: true }], ngImport: i0, template: "@for (cell of cells(); track trackIndex($index)) {\n <input\n #cell\n class=\"wr-input-otp__cell\"\n [type]=\"cellType()\"\n [attr.inputmode]=\"cellInputMode()\"\n maxlength=\"1\"\n autocomplete=\"one-time-code\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [value]=\"cell\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"cellLabel($index)\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n (input)=\"onInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (compositionend)=\"onCompositionEnd($event, $index)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n}\n", encapsulation: i0.ViewEncapsulation.None });
338
338
  }
339
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrInputOtp, decorators: [{
339
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrInputOtp, decorators: [{
340
340
  type: Component,
341
341
  args: [{ selector: 'wr-input-otp', encapsulation: ViewEncapsulation.None, host: {
342
342
  '[class]': 'classes()',