ngwr 14.8.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 (447) 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/button/styles/_index.scss +6 -0
  7. package/cascader/styles/_rules.scss +1 -1
  8. package/checkbox/styles/_index.scss +4 -0
  9. package/command-palette/styles/_index.scss +1 -1
  10. package/divider/styles/_index.scss +2 -0
  11. package/editor/styles/_index.scss +441 -0
  12. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -1
  13. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  14. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  15. package/fesm2022/ngwr-affix.mjs +3 -3
  16. package/fesm2022/ngwr-affix.mjs.map +1 -1
  17. package/fesm2022/ngwr-alert.mjs +3 -3
  18. package/fesm2022/ngwr-alert.mjs.map +1 -1
  19. package/fesm2022/ngwr-anchor.mjs +3 -3
  20. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  21. package/fesm2022/ngwr-aurora.mjs +3 -3
  22. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  23. package/fesm2022/ngwr-avatar.mjs +3 -3
  24. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  25. package/fesm2022/ngwr-back-top.mjs +3 -3
  26. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  27. package/fesm2022/ngwr-badge.mjs +28 -13
  28. package/fesm2022/ngwr-badge.mjs.map +1 -1
  29. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
  30. package/fesm2022/ngwr-bar-chart.mjs +3 -3
  31. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  32. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -1
  33. package/fesm2022/ngwr-blur-text.mjs +3 -3
  34. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  35. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -1
  36. package/fesm2022/ngwr-border-glow.mjs +3 -3
  37. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  38. package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
  39. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  40. package/fesm2022/ngwr-burger.mjs +3 -3
  41. package/fesm2022/ngwr-burger.mjs.map +1 -1
  42. package/fesm2022/ngwr-button-testing.mjs.map +1 -1
  43. package/fesm2022/ngwr-button.mjs +6 -6
  44. package/fesm2022/ngwr-button.mjs.map +1 -1
  45. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
  46. package/fesm2022/ngwr-calendar-heatmap.mjs +3 -3
  47. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  48. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  49. package/fesm2022/ngwr-calendar.mjs +3 -3
  50. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  51. package/fesm2022/ngwr-card.mjs +9 -9
  52. package/fesm2022/ngwr-card.mjs.map +1 -1
  53. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -1
  54. package/fesm2022/ngwr-carousel.mjs +6 -6
  55. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  56. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
  57. package/fesm2022/ngwr-cascader.mjs +4 -4
  58. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  59. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
  60. package/fesm2022/ngwr-checkbox.mjs +6 -6
  61. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  62. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -1
  63. package/fesm2022/ngwr-circular-text.mjs +3 -3
  64. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  65. package/fesm2022/ngwr-click-spark.mjs +3 -3
  66. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  67. package/fesm2022/ngwr-clipboard.mjs +3 -3
  68. package/fesm2022/ngwr-clipboard.mjs.map +1 -1
  69. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -1
  70. package/fesm2022/ngwr-collapse.mjs +9 -9
  71. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  72. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  73. package/fesm2022/ngwr-color-picker.mjs +6 -6
  74. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  75. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -1
  76. package/fesm2022/ngwr-command-palette.mjs +3 -3
  77. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  78. package/fesm2022/ngwr-compare.mjs +3 -3
  79. package/fesm2022/ngwr-compare.mjs.map +1 -1
  80. package/fesm2022/ngwr-confetti.mjs +3 -3
  81. package/fesm2022/ngwr-confetti.mjs.map +1 -1
  82. package/fesm2022/ngwr-config.mjs.map +1 -1
  83. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
  84. package/fesm2022/ngwr-context-menu.mjs +12 -12
  85. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  86. package/fesm2022/ngwr-cookie.mjs +3 -3
  87. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  88. package/fesm2022/ngwr-counter.mjs +6 -6
  89. package/fesm2022/ngwr-counter.mjs.map +1 -1
  90. package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
  91. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  92. package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
  93. package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -1
  94. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  95. package/fesm2022/ngwr-date-picker.mjs +21 -21
  96. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  97. package/fesm2022/ngwr-date.mjs +3 -3
  98. package/fesm2022/ngwr-date.mjs.map +1 -1
  99. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -1
  100. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  101. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  102. package/fesm2022/ngwr-density.mjs +6 -6
  103. package/fesm2022/ngwr-density.mjs.map +1 -1
  104. package/fesm2022/ngwr-descriptions.mjs +6 -6
  105. package/fesm2022/ngwr-descriptions.mjs.map +1 -1
  106. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
  107. package/fesm2022/ngwr-dialog.mjs +15 -15
  108. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  109. package/fesm2022/ngwr-directives.mjs +12 -12
  110. package/fesm2022/ngwr-directives.mjs.map +1 -1
  111. package/fesm2022/ngwr-divider.mjs +3 -3
  112. package/fesm2022/ngwr-divider.mjs.map +1 -1
  113. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
  114. package/fesm2022/ngwr-donut-chart.mjs +3 -3
  115. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  116. package/fesm2022/ngwr-drag-drop.mjs +6 -6
  117. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  118. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -1
  119. package/fesm2022/ngwr-drawer.mjs +18 -18
  120. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  121. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -1
  122. package/fesm2022/ngwr-dropdown.mjs +9 -9
  123. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  124. package/fesm2022/ngwr-editor-testing.mjs +568 -0
  125. package/fesm2022/ngwr-editor-testing.mjs.map +1 -0
  126. package/fesm2022/ngwr-editor.mjs +2601 -0
  127. package/fesm2022/ngwr-editor.mjs.map +1 -0
  128. package/fesm2022/ngwr-empty.mjs +3 -3
  129. package/fesm2022/ngwr-empty.mjs.map +1 -1
  130. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  131. package/fesm2022/ngwr-event-calendar.mjs +6 -6
  132. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  133. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
  134. package/fesm2022/ngwr-falling-text.mjs +3 -3
  135. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  136. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -1
  137. package/fesm2022/ngwr-file-upload.mjs +3 -3
  138. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  139. package/fesm2022/ngwr-form-testing.mjs.map +1 -1
  140. package/fesm2022/ngwr-form.mjs +9 -9
  141. package/fesm2022/ngwr-form.mjs.map +1 -1
  142. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -1
  143. package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
  144. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  145. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
  146. package/fesm2022/ngwr-gauge.mjs +3 -3
  147. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  148. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
  149. package/fesm2022/ngwr-glitch-text.mjs +3 -3
  150. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  151. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
  152. package/fesm2022/ngwr-gradient-text.mjs +3 -3
  153. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  154. package/fesm2022/ngwr-graph-testing.mjs.map +1 -1
  155. package/fesm2022/ngwr-graph.mjs +6 -6
  156. package/fesm2022/ngwr-graph.mjs.map +1 -1
  157. package/fesm2022/ngwr-hotkey.mjs +6 -6
  158. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  159. package/fesm2022/ngwr-i18n-ar.mjs +29 -0
  160. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  161. package/fesm2022/ngwr-i18n-cs.mjs +29 -0
  162. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  163. package/fesm2022/ngwr-i18n-de.mjs +29 -0
  164. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  165. package/fesm2022/ngwr-i18n-en.mjs +34 -0
  166. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  167. package/fesm2022/ngwr-i18n-es.mjs +29 -0
  168. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  169. package/fesm2022/ngwr-i18n-fr.mjs +29 -0
  170. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  171. package/fesm2022/ngwr-i18n-he.mjs +29 -0
  172. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  173. package/fesm2022/ngwr-i18n-hi.mjs +29 -0
  174. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  175. package/fesm2022/ngwr-i18n-id.mjs +29 -0
  176. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  177. package/fesm2022/ngwr-i18n-it.mjs +29 -0
  178. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  179. package/fesm2022/ngwr-i18n-ja.mjs +29 -0
  180. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  181. package/fesm2022/ngwr-i18n-ko.mjs +29 -0
  182. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  183. package/fesm2022/ngwr-i18n-nl.mjs +29 -0
  184. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  185. package/fesm2022/ngwr-i18n-pl.mjs +29 -0
  186. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  187. package/fesm2022/ngwr-i18n-pt.mjs +29 -0
  188. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  189. package/fesm2022/ngwr-i18n-ru.mjs +29 -0
  190. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  191. package/fesm2022/ngwr-i18n-sv.mjs +29 -0
  192. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  193. package/fesm2022/ngwr-i18n-tr.mjs +29 -0
  194. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  195. package/fesm2022/ngwr-i18n-uk.mjs +29 -0
  196. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  197. package/fesm2022/ngwr-i18n-vi.mjs +29 -0
  198. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  199. package/fesm2022/ngwr-i18n-zh-TW.mjs +29 -0
  200. package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
  201. package/fesm2022/ngwr-i18n-zh.mjs +29 -0
  202. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  203. package/fesm2022/ngwr-i18n.mjs +9 -9
  204. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  205. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  206. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  207. package/fesm2022/ngwr-icon.mjs +6 -6
  208. package/fesm2022/ngwr-icon.mjs.map +1 -1
  209. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
  210. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  211. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  212. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
  213. package/fesm2022/ngwr-input-number.mjs +3 -3
  214. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  215. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
  216. package/fesm2022/ngwr-input-otp.mjs +3 -3
  217. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  218. package/fesm2022/ngwr-input-testing.mjs.map +1 -1
  219. package/fesm2022/ngwr-input.mjs +15 -15
  220. package/fesm2022/ngwr-input.mjs.map +1 -1
  221. package/fesm2022/ngwr-keyboard.mjs +3 -3
  222. package/fesm2022/ngwr-keyboard.mjs.map +1 -1
  223. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  224. package/fesm2022/ngwr-knob.mjs +3 -3
  225. package/fesm2022/ngwr-knob.mjs.map +1 -1
  226. package/fesm2022/ngwr-layout.mjs +15 -15
  227. package/fesm2022/ngwr-layout.mjs.map +1 -1
  228. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -1
  229. package/fesm2022/ngwr-lightbox.mjs +3 -3
  230. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  231. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
  232. package/fesm2022/ngwr-line-chart.mjs +3 -3
  233. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  234. package/fesm2022/ngwr-list.mjs +6 -6
  235. package/fesm2022/ngwr-list.mjs.map +1 -1
  236. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -1
  237. package/fesm2022/ngwr-loading-bar.mjs +6 -6
  238. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  239. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -1
  240. package/fesm2022/ngwr-markdown.mjs +798 -17
  241. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  242. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -1
  243. package/fesm2022/ngwr-marquee.mjs +3 -3
  244. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  245. package/fesm2022/ngwr-media.mjs +3 -3
  246. package/fesm2022/ngwr-media.mjs.map +1 -1
  247. package/fesm2022/ngwr-mention-testing.mjs.map +1 -1
  248. package/fesm2022/ngwr-mention.mjs +6 -6
  249. package/fesm2022/ngwr-mention.mjs.map +1 -1
  250. package/fesm2022/ngwr-meta.mjs +6 -6
  251. package/fesm2022/ngwr-meta.mjs.map +1 -1
  252. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
  253. package/fesm2022/ngwr-meter-group.mjs +3 -3
  254. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  255. package/fesm2022/ngwr-overlay.mjs +6 -6
  256. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  257. package/fesm2022/ngwr-page-header.mjs +3 -3
  258. package/fesm2022/ngwr-page-header.mjs.map +1 -1
  259. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  260. package/fesm2022/ngwr-pagination.mjs +3 -3
  261. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  262. package/fesm2022/ngwr-pipes.mjs +21 -21
  263. package/fesm2022/ngwr-pipes.mjs.map +1 -1
  264. package/fesm2022/ngwr-platform.mjs +9 -9
  265. package/fesm2022/ngwr-platform.mjs.map +1 -1
  266. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -1
  267. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  268. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  269. package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
  270. package/fesm2022/ngwr-popover.mjs +12 -12
  271. package/fesm2022/ngwr-popover.mjs.map +1 -1
  272. package/fesm2022/ngwr-progress.mjs +3 -3
  273. package/fesm2022/ngwr-progress.mjs.map +1 -1
  274. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  275. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  276. package/fesm2022/ngwr-qr.mjs +3 -3
  277. package/fesm2022/ngwr-qr.mjs.map +1 -1
  278. package/fesm2022/ngwr-radio-testing.mjs.map +1 -1
  279. package/fesm2022/ngwr-radio.mjs +6 -6
  280. package/fesm2022/ngwr-radio.mjs.map +1 -1
  281. package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
  282. package/fesm2022/ngwr-rating.mjs +3 -3
  283. package/fesm2022/ngwr-rating.mjs.map +1 -1
  284. package/fesm2022/ngwr-result.mjs +12 -12
  285. package/fesm2022/ngwr-result.mjs.map +1 -1
  286. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -1
  287. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  288. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  289. package/fesm2022/ngwr-schema-form.mjs +3 -3
  290. package/fesm2022/ngwr-schema-form.mjs.map +1 -1
  291. package/fesm2022/ngwr-scroll.mjs +3 -3
  292. package/fesm2022/ngwr-scroll.mjs.map +1 -1
  293. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
  294. package/fesm2022/ngwr-segmented.mjs +3 -3
  295. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  296. package/fesm2022/ngwr-select-testing.mjs.map +1 -1
  297. package/fesm2022/ngwr-select.mjs +12 -12
  298. package/fesm2022/ngwr-select.mjs.map +1 -1
  299. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -1
  300. package/fesm2022/ngwr-shiny-text.mjs +6 -6
  301. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  302. package/fesm2022/ngwr-sidebar.mjs +3 -3
  303. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  304. package/fesm2022/ngwr-skeleton.mjs +3 -3
  305. package/fesm2022/ngwr-skeleton.mjs.map +1 -1
  306. package/fesm2022/ngwr-slider-testing.mjs.map +1 -1
  307. package/fesm2022/ngwr-slider.mjs +3 -3
  308. package/fesm2022/ngwr-slider.mjs.map +1 -1
  309. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -1
  310. package/fesm2022/ngwr-sparkline.mjs +3 -3
  311. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  312. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
  313. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  314. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  315. package/fesm2022/ngwr-spinner.mjs +3 -3
  316. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  317. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -1
  318. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  319. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  320. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -1
  321. package/fesm2022/ngwr-split-text.mjs +3 -3
  322. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  323. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  324. package/fesm2022/ngwr-splitter.mjs +3 -3
  325. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  326. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -1
  327. package/fesm2022/ngwr-spotlight-card.mjs +6 -6
  328. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  329. package/fesm2022/ngwr-squircle.mjs +6 -6
  330. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  331. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -1
  332. package/fesm2022/ngwr-star-border.mjs +3 -3
  333. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  334. package/fesm2022/ngwr-statistic.mjs +9 -9
  335. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  336. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
  337. package/fesm2022/ngwr-stepper.mjs +6 -6
  338. package/fesm2022/ngwr-stepper.mjs.map +1 -1
  339. package/fesm2022/ngwr-storage.mjs +3 -3
  340. package/fesm2022/ngwr-storage.mjs.map +1 -1
  341. package/fesm2022/ngwr-switch-testing.mjs.map +1 -1
  342. package/fesm2022/ngwr-switch.mjs +3 -3
  343. package/fesm2022/ngwr-switch.mjs.map +1 -1
  344. package/fesm2022/ngwr-table-testing.mjs.map +1 -1
  345. package/fesm2022/ngwr-table.mjs +18 -18
  346. package/fesm2022/ngwr-table.mjs.map +1 -1
  347. package/fesm2022/ngwr-tabs-router.mjs +3 -3
  348. package/fesm2022/ngwr-tabs-router.mjs.map +1 -1
  349. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  350. package/fesm2022/ngwr-tabs.mjs +6 -6
  351. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  352. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -1
  353. package/fesm2022/ngwr-textarea.mjs +3 -3
  354. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  355. package/fesm2022/ngwr-theme.mjs +3 -3
  356. package/fesm2022/ngwr-theme.mjs.map +1 -1
  357. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -1
  358. package/fesm2022/ngwr-tilt-card.mjs +6 -6
  359. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  360. package/fesm2022/ngwr-timeline.mjs +6 -6
  361. package/fesm2022/ngwr-timeline.mjs.map +1 -1
  362. package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
  363. package/fesm2022/ngwr-toast.mjs +9 -9
  364. package/fesm2022/ngwr-toast.mjs.map +1 -1
  365. package/fesm2022/ngwr-toolbar.mjs +3 -3
  366. package/fesm2022/ngwr-toolbar.mjs.map +1 -1
  367. package/fesm2022/ngwr-tour-testing.mjs.map +1 -1
  368. package/fesm2022/ngwr-tour.mjs +6 -6
  369. package/fesm2022/ngwr-tour.mjs.map +1 -1
  370. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -1
  371. package/fesm2022/ngwr-transfer.mjs +3 -3
  372. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  373. package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
  374. package/fesm2022/ngwr-tree.mjs +3 -3
  375. package/fesm2022/ngwr-tree.mjs.map +1 -1
  376. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
  377. package/fesm2022/ngwr-typewriter.mjs +3 -3
  378. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  379. package/fesm2022/ngwr-typography.mjs +7 -3
  380. package/fesm2022/ngwr-typography.mjs.map +1 -1
  381. package/fesm2022/ngwr-utils.mjs.map +1 -1
  382. package/fesm2022/ngwr-validators.mjs.map +1 -1
  383. package/fesm2022/ngwr-version.mjs +1 -1
  384. package/fesm2022/ngwr-version.mjs.map +1 -1
  385. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  386. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  387. package/fesm2022/ngwr-waves-testing.mjs.map +1 -1
  388. package/fesm2022/ngwr-waves.mjs +3 -3
  389. package/fesm2022/ngwr-waves.mjs.map +1 -1
  390. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  391. package/fesm2022/ngwr-window.mjs +12 -12
  392. package/fesm2022/ngwr-window.mjs.map +1 -1
  393. package/form/styles/_index.scss +5 -0
  394. package/i18n/ar.json +29 -0
  395. package/i18n/cs.json +29 -0
  396. package/i18n/de.json +29 -0
  397. package/i18n/en.json +29 -0
  398. package/i18n/es.json +29 -0
  399. package/i18n/fr.json +29 -0
  400. package/i18n/he.json +29 -0
  401. package/i18n/hi.json +29 -0
  402. package/i18n/id.json +29 -0
  403. package/i18n/it.json +29 -0
  404. package/i18n/ja.json +29 -0
  405. package/i18n/ko.json +29 -0
  406. package/i18n/nl.json +29 -0
  407. package/i18n/pl.json +29 -0
  408. package/i18n/pt.json +29 -0
  409. package/i18n/ru.json +29 -0
  410. package/i18n/sv.json +29 -0
  411. package/i18n/tr.json +29 -0
  412. package/i18n/uk.json +29 -0
  413. package/i18n/vi.json +29 -0
  414. package/i18n/zh-TW.json +29 -0
  415. package/i18n/zh.json +29 -0
  416. package/input/styles/_index.scss +5 -1
  417. package/input-otp/styles/_index.scss +5 -5
  418. package/llms-full.txt +11 -1
  419. package/llms.txt +188 -132
  420. package/mcp/providers.js +2 -2
  421. package/mcp/server.js +1 -1
  422. package/overlay/styles/_arrow.scss +3 -0
  423. package/package.json +43 -2
  424. package/progress/styles/_index.scss +4 -0
  425. package/radio/styles/_index.scss +4 -1
  426. package/schematics/use/symbol-map.json +1 -0
  427. package/select/styles/_index.scss +3 -3
  428. package/sidebar/styles/_index.scss +10 -0
  429. package/skeleton/styles/_index.scss +3 -0
  430. package/skills/ngwr/SKILL.md +5 -5
  431. package/skills/ngwr/references/catalog.md +2 -0
  432. package/skills/ngwr/references/setup.md +3 -3
  433. package/styles.scss +1 -0
  434. package/table/styles/_index.scss +7 -0
  435. package/textarea/styles/_index.scss +12 -3
  436. package/theme/styles/_colors.scss +39 -13
  437. package/theme/styles/_contrast.scss +9 -7
  438. package/toast/styles/_index.scss +4 -0
  439. package/tree/styles/_index.scss +1 -1
  440. package/types/ngwr-badge.d.ts +22 -7
  441. package/types/ngwr-config.d.ts +12 -0
  442. package/types/ngwr-editor-testing.d.ts +378 -0
  443. package/types/ngwr-editor.d.ts +449 -0
  444. package/types/ngwr-markdown.d.ts +61 -2
  445. package/types/ngwr-typography.d.ts +4 -0
  446. package/types/ngwr-version.d.ts +1 -1
  447. package/typography/styles/_index.scss +16 -0
@@ -11,9 +11,10 @@ import { WrSpinner } from 'ngwr/spinner';
11
11
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
12
12
  */
13
13
  /**
14
- * Lean status chip — color, size, rounded/pill. Use for short labels
15
- * and counts (e.g. "ONLINE", "3 unread", "v2.1"). Reach for `<wr-tag>`
16
- * when you need icons, loading state, or outlined / transparent styles.
14
+ * Lean chip — color, size, rounded/pill. Use for short labels and counts
15
+ * (e.g. "ONLINE", "3 unread", "v2.1"), where the words say the state and the
16
+ * colour decorates it. Reach for `<wr-tag>` when you need icons, loading
17
+ * state, or outlined / transparent styles.
17
18
  *
18
19
  * @example
19
20
  * ```html
@@ -26,7 +27,12 @@ import { WrSpinner } from 'ngwr/spinner';
26
27
  */
27
28
  class WrBadge {
28
29
  /**
29
- * Color variant. Maps to `--wr-color-*` CSS variables from the theme.
30
+ * Color variant. Maps to `--wr-color-*` CSS variables from the theme. It is
31
+ * decoration or category, never the state on its own: six of the nine intents
32
+ * share one lightness and `success` against `danger` is 1.004:1, so the label
33
+ * has to say the state (`Offline`, not a red `Status`). A badge draws no
34
+ * icon — when a chip must stand for a status without words, use `<wr-tag>`
35
+ * with its `icon` input.
30
36
  *
31
37
  * @default 'primary'
32
38
  */
@@ -69,10 +75,10 @@ class WrBadge {
69
75
  return parts.join(' ');
70
76
  }, /* @ts-ignore */
71
77
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
72
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
73
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: WrBadge, isStandalone: true, selector: "wr-badge", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
78
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
79
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: WrBadge, isStandalone: true, selector: "wr-badge", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
74
80
  }
75
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBadge, decorators: [{
81
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrBadge, decorators: [{
76
82
  type: Component,
77
83
  args: [{
78
84
  selector: 'wr-badge',
@@ -107,15 +113,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
107
113
  */
108
114
  class WrTag {
109
115
  /**
110
- * Color variant.
116
+ * Color variant — decoration or category, never the state on its own. The
117
+ * label carries the state (`Failed`, not a red `Build`): six of the nine
118
+ * intents share one lightness and `success` against `danger` is 1.004:1, so a
119
+ * reader who cannot separate red from green cannot tell those two tags
120
+ * apart, and `primary` and `info` are the same blue to everyone. When a tag has to stand
121
+ * for a status by itself, give it an `icon` as well — register one with
122
+ * `provideWrIcons(lucideIcons({ 'octagon-alert': OctagonAlert }))`, then write
123
+ * `<wr-tag color="danger" icon="octagon-alert">Failed</wr-tag>`.
111
124
  *
112
125
  * @default 'primary'
113
126
  */
114
127
  color = input('primary', /* @ts-ignore */
115
128
  ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
116
129
  /**
117
- * Icon name shown alongside the content. The icon is replaced by a
118
- * spinner when `loading` is true.
130
+ * Icon name shown alongside the content — the one channel besides the label
131
+ * that a tag has, so the one to reach for when colour is carrying a status.
132
+ * Nothing ships built in: register the name through `provideWrIcons` first.
133
+ * The icon is replaced by a spinner when `loading` is true.
119
134
  *
120
135
  * @default null
121
136
  */
@@ -176,10 +191,10 @@ class WrTag {
176
191
  return parts.join(' ');
177
192
  }, /* @ts-ignore */
178
193
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
179
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrTag, deps: [], target: i0.ɵɵFactoryTarget.Component });
180
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrTag, isStandalone: true, selector: "wr-tag", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null }, transparent: { classPropertyName: "transparent", publicName: "transparent", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "@if (iconPosition() === 'start') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n\n<ng-content />\n\n@if (iconPosition() === 'end') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }, { kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size", "ariaLabel"] }], encapsulation: i0.ViewEncapsulation.None });
194
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrTag, deps: [], target: i0.ɵɵFactoryTarget.Component });
195
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrTag, isStandalone: true, selector: "wr-tag", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null }, transparent: { classPropertyName: "transparent", publicName: "transparent", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "@if (iconPosition() === 'start') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n\n<ng-content />\n\n@if (iconPosition() === 'end') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }, { kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size", "ariaLabel"] }], encapsulation: i0.ViewEncapsulation.None });
181
196
  }
182
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrTag, decorators: [{
197
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrTag, decorators: [{
183
198
  type: Component,
184
199
  args: [{ selector: 'wr-tag', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrIcon, WrSpinner], template: "@if (iconPosition() === 'start') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n\n<ng-content />\n\n@if (iconPosition() === 'end') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n" }]
185
200
  }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], outlined: [{ type: i0.Input, args: [{ isSignal: true, alias: "outlined", required: false }] }], transparent: [{ type: i0.Input, args: [{ isSignal: true, alias: "transparent", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-badge.mjs","sources":["../../../projects/lib/badge/badge.ts","../../../projects/lib/badge/tag.ts","../../../projects/lib/badge/tag.html","../../../projects/lib/badge/ngwr-badge.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport type { WrColor } from 'ngwr/theme';\n\nimport type { WrBadgeShape, WrBadgeSize } from './interfaces';\n\n/**\n * Lean status chip — color, size, rounded/pill. Use for short labels\n * and counts (e.g. \"ONLINE\", \"3 unread\", \"v2.1\"). Reach for `<wr-tag>`\n * when you need icons, loading state, or outlined / transparent styles.\n *\n * @example\n * ```html\n * <wr-badge color=\"success\">Active</wr-badge>\n * <wr-badge color=\"danger\" size=\"sm\">3</wr-badge>\n * <wr-badge color=\"primary\" shape=\"pill\">New</wr-badge>\n * ```\n *\n * @see https://ngwr.dev/reference/components/badge\n */\n@Component({\n selector: 'wr-badge',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n },\n})\nexport class WrBadge {\n /**\n * Color variant. Maps to `--wr-color-*` CSS variables from the theme.\n *\n * @default 'primary'\n */\n readonly color = input<WrColor>('primary');\n\n /**\n * Size variant.\n *\n * @default 'md'\n */\n readonly size = input<WrBadgeSize>('md');\n\n /**\n * Corner treatment. Mirrors `<wr-btn>` — `rounded` (default) uses the\n * small form-radius; `pill` rounds the ends fully.\n *\n * @default 'rounded'\n */\n readonly shape = input<WrBadgeShape>('rounded');\n\n /**\n * Outlined style — transparent fill, colored border and text.\n *\n * @default false\n */\n readonly outlined = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-badge', `wr-badge--${this.color()}`];\n const size = this.size();\n if (size !== 'md') {\n parts.push(`wr-badge--${size}`);\n }\n const shape = this.shape();\n if (shape !== 'rounded') {\n parts.push(`wr-badge--${shape}`);\n }\n if (this.outlined()) {\n parts.push('wr-badge--outlined');\n }\n return parts.join(' ');\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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { WrSpinner } from 'ngwr/spinner';\nimport type { WrColor } from 'ngwr/theme';\n\nimport type { WrTagIconPosition } from './interfaces';\n\n/**\n * Rich inline label — icons, loading state, three visual styles\n * (solid / outlined / transparent). Reach for `<wr-badge>` when all\n * you need is a colored text chip.\n *\n * @example\n * ```html\n * <wr-tag>Default</wr-tag>\n * <wr-tag color=\"success\" icon=\"checkmark\">Done</wr-tag>\n * <wr-tag color=\"primary\" outlined rounded>Beta</wr-tag>\n * <wr-tag color=\"warning\" loading>Saving</wr-tag>\n * ```\n *\n * @see https://ngwr.dev/reference/components/badge\n */\n@Component({\n selector: 'wr-tag',\n templateUrl: './tag.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrIcon, WrSpinner],\n})\nexport class WrTag {\n /**\n * Color variant.\n *\n * @default 'primary'\n */\n readonly color = input<WrColor>('primary');\n\n /**\n * Icon name shown alongside the content. The icon is replaced by a\n * spinner when `loading` is true.\n *\n * @default null\n */\n readonly icon = input<WrIconName | null>(null);\n\n /**\n * Where the icon/spinner is rendered.\n *\n * @default 'start'\n */\n readonly iconPosition = input<WrTagIconPosition>('start');\n\n /**\n * Outlined style — colored text and border on a tinted background.\n *\n * @default false\n */\n readonly outlined = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Transparent style — colored text on a low-opacity tint, no border.\n *\n * @default false\n */\n readonly transparent = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Pill-shaped corners.\n *\n * @default false\n */\n readonly rounded = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Adds a hover state — use when the tag is interactive (button/link).\n *\n * @default false\n */\n readonly hoverable = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Show a spinner in place of the icon.\n *\n * @default false\n */\n readonly loading = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-tag', `wr-tag--${this.color()}`];\n if (this.outlined()) parts.push('wr-tag--outlined');\n if (this.transparent()) parts.push('wr-tag--transparent');\n if (this.rounded()) parts.push('wr-tag--rounded');\n if (this.hoverable()) parts.push('wr-tag--hoverable');\n if (this.loading()) parts.push('wr-tag--loading');\n\n const hasAdornment = !!this.icon() || this.loading();\n if (hasAdornment) parts.push(`wr-tag--icon-${this.iconPosition()}`);\n\n return parts.join(' ');\n });\n}\n","@if (iconPosition() === 'start') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n\n<ng-content />\n\n@if (iconPosition() === 'end') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\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-badge.mjs","sources":["../../../projects/lib/badge/badge.ts","../../../projects/lib/badge/tag.ts","../../../projects/lib/badge/tag.html","../../../projects/lib/badge/ngwr-badge.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport type { WrColor } from 'ngwr/theme';\n\nimport type { WrBadgeShape, WrBadgeSize } from './interfaces';\n\n/**\n * Lean chip — color, size, rounded/pill. Use for short labels and counts\n * (e.g. \"ONLINE\", \"3 unread\", \"v2.1\"), where the words say the state and the\n * colour decorates it. Reach for `<wr-tag>` when you need icons, loading\n * state, or outlined / transparent styles.\n *\n * @example\n * ```html\n * <wr-badge color=\"success\">Active</wr-badge>\n * <wr-badge color=\"danger\" size=\"sm\">3</wr-badge>\n * <wr-badge color=\"primary\" shape=\"pill\">New</wr-badge>\n * ```\n *\n * @see https://ngwr.dev/reference/components/badge\n */\n@Component({\n selector: 'wr-badge',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n },\n})\nexport class WrBadge {\n /**\n * Color variant. Maps to `--wr-color-*` CSS variables from the theme. It is\n * decoration or category, never the state on its own: six of the nine intents\n * share one lightness and `success` against `danger` is 1.004:1, so the label\n * has to say the state (`Offline`, not a red `Status`). A badge draws no\n * icon — when a chip must stand for a status without words, use `<wr-tag>`\n * with its `icon` input.\n *\n * @default 'primary'\n */\n readonly color = input<WrColor>('primary');\n\n /**\n * Size variant.\n *\n * @default 'md'\n */\n readonly size = input<WrBadgeSize>('md');\n\n /**\n * Corner treatment. Mirrors `<wr-btn>` — `rounded` (default) uses the\n * small form-radius; `pill` rounds the ends fully.\n *\n * @default 'rounded'\n */\n readonly shape = input<WrBadgeShape>('rounded');\n\n /**\n * Outlined style — transparent fill, colored border and text.\n *\n * @default false\n */\n readonly outlined = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-badge', `wr-badge--${this.color()}`];\n const size = this.size();\n if (size !== 'md') {\n parts.push(`wr-badge--${size}`);\n }\n const shape = this.shape();\n if (shape !== 'rounded') {\n parts.push(`wr-badge--${shape}`);\n }\n if (this.outlined()) {\n parts.push('wr-badge--outlined');\n }\n return parts.join(' ');\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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { WrSpinner } from 'ngwr/spinner';\nimport type { WrColor } from 'ngwr/theme';\n\nimport type { WrTagIconPosition } from './interfaces';\n\n/**\n * Rich inline label — icons, loading state, three visual styles\n * (solid / outlined / transparent). Reach for `<wr-badge>` when all\n * you need is a colored text chip.\n *\n * @example\n * ```html\n * <wr-tag>Default</wr-tag>\n * <wr-tag color=\"success\" icon=\"checkmark\">Done</wr-tag>\n * <wr-tag color=\"primary\" outlined rounded>Beta</wr-tag>\n * <wr-tag color=\"warning\" loading>Saving</wr-tag>\n * ```\n *\n * @see https://ngwr.dev/reference/components/badge\n */\n@Component({\n selector: 'wr-tag',\n templateUrl: './tag.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrIcon, WrSpinner],\n})\nexport class WrTag {\n /**\n * Color variant — decoration or category, never the state on its own. The\n * label carries the state (`Failed`, not a red `Build`): six of the nine\n * intents share one lightness and `success` against `danger` is 1.004:1, so a\n * reader who cannot separate red from green cannot tell those two tags\n * apart, and `primary` and `info` are the same blue to everyone. When a tag has to stand\n * for a status by itself, give it an `icon` as well — register one with\n * `provideWrIcons(lucideIcons({ 'octagon-alert': OctagonAlert }))`, then write\n * `<wr-tag color=\"danger\" icon=\"octagon-alert\">Failed</wr-tag>`.\n *\n * @default 'primary'\n */\n readonly color = input<WrColor>('primary');\n\n /**\n * Icon name shown alongside the content — the one channel besides the label\n * that a tag has, so the one to reach for when colour is carrying a status.\n * Nothing ships built in: register the name through `provideWrIcons` first.\n * The icon is replaced by a spinner when `loading` is true.\n *\n * @default null\n */\n readonly icon = input<WrIconName | null>(null);\n\n /**\n * Where the icon/spinner is rendered.\n *\n * @default 'start'\n */\n readonly iconPosition = input<WrTagIconPosition>('start');\n\n /**\n * Outlined style — colored text and border on a tinted background.\n *\n * @default false\n */\n readonly outlined = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Transparent style — colored text on a low-opacity tint, no border.\n *\n * @default false\n */\n readonly transparent = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Pill-shaped corners.\n *\n * @default false\n */\n readonly rounded = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Adds a hover state — use when the tag is interactive (button/link).\n *\n * @default false\n */\n readonly hoverable = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Show a spinner in place of the icon.\n *\n * @default false\n */\n readonly loading = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-tag', `wr-tag--${this.color()}`];\n if (this.outlined()) parts.push('wr-tag--outlined');\n if (this.transparent()) parts.push('wr-tag--transparent');\n if (this.rounded()) parts.push('wr-tag--rounded');\n if (this.hoverable()) parts.push('wr-tag--hoverable');\n if (this.loading()) parts.push('wr-tag--loading');\n\n const hasAdornment = !!this.icon() || this.loading();\n if (hasAdornment) parts.push(`wr-tag--icon-${this.iconPosition()}`);\n\n return parts.join(' ');\n });\n}\n","@if (iconPosition() === 'start') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\n\n<ng-content />\n\n@if (iconPosition() === 'end') {\n @if (loading()) {\n <wr-spinner class=\"wr-tag__adornment\" size=\"sm\" />\n } @else if (icon(); as name) {\n <wr-icon class=\"wr-tag__adornment\" [name]=\"name\" />\n }\n}\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-bar-chart-testing.mjs","sources":["../../../projects/lib/bar-chart/testing/wr-bar-chart-harness.ts","../../../projects/lib/bar-chart/testing/ngwr-bar-chart-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 { WrBarChartBar, WrBarChartHarnessFilters } from './interfaces';\n\n/** The `height: <n>%` the component writes onto a bar, out of its inline `style`. */\nconst BAR_HEIGHT = /(?:^|;)\\s*height\\s*:\\s*([\\d.]+)%/;\n\n/**\n * Test harness for `<wr-bar-chart>`.\n *\n * **The bars are read as PERCENTAGES, not as pixels.** Each column's height is\n * written inline as a share of the chart's maximum, which is the one number that\n * survives a test with no layout — and it is also the component's whole job, since\n * the maximum is either given or derived from the data. That share is read off the\n * `style` ATTRIBUTE rather than through `getCssValue()`, and the difference is not\n * cosmetic: `getCssValue()` is `getComputedStyle()`, which hands the declared `60%`\n * straight back in jsdom and resolves it to used pixels in a real browser — so the\n * same harness answered `100` here and `200` under Selenium or a browser runner,\n * with no error to say which one you got. The stylesheet's own `min-height: 1px`\n * is a second way that read lies, turning an empty bar into `1`.\n *\n * **The visible label row is decoration.** Every column carries `role=\"img\"` with\n * the label AND the value in its name, and the printed labels underneath are\n * `aria-hidden` — so {@link WrBarChartBar.name} is what a screen reader gets and\n * {@link WrBarChartBar.label} is what is drawn. Asserting only the second would pass\n * on a chart that announces nothing.\n *\n * @example\n * ```ts\n * const chart = await loader.getHarness(WrBarChartHarness);\n *\n * expect(await chart.getLabels()).toEqual(['Mon', 'Tue', 'Wed']);\n * expect((await chart.getBars())[1].heightPercent).toBe(100);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBarChartHarness extends ComponentHarness {\n static hostSelector = 'wr-bar-chart';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBarChartHarnessFilters = {}): HarnessPredicate<WrBarChartHarness> {\n return new HarnessPredicate(WrBarChartHarness, options).addOption(\n 'barLabel',\n options.barLabel,\n async (harness, label) => {\n for (const printed of await harness.getLabels()) {\n if (await HarnessPredicate.stringMatches(printed, label)) return true;\n }\n return false;\n }\n );\n }\n\n /** Every column, in data order. */\n async getBars(): Promise<WrBarChartBar[]> {\n const columns = await this.locatorForAll('.wr-bar-chart__column')();\n const bars = await this.locatorForAll('.wr-bar-chart__bar')();\n const labels = await this.getLabels();\n\n return Promise.all(\n columns.map(async (column, index) => {\n const value = await column.text();\n return {\n label: labels[index] ?? '',\n value: value === '' ? null : value,\n name: await column.getAttribute('aria-label'),\n heightPercent: WrBarChartHarness.heightPercentOf(await bars[index].getAttribute('style')),\n };\n })\n );\n }\n\n /** Parse one bar's declared share. See the class note on why this is not `getCssValue()`. */\n private static heightPercentOf(style: string | null): number {\n const match = BAR_HEIGHT.exec(style ?? '');\n if (!match) {\n throw new Error(\n `WrBarChartHarness.getBars(): a bar carries no inline height percentage (style: \"${style ?? ''}\"). ` +\n 'The component writes one for every bar, an empty one included, so its absence is the bug — ' +\n 'reporting 0 would read as a bar with nothing in it.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n\n /** The labels printed under the columns, in order. */\n async getLabels(): Promise<string[]> {\n const labels = await this.locatorForAll('.wr-bar-chart__label')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /** What each column announces — the label and the value together. */\n async getAccessibleNames(): Promise<(string | null)[]> {\n const columns = await this.locatorForAll('.wr-bar-chart__column')();\n return Promise.all(columns.map(column => column.getAttribute('aria-label')));\n }\n\n /** How many columns the chart drew. */\n async getBarCount(): Promise<number> {\n return (await this.locatorForAll('.wr-bar-chart__column')()).length;\n }\n\n /** Whether the values are printed above the bars (`showValues`). */\n async hasValues(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-bar-chart__value')()) !== null;\n }\n\n /**\n * The plot's height in pixels, read off the `style` ATTRIBUTE.\n *\n * NOT `getCssValue()`, which is `getComputedStyle()` — the same rule this\n * file's class note already states and this method alone broke. A computed\n * read answers from the LAID-OUT box, so it agrees with the inline value in\n * jsdom (which lays nothing out and echoes the declaration) and disagrees in a\n * browser, where sub-pixel rounding turns a declared 240 into 239.5. A harness\n * that answers differently in the two places it runs is worse than one that\n * refuses.\n */\n async getPlotHeight(): Promise<number> {\n const style = await (await this.locatorFor('.wr-bar-chart__plot')()).getAttribute('style');\n const match = /height:\\s*([\\d.]+)px/.exec(style ?? '');\n if (!match) {\n throw new Error(\n `WrBarChartHarness.getPlotHeight(): no inline height on .wr-bar-chart__plot (style: \"${style ?? ''}\"). ` +\n 'The component writes one unconditionally, so its absence is the bug rather than a zero.'\n );\n }\n return Number.parseFloat(match[1]);\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-bar-chart-testing.mjs","sources":["../../../projects/lib/bar-chart/testing/wr-bar-chart-harness.ts","../../../projects/lib/bar-chart/testing/ngwr-bar-chart-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 { WrBarChartBar, WrBarChartHarnessFilters } from './interfaces';\n\n/** The `height: <n>%` the component writes onto a bar, out of its inline `style`. */\nconst BAR_HEIGHT = /(?:^|;)\\s*height\\s*:\\s*([\\d.]+)%/;\n\n/**\n * Test harness for `<wr-bar-chart>`.\n *\n * **The bars are read as PERCENTAGES, not as pixels.** Each column's height is\n * written inline as a share of the chart's maximum, which is the one number that\n * survives a test with no layout — and it is also the component's whole job, since\n * the maximum is either given or derived from the data. That share is read off the\n * `style` ATTRIBUTE rather than through `getCssValue()`, and the difference is not\n * cosmetic: `getCssValue()` is `getComputedStyle()`, which hands the declared `60%`\n * straight back in jsdom and resolves it to used pixels in a real browser — so the\n * same harness answered `100` here and `200` under Selenium or a browser runner,\n * with no error to say which one you got. The stylesheet's own `min-height: 1px`\n * is a second way that read lies, turning an empty bar into `1`.\n *\n * **The visible label row is decoration.** Every column carries `role=\"img\"` with\n * the label AND the value in its name, and the printed labels underneath are\n * `aria-hidden` — so {@link WrBarChartBar.name} is what a screen reader gets and\n * {@link WrBarChartBar.label} is what is drawn. Asserting only the second would pass\n * on a chart that announces nothing.\n *\n * @example\n * ```ts\n * const chart = await loader.getHarness(WrBarChartHarness);\n *\n * expect(await chart.getLabels()).toEqual(['Mon', 'Tue', 'Wed']);\n * expect((await chart.getBars())[1].heightPercent).toBe(100);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBarChartHarness extends ComponentHarness {\n static hostSelector = 'wr-bar-chart';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBarChartHarnessFilters = {}): HarnessPredicate<WrBarChartHarness> {\n return new HarnessPredicate(WrBarChartHarness, options).addOption(\n 'barLabel',\n options.barLabel,\n async (harness, label) => {\n for (const printed of await harness.getLabels()) {\n if (await HarnessPredicate.stringMatches(printed, label)) return true;\n }\n return false;\n }\n );\n }\n\n /** Every column, in data order. */\n async getBars(): Promise<WrBarChartBar[]> {\n const columns = await this.locatorForAll('.wr-bar-chart__column')();\n const bars = await this.locatorForAll('.wr-bar-chart__bar')();\n const labels = await this.getLabels();\n\n return Promise.all(\n columns.map(async (column, index) => {\n const value = await column.text();\n return {\n label: labels[index] ?? '',\n value: value === '' ? null : value,\n name: await column.getAttribute('aria-label'),\n heightPercent: WrBarChartHarness.heightPercentOf(await bars[index].getAttribute('style')),\n };\n })\n );\n }\n\n /** Parse one bar's declared share. See the class note on why this is not `getCssValue()`. */\n private static heightPercentOf(style: string | null): number {\n const match = BAR_HEIGHT.exec(style ?? '');\n if (!match) {\n throw new Error(\n `WrBarChartHarness.getBars(): a bar carries no inline height percentage (style: \"${style ?? ''}\"). ` +\n 'The component writes one for every bar, an empty one included, so its absence is the bug — ' +\n 'reporting 0 would read as a bar with nothing in it.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n\n /** The labels printed under the columns, in order. */\n async getLabels(): Promise<string[]> {\n const labels = await this.locatorForAll('.wr-bar-chart__label')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /** What each column announces — the label and the value together. */\n async getAccessibleNames(): Promise<(string | null)[]> {\n const columns = await this.locatorForAll('.wr-bar-chart__column')();\n return Promise.all(columns.map(column => column.getAttribute('aria-label')));\n }\n\n /** How many columns the chart drew. */\n async getBarCount(): Promise<number> {\n return (await this.locatorForAll('.wr-bar-chart__column')()).length;\n }\n\n /** Whether the values are printed above the bars (`showValues`). */\n async hasValues(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-bar-chart__value')()) !== null;\n }\n\n /**\n * The plot's height in pixels, read off the `style` ATTRIBUTE.\n *\n * NOT `getCssValue()`, which is `getComputedStyle()` — the same rule this\n * file's class note already states and this method alone broke. A computed\n * read answers from the LAID-OUT box, so it agrees with the inline value in\n * jsdom (which lays nothing out and echoes the declaration) and disagrees in a\n * browser, where sub-pixel rounding turns a declared 240 into 239.5. A harness\n * that answers differently in the two places it runs is worse than one that\n * refuses.\n */\n async getPlotHeight(): Promise<number> {\n const style = await (await this.locatorFor('.wr-bar-chart__plot')()).getAttribute('style');\n const match = /height:\\s*([\\d.]+)px/.exec(style ?? '');\n if (!match) {\n throw new Error(\n `WrBarChartHarness.getPlotHeight(): no inline height on .wr-bar-chart__plot (style: \"${style ?? ''}\"). ` +\n 'The component writes one unconditionally, so its absence is the bug rather than a zero.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -99,10 +99,10 @@ class WrBarChart {
99
99
  return null;
100
100
  return { datum: { label: bar.label, value: String(bar.value), color: bar.color }, anchor };
101
101
  });
102
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBarChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
103
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrBarChart, isStandalone: true, selector: "wr-bar-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-bar-chart" }, viewQueries: [{ propertyName: "barEls", predicate: ["barEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
102
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrBarChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
103
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrBarChart, isStandalone: true, selector: "wr-bar-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-bar-chart" }, viewQueries: [{ propertyName: "barEls", predicate: ["barEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
104
104
  }
105
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBarChart, decorators: [{
105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrBarChart, decorators: [{
106
106
  type: Component,
107
107
  args: [{ selector: 'wr-bar-chart', encapsulation: ViewEncapsulation.None, host: { class: 'wr-bar-chart' }, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n" }]
108
108
  }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], barEls: [{ type: i0.ViewChildren, args: ['barEl', { isSignal: true }] }] } });
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-bar-chart.mjs","sources":["../../../projects/lib/bar-chart/bar-chart.ts","../../../projects/lib/bar-chart/bar-chart.html","../../../projects/lib/bar-chart/ngwr-bar-chart.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, type ElementRef, ViewEncapsulation, computed, input, viewChildren } from '@angular/core';\n\nimport { useChartTooltip } from 'ngwr/popover';\n\nimport type { WrBarChartDatum } from './interfaces';\n\n/**\n * Minimal vertical bar chart. Pass `[data]` — each datum becomes a bar.\n * Axis-free; for the simple \"show me a comparison\" case.\n *\n * @example\n * ```html\n * <wr-bar-chart [data]=\"[\n * { label: 'Mon', value: 12 },\n * { label: 'Tue', value: 18, color: 'var(--wr-color-success)' },\n * { label: 'Wed', value: 9 }\n * ]\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/bar-chart\n */\n@Component({\n selector: 'wr-bar-chart',\n templateUrl: './bar-chart.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-bar-chart' },\n})\nexport class WrBarChart {\n readonly data = input<readonly WrBarChartDatum[]>([]);\n\n /** Default bar colour when a datum has none. @default primary */\n readonly color = input<string>('var(--wr-color-primary)');\n\n /** Show value labels above each bar. @default true */\n readonly showValues = input(true, { transform: coerceBooleanProperty });\n\n /** Pixel height of the chart area. @default 200 */\n readonly height = input(200, { transform: (v: unknown): number => Math.max(40, coerceNumberProperty(v, 200)) });\n\n /** Explicit max value. `0` = auto. @default 0 */\n readonly max = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /**\n * Show a tooltip with the bar's label and value on hover. Pointer only — the\n * column's accessible name already carries both. @default true\n */\n readonly tooltip = input(true, { transform: coerceBooleanProperty });\n\n private readonly barEls = viewChildren<ElementRef<HTMLElement>>('barEl');\n\n /**\n * A datum's value, with anything non-finite read as `0`.\n *\n * One `NaN` used to take out the whole chart rather than its own bar:\n * `Math.max(...values)` is `NaN` as soon as one value is, so the scale fell\n * back to `1` and every OTHER bar was asked for a height of `value * 100`%.\n * The bad bar also printed the literal text `NaN` above itself.\n */\n private static value(raw: number): number {\n return Number.isFinite(raw) ? raw : 0;\n }\n\n protected readonly resolvedMax = computed(() => {\n const explicit = this.max();\n if (explicit > 0) return explicit;\n const data = this.data();\n if (data.length === 0) return 1;\n const top = Math.max(...data.map(d => WrBarChart.value(d.value)), 0);\n return top > 0 ? top : 1;\n });\n\n protected readonly bars = computed(() =>\n this.data().map(d => {\n const value = WrBarChart.value(d.value);\n return {\n label: d.label,\n value,\n color: d.color ?? this.color(),\n heightPercent: (Math.max(0, value) / this.resolvedMax()) * 100,\n /**\n * The bar's accessible name. The label and the value sit in separate\n * rows, so a screen reader read every number and then every label with\n * nothing tying the two together — and with `showValues` off the\n * numbers were not in the accessible tree at all.\n */\n name: `${d.label}: ${value}`,\n };\n })\n );\n\n /**\n * One tooltip for the whole chart, with the value exactly as it is printed above\n * the bar. It points at the top edge of the BAR — above it, below it when there is\n * no room — rather than at the column, which is the full height of the plot and\n * would put the chip above every bar alike. A bar with nothing to draw (zero, a\n * negative value) keeps its 1px floor on the baseline, so its chip points there.\n *\n * The bar is the anchor element itself, so an open chip follows it through the\n * height transition a data change starts.\n */\n protected readonly tip = useChartTooltip(this.tooltip, index => {\n const bar = this.bars()[index];\n const anchor = this.barEls()[index]?.nativeElement;\n if (!bar || !anchor) return null;\n return { datum: { label: bar.label, value: String(bar.value), color: bar.color }, anchor };\n });\n}\n\nexport type { WrBarChartDatum } from './interfaces';\n","<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;AAAA;;;;;AAKG;AASH;;;;;;;;;;;;;;AAcG;MAOU,UAAU,CAAA;IACZ,IAAI,GAAG,KAAK,CAA6B,EAAE;6EAAC;;IAG5C,KAAK,GAAG,KAAK,CAAS,yBAAyB;8EAAC;;IAGhD,UAAU,GAAG,KAAK,CAAC,IAAI,kFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG9D,MAAM,GAAG,KAAK,CAAC,GAAG,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,QAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,oBAAoB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAA,CAAG;;IAGtG,GAAG,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,KAAA,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;AAEvG;;;AAGG;IACM,OAAO,GAAG,KAAK,CAAC,IAAI,+EAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;IAEnD,MAAM,GAAG,YAAY,CAA0B,OAAO;+EAAC;AAExE;;;;;;;AAOG;IACK,OAAO,KAAK,CAAC,GAAW,EAAA;AAC9B,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC;IACvC;AAEmB,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;AAC7C,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE;QAC3B,IAAI,QAAQ,GAAG,CAAC;AAAE,YAAA,OAAO,QAAQ;AACjC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;AAAE,YAAA,OAAO,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACpE,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC;IAC1B,CAAC;oFAAC;AAEiB,IAAA,IAAI,GAAG,QAAQ,CAAC,MACjC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAG;QAClB,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QACvC,OAAO;YACL,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,KAAK;YACL,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE;AAC9B,YAAA,aAAa,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG;AAC9D;;;;;AAKG;AACH,YAAA,IAAI,EAAE,CAAA,EAAG,CAAC,CAAC,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE;SAC7B;AACH,IAAA,CAAC,CAAC;6EACH;AAED;;;;;;;;;AASG;IACgB,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,IAAG;QAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,aAAa;AAClD,QAAA,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,IAAI;AAChC,QAAA,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE;AAC5F,IAAA,CAAC,CAAC;uGA9ES,UAAU,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAV,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAU,i8BCnCvB,ozBAsBA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDaa,UAAU,EAAA,UAAA,EAAA,CAAA;kBANtB,SAAS;+BACE,cAAc,EAAA,aAAA,EAET,iBAAiB,CAAC,IAAI,QAC/B,EAAE,KAAK,EAAE,cAAc,EAAE,EAAA,QAAA,EAAA,ozBAAA,EAAA;6mBAuBiC,OAAO,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AExDzE;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-bar-chart.mjs","sources":["../../../projects/lib/bar-chart/bar-chart.ts","../../../projects/lib/bar-chart/bar-chart.html","../../../projects/lib/bar-chart/ngwr-bar-chart.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, type ElementRef, ViewEncapsulation, computed, input, viewChildren } from '@angular/core';\n\nimport { useChartTooltip } from 'ngwr/popover';\n\nimport type { WrBarChartDatum } from './interfaces';\n\n/**\n * Minimal vertical bar chart. Pass `[data]` — each datum becomes a bar.\n * Axis-free; for the simple \"show me a comparison\" case.\n *\n * @example\n * ```html\n * <wr-bar-chart [data]=\"[\n * { label: 'Mon', value: 12 },\n * { label: 'Tue', value: 18, color: 'var(--wr-color-success)' },\n * { label: 'Wed', value: 9 }\n * ]\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/bar-chart\n */\n@Component({\n selector: 'wr-bar-chart',\n templateUrl: './bar-chart.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-bar-chart' },\n})\nexport class WrBarChart {\n readonly data = input<readonly WrBarChartDatum[]>([]);\n\n /** Default bar colour when a datum has none. @default primary */\n readonly color = input<string>('var(--wr-color-primary)');\n\n /** Show value labels above each bar. @default true */\n readonly showValues = input(true, { transform: coerceBooleanProperty });\n\n /** Pixel height of the chart area. @default 200 */\n readonly height = input(200, { transform: (v: unknown): number => Math.max(40, coerceNumberProperty(v, 200)) });\n\n /** Explicit max value. `0` = auto. @default 0 */\n readonly max = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /**\n * Show a tooltip with the bar's label and value on hover. Pointer only — the\n * column's accessible name already carries both. @default true\n */\n readonly tooltip = input(true, { transform: coerceBooleanProperty });\n\n private readonly barEls = viewChildren<ElementRef<HTMLElement>>('barEl');\n\n /**\n * A datum's value, with anything non-finite read as `0`.\n *\n * One `NaN` used to take out the whole chart rather than its own bar:\n * `Math.max(...values)` is `NaN` as soon as one value is, so the scale fell\n * back to `1` and every OTHER bar was asked for a height of `value * 100`%.\n * The bad bar also printed the literal text `NaN` above itself.\n */\n private static value(raw: number): number {\n return Number.isFinite(raw) ? raw : 0;\n }\n\n protected readonly resolvedMax = computed(() => {\n const explicit = this.max();\n if (explicit > 0) return explicit;\n const data = this.data();\n if (data.length === 0) return 1;\n const top = Math.max(...data.map(d => WrBarChart.value(d.value)), 0);\n return top > 0 ? top : 1;\n });\n\n protected readonly bars = computed(() =>\n this.data().map(d => {\n const value = WrBarChart.value(d.value);\n return {\n label: d.label,\n value,\n color: d.color ?? this.color(),\n heightPercent: (Math.max(0, value) / this.resolvedMax()) * 100,\n /**\n * The bar's accessible name. The label and the value sit in separate\n * rows, so a screen reader read every number and then every label with\n * nothing tying the two together — and with `showValues` off the\n * numbers were not in the accessible tree at all.\n */\n name: `${d.label}: ${value}`,\n };\n })\n );\n\n /**\n * One tooltip for the whole chart, with the value exactly as it is printed above\n * the bar. It points at the top edge of the BAR — above it, below it when there is\n * no room — rather than at the column, which is the full height of the plot and\n * would put the chip above every bar alike. A bar with nothing to draw (zero, a\n * negative value) keeps its 1px floor on the baseline, so its chip points there.\n *\n * The bar is the anchor element itself, so an open chip follows it through the\n * height transition a data change starts.\n */\n protected readonly tip = useChartTooltip(this.tooltip, index => {\n const bar = this.bars()[index];\n const anchor = this.barEls()[index]?.nativeElement;\n if (!bar || !anchor) return null;\n return { datum: { label: bar.label, value: String(bar.value), color: bar.color }, anchor };\n });\n}\n\nexport type { WrBarChartDatum } from './interfaces';\n","<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\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-blur-text-testing.mjs","sources":["../../../projects/lib/blur-text/testing/wr-blur-text-harness.ts","../../../projects/lib/blur-text/testing/ngwr-blur-text-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, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrBlurTextHarnessFilters } from './interfaces';\n\n/** The animated pieces — one per word or per character, whitespace excluded. */\nconst PIECE = '.wr-blur-text__piece';\n\n/** The whitespace runs between them, rendered but never staggered. */\nconst SPACE = '.wr-blur-text__space';\n\n/** The readable copy of the string, carried once and hidden with CSS. */\nconst SR_ONLY = '.wr-blur-text__sr-only';\n\n/**\n * An inline `opacity` declaration — the one property the reveal writes on every path.\n *\n * It is staged before the tween starts and re-committed when it finishes, so its\n * presence is the durable evidence that the component touched a piece at all. Matched\n * off the `style` ATTRIBUTE rather than through `getCssValue`, which would answer from\n * a computed style and report `1` for a piece nobody ever wrote to.\n */\nconst INLINE_OPACITY = /(?:^|;)\\s*opacity\\s*:/;\n\n/** Exactly what the element holds — untrimmed, unlike `TestElement.text()`. */\nasync function rawText(element: TestElement): Promise<string> {\n return (await element.getProperty<string | null>('textContent')) ?? '';\n}\n\n/**\n * Test harness for `<wr-blur-text>`.\n *\n * **Most of what this component does cannot be read from a test, and the methods that\n * are missing are missing on purpose.** The reveal is a Web Animations tween per piece\n * — blur, vertical offset and opacity, staggered — started by an `IntersectionObserver`.\n * A unit test has no layout, no compositor and no WAAPI, so there is no\n * `getOpacity()`, no `getBlurRadius()` and no `getOffset()`: each would report the\n * staged from-state forever in jsdom, and in a real browser it would report the inline\n * value while the compositor paints a different one. There is no `isAnimating()` and no\n * `whenAnimationComplete()` either — nothing in the DOM says a tween is running, and\n * completion is the `animationComplete` OUTPUT, which the host binds. Nor is there an\n * `enterViewport()` / `scrollIntoView()`: the trigger is an observer, which a\n * `TestElement` cannot reach, and a method that pretended to scroll would do nothing in\n * jsdom and something untimed in a browser. The inputs that shape the tween —\n * `direction`, `delay`, `stepDuration`, `easing` — never reach the DOM at all; they live\n * in a keyframe array and an effect timing, and neither is a document.\n *\n * **What is real is the split and the accessible layer, and they are two questions.**\n * The string is carried once in a visually-hidden span, and every animated piece is\n * `aria-hidden` so a screen reader does not spell the text out letter by letter — so\n * {@link getAccessibleText} is what is heard and {@link getPieces} is what is drawn, and\n * a spec that only asserted the second would pass on a component that announces\n * `W. e. l. c. o. m. e.` {@link getRenderedText} closes the loop: the split has to be\n * lossless, and comparing it against the accessible copy is what catches a dropped space\n * or a halved surrogate pair. (Whether the hidden copy is really off-screen is CSS —\n * `clip-path` and a 1px box — and a test running no stylesheets cannot tell a correctly\n * hidden span from one printing the string twice, so nothing here claims to.)\n *\n * **{@link hasStagedMotion} is the one thing about the tween worth pinning**, and it\n * reports that the pieces were taken over rather than that anything is moving. Under\n * reduced motion the component returns before touching them, which is the promise: a\n * reader who asked for less motion must never be left with text staged at `opacity: 0`\n * by an animation that then never runs.\n *\n * Driving the reveal is the spec's job, since jsdom supplies neither half of it:\n *\n * ```ts\n * // Before TestBed.createComponent — keep the callbacks so the spec can fire them.\n * vi.stubGlobal('IntersectionObserver', StubObserver);\n * // Otherwise `el.animate(...)` throws mid-stagger and leaves the DOM half-staged.\n * Reflect.set(Element.prototype, 'animate', () => ({ cancel: () => undefined, onfinish: null }));\n * ```\n *\n * @example\n * ```ts\n * const text = await loader.getHarness(WrBlurTextHarness.with({ text: 'Welcome to ngwr' }));\n *\n * expect(await text.getPieces()).toEqual(['Welcome', 'to', 'ngwr']);\n * expect(await text.getRenderedText()).toBe(await text.getAccessibleText());\n * expect(await text.isTextHidden()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBlurTextHarness extends ComponentHarness {\n static hostSelector = 'wr-blur-text';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBlurTextHarnessFilters = {}): HarnessPredicate<WrBlurTextHarness> {\n return new HarnessPredicate(WrBlurTextHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getAccessibleText(), text)\n )\n .addOption('pieceCount', options.pieceCount, async (harness, count) => (await harness.getPieceCount()) === count);\n }\n\n private readonly pieces = this.locatorForAll(PIECE);\n private readonly spaces = this.locatorForAll(SPACE);\n\n /**\n * The whole string, as the one readable copy holds it.\n *\n * The only thing a screen reader gets, since every drawn piece is out of the tree —\n * lose this span and the component becomes a nameless pile of hidden spans with no\n * other symptom anywhere in the DOM.\n *\n * Untrimmed, deliberately. Leading and trailing whitespace is content the split has to\n * carry through, and {@link getRenderedText} is meant to compare equal to this; a\n * trimmed read on one side of that comparison would quietly forgive the case it exists\n * to catch.\n */\n async getAccessibleText(): Promise<string> {\n return rawText(await this.locatorFor(SR_ONLY)());\n }\n\n /**\n * The animated pieces, in document order — one per word or one per character.\n *\n * This is the split the stagger runs over: `['Hi', 'there']` for `animateBy=\"words\"`\n * against `['H', 'i', 't', 'h', 'e', 'r', 'e']` for `\"chars\"`, and `['a', '🚀']` for a\n * surrogate pair the code-point split kept whole rather than halved into two broken\n * glyphs. Whitespace is not here — it is rendered separately and never staggered, so\n * see {@link getSpaceCount}.\n */\n async getPieces(): Promise<string[]> {\n return Promise.all((await this.pieces()).map(rawText));\n }\n\n /**\n * How many pieces the stagger runs over.\n *\n * `delay` × this is the whole length of the effect, and it is the only number about\n * the animation that exists in the document.\n */\n async getPieceCount(): Promise<number> {\n return (await this.pieces()).length;\n }\n\n /**\n * How many whitespace runs sit between the pieces.\n *\n * Worth a method of its own because whitespace is deliberately NOT a staggered piece:\n * a regression that reclassified spaces as pieces would leave {@link getRenderedText}\n * identical, change the stagger count and the rhythm of the reveal, and show up\n * nowhere else.\n */\n async getSpaceCount(): Promise<number> {\n return (await this.spaces()).length;\n }\n\n /**\n * Everything the component drew, pieces and spaces together, in document order.\n *\n * The assertion this exists for is `getRenderedText() === getAccessibleText()`: the\n * split must be lossless. A swallowed space, a re-split that dropped a character, or a\n * surrogate pair cut in half all leave the accessible copy untouched and are invisible\n * from anywhere else.\n */\n async getRenderedText(): Promise<string> {\n const parts = await this.locatorForAll(`${PIECE}, ${SPACE}`)();\n return (await Promise.all(parts.map(rawText))).join('');\n }\n\n /**\n * Whether every drawn piece is out of the accessibility tree.\n *\n * The defect this component exists to avoid is a screen reader reading\n * `W. e. l. c. o. m. e.`, and one piece missing `aria-hidden` is enough to bring it\n * back — so this is all-or-nothing, and across both element kinds: a stray readable\n * space is heard as a pause in the middle of a word.\n *\n * Throws when the split produced nothing at all. An empty run of elements satisfies\n * \"every one of them is hidden\" without anything being hidden, which is the answer a\n * component that stopped rendering its pieces would also give.\n */\n async isTextHidden(): Promise<boolean> {\n const parts = await this.locatorForAll(`${PIECE}, ${SPACE}`)();\n\n if (parts.length === 0) {\n throw new Error(\n 'WrBlurTextHarness.isTextHidden(): this <wr-blur-text> drew no pieces, so there is nothing to be ' +\n 'hidden from a screen reader and \"true\" would be a vacuous answer — the same one a component that ' +\n 'stopped splitting its text would give. Check `text` is set, or ask getPieceCount().'\n );\n }\n\n const hidden = await Promise.all(parts.map(part => part.getAttribute('aria-hidden')));\n return hidden.every(value => value === 'true');\n }\n\n /**\n * Whether the reveal has taken the pieces over — NOT whether anything is animating.\n *\n * Read off the inline `style` attribute the component writes itself, so it stays\n * `true` after the reveal finishes: the tween commits its end state back to the same\n * inline properties on `onfinish`. Anyone reaching for `isAnimating()` wants a frame,\n * and there are no frames here.\n *\n * `false` is the answer worth asserting. It means no piece was written to at all,\n * which is both the pre-trigger state and the reduced-motion promise — the component\n * returns before staging anything, so a reader who asked for less motion cannot be\n * left staring at text parked at `opacity: 0` by an animation that never runs.\n *\n * ANY piece counts, not all of them, and that is the conservative reading rather than\n * a loose one: a stagger that stages the first piece and then throws leaves exactly one\n * piece invisible, which is a real failure and one jsdom reproduces whenever\n * `Element.prototype.animate` is missing.\n */\n async hasStagedMotion(): Promise<boolean> {\n const styles = await Promise.all((await this.pieces()).map(piece => piece.getAttribute('style')));\n return styles.some(style => INLINE_OPACITY.test(style ?? ''));\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-blur-text-testing.mjs","sources":["../../../projects/lib/blur-text/testing/wr-blur-text-harness.ts","../../../projects/lib/blur-text/testing/ngwr-blur-text-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, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrBlurTextHarnessFilters } from './interfaces';\n\n/** The animated pieces — one per word or per character, whitespace excluded. */\nconst PIECE = '.wr-blur-text__piece';\n\n/** The whitespace runs between them, rendered but never staggered. */\nconst SPACE = '.wr-blur-text__space';\n\n/** The readable copy of the string, carried once and hidden with CSS. */\nconst SR_ONLY = '.wr-blur-text__sr-only';\n\n/**\n * An inline `opacity` declaration — the one property the reveal writes on every path.\n *\n * It is staged before the tween starts and re-committed when it finishes, so its\n * presence is the durable evidence that the component touched a piece at all. Matched\n * off the `style` ATTRIBUTE rather than through `getCssValue`, which would answer from\n * a computed style and report `1` for a piece nobody ever wrote to.\n */\nconst INLINE_OPACITY = /(?:^|;)\\s*opacity\\s*:/;\n\n/** Exactly what the element holds — untrimmed, unlike `TestElement.text()`. */\nasync function rawText(element: TestElement): Promise<string> {\n return (await element.getProperty<string | null>('textContent')) ?? '';\n}\n\n/**\n * Test harness for `<wr-blur-text>`.\n *\n * **Most of what this component does cannot be read from a test, and the methods that\n * are missing are missing on purpose.** The reveal is a Web Animations tween per piece\n * — blur, vertical offset and opacity, staggered — started by an `IntersectionObserver`.\n * A unit test has no layout, no compositor and no WAAPI, so there is no\n * `getOpacity()`, no `getBlurRadius()` and no `getOffset()`: each would report the\n * staged from-state forever in jsdom, and in a real browser it would report the inline\n * value while the compositor paints a different one. There is no `isAnimating()` and no\n * `whenAnimationComplete()` either — nothing in the DOM says a tween is running, and\n * completion is the `animationComplete` OUTPUT, which the host binds. Nor is there an\n * `enterViewport()` / `scrollIntoView()`: the trigger is an observer, which a\n * `TestElement` cannot reach, and a method that pretended to scroll would do nothing in\n * jsdom and something untimed in a browser. The inputs that shape the tween —\n * `direction`, `delay`, `stepDuration`, `easing` — never reach the DOM at all; they live\n * in a keyframe array and an effect timing, and neither is a document.\n *\n * **What is real is the split and the accessible layer, and they are two questions.**\n * The string is carried once in a visually-hidden span, and every animated piece is\n * `aria-hidden` so a screen reader does not spell the text out letter by letter — so\n * {@link getAccessibleText} is what is heard and {@link getPieces} is what is drawn, and\n * a spec that only asserted the second would pass on a component that announces\n * `W. e. l. c. o. m. e.` {@link getRenderedText} closes the loop: the split has to be\n * lossless, and comparing it against the accessible copy is what catches a dropped space\n * or a halved surrogate pair. (Whether the hidden copy is really off-screen is CSS —\n * `clip-path` and a 1px box — and a test running no stylesheets cannot tell a correctly\n * hidden span from one printing the string twice, so nothing here claims to.)\n *\n * **{@link hasStagedMotion} is the one thing about the tween worth pinning**, and it\n * reports that the pieces were taken over rather than that anything is moving. Under\n * reduced motion the component returns before touching them, which is the promise: a\n * reader who asked for less motion must never be left with text staged at `opacity: 0`\n * by an animation that then never runs.\n *\n * Driving the reveal is the spec's job, since jsdom supplies neither half of it:\n *\n * ```ts\n * // Before TestBed.createComponent — keep the callbacks so the spec can fire them.\n * vi.stubGlobal('IntersectionObserver', StubObserver);\n * // Otherwise `el.animate(...)` throws mid-stagger and leaves the DOM half-staged.\n * Reflect.set(Element.prototype, 'animate', () => ({ cancel: () => undefined, onfinish: null }));\n * ```\n *\n * @example\n * ```ts\n * const text = await loader.getHarness(WrBlurTextHarness.with({ text: 'Welcome to ngwr' }));\n *\n * expect(await text.getPieces()).toEqual(['Welcome', 'to', 'ngwr']);\n * expect(await text.getRenderedText()).toBe(await text.getAccessibleText());\n * expect(await text.isTextHidden()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBlurTextHarness extends ComponentHarness {\n static hostSelector = 'wr-blur-text';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBlurTextHarnessFilters = {}): HarnessPredicate<WrBlurTextHarness> {\n return new HarnessPredicate(WrBlurTextHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getAccessibleText(), text)\n )\n .addOption('pieceCount', options.pieceCount, async (harness, count) => (await harness.getPieceCount()) === count);\n }\n\n private readonly pieces = this.locatorForAll(PIECE);\n private readonly spaces = this.locatorForAll(SPACE);\n\n /**\n * The whole string, as the one readable copy holds it.\n *\n * The only thing a screen reader gets, since every drawn piece is out of the tree —\n * lose this span and the component becomes a nameless pile of hidden spans with no\n * other symptom anywhere in the DOM.\n *\n * Untrimmed, deliberately. Leading and trailing whitespace is content the split has to\n * carry through, and {@link getRenderedText} is meant to compare equal to this; a\n * trimmed read on one side of that comparison would quietly forgive the case it exists\n * to catch.\n */\n async getAccessibleText(): Promise<string> {\n return rawText(await this.locatorFor(SR_ONLY)());\n }\n\n /**\n * The animated pieces, in document order — one per word or one per character.\n *\n * This is the split the stagger runs over: `['Hi', 'there']` for `animateBy=\"words\"`\n * against `['H', 'i', 't', 'h', 'e', 'r', 'e']` for `\"chars\"`, and `['a', '🚀']` for a\n * surrogate pair the code-point split kept whole rather than halved into two broken\n * glyphs. Whitespace is not here — it is rendered separately and never staggered, so\n * see {@link getSpaceCount}.\n */\n async getPieces(): Promise<string[]> {\n return Promise.all((await this.pieces()).map(rawText));\n }\n\n /**\n * How many pieces the stagger runs over.\n *\n * `delay` × this is the whole length of the effect, and it is the only number about\n * the animation that exists in the document.\n */\n async getPieceCount(): Promise<number> {\n return (await this.pieces()).length;\n }\n\n /**\n * How many whitespace runs sit between the pieces.\n *\n * Worth a method of its own because whitespace is deliberately NOT a staggered piece:\n * a regression that reclassified spaces as pieces would leave {@link getRenderedText}\n * identical, change the stagger count and the rhythm of the reveal, and show up\n * nowhere else.\n */\n async getSpaceCount(): Promise<number> {\n return (await this.spaces()).length;\n }\n\n /**\n * Everything the component drew, pieces and spaces together, in document order.\n *\n * The assertion this exists for is `getRenderedText() === getAccessibleText()`: the\n * split must be lossless. A swallowed space, a re-split that dropped a character, or a\n * surrogate pair cut in half all leave the accessible copy untouched and are invisible\n * from anywhere else.\n */\n async getRenderedText(): Promise<string> {\n const parts = await this.locatorForAll(`${PIECE}, ${SPACE}`)();\n return (await Promise.all(parts.map(rawText))).join('');\n }\n\n /**\n * Whether every drawn piece is out of the accessibility tree.\n *\n * The defect this component exists to avoid is a screen reader reading\n * `W. e. l. c. o. m. e.`, and one piece missing `aria-hidden` is enough to bring it\n * back — so this is all-or-nothing, and across both element kinds: a stray readable\n * space is heard as a pause in the middle of a word.\n *\n * Throws when the split produced nothing at all. An empty run of elements satisfies\n * \"every one of them is hidden\" without anything being hidden, which is the answer a\n * component that stopped rendering its pieces would also give.\n */\n async isTextHidden(): Promise<boolean> {\n const parts = await this.locatorForAll(`${PIECE}, ${SPACE}`)();\n\n if (parts.length === 0) {\n throw new Error(\n 'WrBlurTextHarness.isTextHidden(): this <wr-blur-text> drew no pieces, so there is nothing to be ' +\n 'hidden from a screen reader and \"true\" would be a vacuous answer — the same one a component that ' +\n 'stopped splitting its text would give. Check `text` is set, or ask getPieceCount().'\n );\n }\n\n const hidden = await Promise.all(parts.map(part => part.getAttribute('aria-hidden')));\n return hidden.every(value => value === 'true');\n }\n\n /**\n * Whether the reveal has taken the pieces over — NOT whether anything is animating.\n *\n * Read off the inline `style` attribute the component writes itself, so it stays\n * `true` after the reveal finishes: the tween commits its end state back to the same\n * inline properties on `onfinish`. Anyone reaching for `isAnimating()` wants a frame,\n * and there are no frames here.\n *\n * `false` is the answer worth asserting. It means no piece was written to at all,\n * which is both the pre-trigger state and the reduced-motion promise — the component\n * returns before staging anything, so a reader who asked for less motion cannot be\n * left staring at text parked at `opacity: 0` by an animation that never runs.\n *\n * ANY piece counts, not all of them, and that is the conservative reading rather than\n * a loose one: a stagger that stages the first piece and then throws leaves exactly one\n * piece invisible, which is a real failure and one jsdom reproduces whenever\n * `Element.prototype.animate` is missing.\n */\n async hasStagedMotion(): Promise<boolean> {\n const styles = await Promise.all((await this.pieces()).map(piece => piece.getAttribute('style')));\n return styles.some(style => INLINE_OPACITY.test(style ?? ''));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -190,10 +190,10 @@ class WrBlurText {
190
190
  };
191
191
  });
192
192
  }
193
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBlurText, deps: [], target: i0.ɵɵFactoryTarget.Component });
194
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrBlurText, isStandalone: true, selector: "wr-blur-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, animateBy: { classPropertyName: "animateBy", publicName: "animateBy", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, stepDuration: { classPropertyName: "stepDuration", publicName: "stepDuration", isSignal: true, isRequired: false, transformFunction: null }, easing: { classPropertyName: "easing", publicName: "easing", isSignal: true, isRequired: false, transformFunction: null }, threshold: { classPropertyName: "threshold", publicName: "threshold", isSignal: true, isRequired: false, transformFunction: null }, rootMargin: { classPropertyName: "rootMargin", publicName: "rootMargin", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { animationComplete: "animationComplete" }, host: { classAttribute: "wr-blur-text" }, ngImport: i0, template: "<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-blur-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-blur-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-blur-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n", styles: [".wr-blur-text{display:inline-flex;flex-wrap:wrap}.wr-blur-text__sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.wr-blur-text__piece{display:inline-block;will-change:transform,filter,opacity}.wr-blur-text__space{display:inline;white-space:pre}\n"], encapsulation: i0.ViewEncapsulation.None });
193
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrBlurText, deps: [], target: i0.ɵɵFactoryTarget.Component });
194
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrBlurText, isStandalone: true, selector: "wr-blur-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, animateBy: { classPropertyName: "animateBy", publicName: "animateBy", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, stepDuration: { classPropertyName: "stepDuration", publicName: "stepDuration", isSignal: true, isRequired: false, transformFunction: null }, easing: { classPropertyName: "easing", publicName: "easing", isSignal: true, isRequired: false, transformFunction: null }, threshold: { classPropertyName: "threshold", publicName: "threshold", isSignal: true, isRequired: false, transformFunction: null }, rootMargin: { classPropertyName: "rootMargin", publicName: "rootMargin", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { animationComplete: "animationComplete" }, host: { classAttribute: "wr-blur-text" }, ngImport: i0, template: "<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-blur-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-blur-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-blur-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n", styles: [".wr-blur-text{display:inline-flex;flex-wrap:wrap}.wr-blur-text__sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.wr-blur-text__piece{display:inline-block;will-change:transform,filter,opacity}.wr-blur-text__space{display:inline;white-space:pre}\n"], encapsulation: i0.ViewEncapsulation.None });
195
195
  }
196
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBlurText, decorators: [{
196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrBlurText, decorators: [{
197
197
  type: Component,
198
198
  args: [{ selector: 'wr-blur-text', encapsulation: ViewEncapsulation.None, host: { class: 'wr-blur-text' }, template: "<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-blur-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-blur-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-blur-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n", styles: [".wr-blur-text{display:inline-flex;flex-wrap:wrap}.wr-blur-text__sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.wr-blur-text__piece{display:inline-block;will-change:transform,filter,opacity}.wr-blur-text__space{display:inline;white-space:pre}\n"] }]
199
199
  }], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], animateBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "animateBy", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], stepDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepDuration", required: false }] }], easing: [{ type: i0.Input, args: [{ isSignal: true, alias: "easing", required: false }] }], threshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "threshold", required: false }] }], rootMargin: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootMargin", required: false }] }], animationComplete: [{ type: i0.Output, args: ["animationComplete"] }] } });
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-blur-text.mjs","sources":["../../../projects/lib/blur-text/blur-text.ts","../../../projects/lib/blur-text/blur-text.html","../../../projects/lib/blur-text/ngwr-blur-text.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 * Angular adaptation of the BlurText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/blur-text\n *\n * The reactbits version uses `motion/react` (framer-motion successor).\n * This port is dependency-free — uses the Web Animations API for the\n * three-keyframe blur-and-fade-in, triggered by `IntersectionObserver`.\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrBlurTextDirection, WrBlurTextUnit } from './interfaces';\n\ntype Piece = { readonly kind: 'piece'; readonly text: string } | { readonly kind: 'space'; readonly text: string };\n\nfunction splitPieces(text: string, unit: WrBlurTextUnit): readonly Piece[] {\n if (!text) return [];\n if (unit === 'words') {\n return text.split(/(\\s+)/).flatMap<Piece>(seg => {\n if (seg.length === 0) return [];\n if (/^\\s+$/.test(seg)) return [{ kind: 'space', text: seg }];\n return [{ kind: 'piece', text: seg }];\n });\n }\n return [...text].map<Piece>(ch => (/\\s/.test(ch) ? { kind: 'space', text: ch } : { kind: 'piece', text: ch }));\n}\n\n/**\n * Reveals text by splitting it into chars or words and animating each\n * piece in from a blurred / offset state through a brief mid-step into\n * the steady state. Triggers when the host enters the viewport.\n *\n * @example\n * ```html\n * <wr-blur-text\n * text=\"Welcome to ngwr\"\n * animateBy=\"words\"\n * direction=\"top\"\n * [delay]=\"120\"\n * [stepDuration]=\"0.4\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/blur-text\n */\n@Component({\n selector: 'wr-blur-text',\n templateUrl: './blur-text.html',\n styleUrl: './blur-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-blur-text' },\n})\nexport class WrBlurText {\n /** Text to animate. Required. */\n readonly text = input.required<string>();\n\n /** Split granularity. @default 'words' */\n readonly animateBy = input<WrBlurTextUnit>('words');\n\n /** Entry direction. `'top'` slides down into place; `'bottom'` slides up. @default 'top' */\n readonly direction = input<WrBlurTextDirection>('top');\n\n /** Per-piece stagger in ms. @default 200 */\n readonly delay = input(200, { transform: numAttr(200) });\n\n /** Duration of each keyframe step in seconds (total = 2 × stepDuration). @default 0.35 */\n readonly stepDuration = input(0.35, { transform: numAttr(0.35) });\n\n /** CSS easing function. @default 'linear' (matches reactbits' identity easing) */\n readonly easing = input('linear');\n\n /** `IntersectionObserver.threshold` (0..1). @default 0.1 */\n readonly threshold = input(0.1, { transform: numAttr(0.1) });\n\n /** `IntersectionObserver.rootMargin`. @default '0px' */\n readonly rootMargin = input('0px');\n\n /** Emitted once all pieces finish animating. */\n readonly animationComplete = output<void>();\n\n protected readonly pieces = computed(() => splitPieces(this.text(), this.animateBy()));\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private hasAnimated = false;\n\n /**\n * The ONE observer, replaced rather than stacked.\n *\n * `startObserver()` used to `new IntersectionObserver(…)` and register its own\n * `destroyRef.onDestroy` every time, and the effect below calls it on every\n * `text` change after setting `hasAnimated = false` — so the guard at the top\n * of `startObserver` never blocked that path. An off-screen element (the only\n * case where the observer does not immediately disconnect itself) therefore\n * leaked one live observer AND one destroy callback per change, all of them\n * watching the same host and all of them able to fire `animate()`.\n */\n private observer: IntersectionObserver | null = null;\n\n /**\n * The effect below reaches `startObserver()` through a `queueMicrotask`, so\n * the component can be destroyed between the two. The destroy hook runs first\n * and disconnects whatever exists at that moment — then the continuation\n * builds a FRESH observer on a host Angular has already detached, and nothing\n * is left to disconnect it. Same hazard, and same flag, as `wr-split-text`.\n */\n private destroyed = false;\n\n constructor() {\n if (!this.isBrowser) return;\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.observer?.disconnect();\n });\n afterNextRender(() => this.startObserver());\n effect(() => {\n this.text();\n this.animateBy();\n this.hasAnimated = false;\n queueMicrotask(() => this.startObserver());\n });\n }\n\n private startObserver(): void {\n if (this.destroyed || this.hasAnimated) return;\n const host = this.host.nativeElement;\n this.observer?.disconnect();\n const io = new IntersectionObserver(\n (entries, obs) => {\n for (const e of entries) {\n if (e.isIntersecting) {\n obs.disconnect();\n this.animate();\n break;\n }\n }\n },\n { threshold: this.threshold(), rootMargin: this.rootMargin() }\n );\n this.observer = io;\n io.observe(host);\n }\n\n private animate(): void {\n if (this.hasAnimated) return;\n this.hasAnimated = true;\n\n // Reduced motion: pieces already render clear and in place — skip\n // the blur tween and report completion right away.\n if (this.platform.prefersReducedMotion()) {\n this.animationComplete.emit();\n return;\n }\n\n const targets = this.host.nativeElement.querySelectorAll<HTMLElement>('.wr-blur-text__piece');\n if (targets.length === 0) {\n this.animationComplete.emit();\n return;\n }\n\n const dir = this.direction();\n const stepMs = this.stepDuration() * 1000;\n const totalMs = stepMs * 2;\n const stagger = this.delay();\n const easing = this.easing();\n\n // Three-keyframe shape per reactbits:\n // 0 → fully blurred, offset, transparent\n // 50% → half-blur, half-opacity, small reverse offset\n // 100% → clear, opaque, at rest\n const startY = dir === 'top' ? -50 : 50;\n const midY = dir === 'top' ? 5 : -5;\n const keyframes: Keyframe[] = [\n { opacity: 0, filter: 'blur(10px)', transform: `translate3d(0, ${startY}px, 0)` },\n { opacity: 0.5, filter: 'blur(5px)', transform: `translate3d(0, ${midY}px, 0)`, offset: 0.5 },\n { opacity: 1, filter: 'blur(0px)', transform: 'translate3d(0, 0, 0)' },\n ];\n\n let remaining = targets.length;\n targets.forEach((el, i) => {\n el.style.opacity = '0';\n el.style.filter = 'blur(10px)';\n el.style.transform = `translate3d(0, ${startY}px, 0)`;\n const animation = el.animate(keyframes, {\n duration: totalMs,\n delay: i * stagger,\n easing,\n fill: 'forwards',\n });\n animation.onfinish = (): void => {\n el.style.opacity = '1';\n el.style.filter = 'blur(0px)';\n el.style.transform = 'translate3d(0, 0, 0)';\n try {\n animation.cancel();\n } catch {\n /* noop */\n }\n remaining -= 1;\n if (remaining === 0) this.animationComplete.emit();\n };\n });\n }\n}\n\nexport type { WrBlurTextDirection, WrBlurTextUnit } from './interfaces';\n","<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-blur-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-blur-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-blur-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\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-blur-text.mjs","sources":["../../../projects/lib/blur-text/blur-text.ts","../../../projects/lib/blur-text/blur-text.html","../../../projects/lib/blur-text/ngwr-blur-text.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 * Angular adaptation of the BlurText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/blur-text\n *\n * The reactbits version uses `motion/react` (framer-motion successor).\n * This port is dependency-free — uses the Web Animations API for the\n * three-keyframe blur-and-fade-in, triggered by `IntersectionObserver`.\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrBlurTextDirection, WrBlurTextUnit } from './interfaces';\n\ntype Piece = { readonly kind: 'piece'; readonly text: string } | { readonly kind: 'space'; readonly text: string };\n\nfunction splitPieces(text: string, unit: WrBlurTextUnit): readonly Piece[] {\n if (!text) return [];\n if (unit === 'words') {\n return text.split(/(\\s+)/).flatMap<Piece>(seg => {\n if (seg.length === 0) return [];\n if (/^\\s+$/.test(seg)) return [{ kind: 'space', text: seg }];\n return [{ kind: 'piece', text: seg }];\n });\n }\n return [...text].map<Piece>(ch => (/\\s/.test(ch) ? { kind: 'space', text: ch } : { kind: 'piece', text: ch }));\n}\n\n/**\n * Reveals text by splitting it into chars or words and animating each\n * piece in from a blurred / offset state through a brief mid-step into\n * the steady state. Triggers when the host enters the viewport.\n *\n * @example\n * ```html\n * <wr-blur-text\n * text=\"Welcome to ngwr\"\n * animateBy=\"words\"\n * direction=\"top\"\n * [delay]=\"120\"\n * [stepDuration]=\"0.4\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/blur-text\n */\n@Component({\n selector: 'wr-blur-text',\n templateUrl: './blur-text.html',\n styleUrl: './blur-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-blur-text' },\n})\nexport class WrBlurText {\n /** Text to animate. Required. */\n readonly text = input.required<string>();\n\n /** Split granularity. @default 'words' */\n readonly animateBy = input<WrBlurTextUnit>('words');\n\n /** Entry direction. `'top'` slides down into place; `'bottom'` slides up. @default 'top' */\n readonly direction = input<WrBlurTextDirection>('top');\n\n /** Per-piece stagger in ms. @default 200 */\n readonly delay = input(200, { transform: numAttr(200) });\n\n /** Duration of each keyframe step in seconds (total = 2 × stepDuration). @default 0.35 */\n readonly stepDuration = input(0.35, { transform: numAttr(0.35) });\n\n /** CSS easing function. @default 'linear' (matches reactbits' identity easing) */\n readonly easing = input('linear');\n\n /** `IntersectionObserver.threshold` (0..1). @default 0.1 */\n readonly threshold = input(0.1, { transform: numAttr(0.1) });\n\n /** `IntersectionObserver.rootMargin`. @default '0px' */\n readonly rootMargin = input('0px');\n\n /** Emitted once all pieces finish animating. */\n readonly animationComplete = output<void>();\n\n protected readonly pieces = computed(() => splitPieces(this.text(), this.animateBy()));\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private hasAnimated = false;\n\n /**\n * The ONE observer, replaced rather than stacked.\n *\n * `startObserver()` used to `new IntersectionObserver(…)` and register its own\n * `destroyRef.onDestroy` every time, and the effect below calls it on every\n * `text` change after setting `hasAnimated = false` — so the guard at the top\n * of `startObserver` never blocked that path. An off-screen element (the only\n * case where the observer does not immediately disconnect itself) therefore\n * leaked one live observer AND one destroy callback per change, all of them\n * watching the same host and all of them able to fire `animate()`.\n */\n private observer: IntersectionObserver | null = null;\n\n /**\n * The effect below reaches `startObserver()` through a `queueMicrotask`, so\n * the component can be destroyed between the two. The destroy hook runs first\n * and disconnects whatever exists at that moment — then the continuation\n * builds a FRESH observer on a host Angular has already detached, and nothing\n * is left to disconnect it. Same hazard, and same flag, as `wr-split-text`.\n */\n private destroyed = false;\n\n constructor() {\n if (!this.isBrowser) return;\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.observer?.disconnect();\n });\n afterNextRender(() => this.startObserver());\n effect(() => {\n this.text();\n this.animateBy();\n this.hasAnimated = false;\n queueMicrotask(() => this.startObserver());\n });\n }\n\n private startObserver(): void {\n if (this.destroyed || this.hasAnimated) return;\n const host = this.host.nativeElement;\n this.observer?.disconnect();\n const io = new IntersectionObserver(\n (entries, obs) => {\n for (const e of entries) {\n if (e.isIntersecting) {\n obs.disconnect();\n this.animate();\n break;\n }\n }\n },\n { threshold: this.threshold(), rootMargin: this.rootMargin() }\n );\n this.observer = io;\n io.observe(host);\n }\n\n private animate(): void {\n if (this.hasAnimated) return;\n this.hasAnimated = true;\n\n // Reduced motion: pieces already render clear and in place — skip\n // the blur tween and report completion right away.\n if (this.platform.prefersReducedMotion()) {\n this.animationComplete.emit();\n return;\n }\n\n const targets = this.host.nativeElement.querySelectorAll<HTMLElement>('.wr-blur-text__piece');\n if (targets.length === 0) {\n this.animationComplete.emit();\n return;\n }\n\n const dir = this.direction();\n const stepMs = this.stepDuration() * 1000;\n const totalMs = stepMs * 2;\n const stagger = this.delay();\n const easing = this.easing();\n\n // Three-keyframe shape per reactbits:\n // 0 → fully blurred, offset, transparent\n // 50% → half-blur, half-opacity, small reverse offset\n // 100% → clear, opaque, at rest\n const startY = dir === 'top' ? -50 : 50;\n const midY = dir === 'top' ? 5 : -5;\n const keyframes: Keyframe[] = [\n { opacity: 0, filter: 'blur(10px)', transform: `translate3d(0, ${startY}px, 0)` },\n { opacity: 0.5, filter: 'blur(5px)', transform: `translate3d(0, ${midY}px, 0)`, offset: 0.5 },\n { opacity: 1, filter: 'blur(0px)', transform: 'translate3d(0, 0, 0)' },\n ];\n\n let remaining = targets.length;\n targets.forEach((el, i) => {\n el.style.opacity = '0';\n el.style.filter = 'blur(10px)';\n el.style.transform = `translate3d(0, ${startY}px, 0)`;\n const animation = el.animate(keyframes, {\n duration: totalMs,\n delay: i * stagger,\n easing,\n fill: 'forwards',\n });\n animation.onfinish = (): void => {\n el.style.opacity = '1';\n el.style.filter = 'blur(0px)';\n el.style.transform = 'translate3d(0, 0, 0)';\n try {\n animation.cancel();\n } catch {\n /* noop */\n }\n remaining -= 1;\n if (remaining === 0) this.animationComplete.emit();\n };\n });\n }\n}\n\nexport type { WrBlurTextDirection, WrBlurTextUnit } from './interfaces';\n","<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-blur-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-blur-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-blur-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\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-border-glow-testing.mjs","sources":["../../../projects/lib/border-glow/testing/wr-border-glow-harness.ts","../../../projects/lib/border-glow/testing/ngwr-border-glow-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 { WrBorderGlowHarnessFilters } from './interfaces';\n\n/** The seven halo steps, in the order the component writes them: 100%, 60%, 50%, 40%, 30%, 20%, 10%. */\nconst GLOW_VAR_SUFFIXES = ['', '-60', '-50', '-40', '-30', '-20', '-10'] as const;\n\n/** The seven mesh-gradient slots, spelled out rather than numbered — as the component names them. */\nconst GRADIENT_VAR_SLOTS = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'] as const;\n\n/** `hsl(40deg 80% 80% / 65%)` — the alpha, which is the only part `glowIntensity` moves. */\nconst GLOW_ALPHA = /\\/\\s*(-?[\\d.]+)%\\s*\\)\\s*$/;\n\n/** `radial-gradient(at 80% 55%, #c084fc 0px, transparent 50%)` — the colour, not the position. */\nconst RADIAL_GRADIENT_COLOR = /^radial-gradient\\(\\s*at\\s+[^,]+,\\s*(.+?)\\s+0px\\s*,/;\n\n/** `linear-gradient(#c084fc 0 100%)` — the flat base slice. */\nconst LINEAR_GRADIENT_COLOR = /^linear-gradient\\(\\s*(.+?)\\s+0\\s+100%\\s*\\)$/;\n\n/**\n * Split an inline `style` attribute into its declarations.\n *\n * Read from the attribute rather than through `getCssValue()` on purpose. jsdom echoes\n * an inline custom property back through `getComputedStyle`, so the computed read passes\n * here and would answer something else entirely in a browser — where the stylesheet's\n * own per-theme default resolves and \"the author left this to the theme\" quietly becomes\n * a colour. The attribute holds what the component itself wrote, and nothing else.\n */\nfunction parseInlineDeclarations(style: string): Map<string, string> {\n const declarations = new Map<string, string>();\n for (const declaration of style.split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n const name = declaration.slice(0, colon).trim();\n if (name !== '') declarations.set(name, declaration.slice(colon + 1).trim());\n }\n return declarations;\n}\n\n/**\n * Test harness for `<wr-border-glow>` — a card whose border lights up under the cursor.\n *\n * **Nearly all of this component is a bag of custom properties written onto the host,\n * and that is what the harness reads.** The paint itself — the conic mask, the stacked\n * `box-shadow` halo, the soft-light bloom — lives in the stylesheet, so a unit test can\n * never see it. What it CAN see is the arithmetic that feeds it, and that is where the\n * breakages are: the seven-step alpha ramp {@link getGlowAlphaRamp} derives from\n * `glowIntensity`, the `[0,1,2,0,1,2,1]` palette mapping in {@link getGradientColors}\n * with its clamp for short palettes, and the pointer maths behind\n * {@link getPointerAngle} / {@link getEdgeProximity}.\n *\n * **Every custom-property getter reads the inline declaration** — see\n * {@link parseInlineDeclarations} for why a computed read would be a different, and\n * wrong, question.\n *\n * **A colour getter answering `null` means the component has never written that\n * variable**, which is the contract when the input is unset: the stylesheet's per-theme\n * default takes over. Note that it does not un-write them either — setting `[glowColor]`\n * and then putting it back to `null` leaves the last value on the host — so `null` is\n * \"never set\", not \"not set now\".\n *\n * There is deliberately no `getGlowOpacity()`, `isGlowVisible()` or `isHovered()`: the\n * visible strength is a `calc()` in the stylesheet gated behind `:not(:hover)`, and\n * jsdom loads no stylesheet and matches no `:hover`, so any number would be invented.\n * There is no measured halo extent either — `.wr-border-glow__edge` is 0×0 in a test,\n * and {@link getGlowRadius} reads what the component asked for instead. And there is no\n * `getSweepProgress()` / `waitForSweep()`: the mount sweep is a four-second chain of\n * `requestAnimationFrame` tweens, so awaiting it either hangs the spec or samples one\n * frame and reports a value that was never settled. {@link isSweeping} answers the one\n * durable fact — that the sweep started.\n *\n * @example\n * ```ts\n * const card = await loader.getHarness(WrBorderGlowHarness.with({ text: /Hover me/ }));\n *\n * expect(await card.getBorderRadius()).toBe(28);\n * expect(await card.getPointerAngle()).toBeNull(); // nothing has pointed at it yet\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBorderGlowHarness extends ComponentHarness {\n static hostSelector = 'wr-border-glow';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBorderGlowHarnessFilters = {}): HarnessPredicate<WrBorderGlowHarness> {\n return new HarnessPredicate(WrBorderGlowHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getContentText(), text)\n )\n .addOption('sweeping', options.sweeping, async (harness, sweeping) => (await harness.isSweeping()) === sweeping);\n }\n\n private readonly edge = this.locatorFor('.wr-border-glow__edge');\n\n /** The corner radius the stylesheet is told to round to, in pixels. */\n async getBorderRadius(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-radius', 'getBorderRadius');\n }\n\n /** How far past the card edge the halo is told to reach, in pixels. */\n async getGlowRadius(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-padding', 'getGlowRadius');\n }\n\n /**\n * The width of the lit cone, as a share of the perimeter.\n *\n * Unitless by contract — the stylesheet multiplies it — so a `px` creeping in here is\n * a real regression that leaves the mask with a length where it wants a number.\n */\n async getConeSpread(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-cone-spread', 'getConeSpread');\n }\n\n /** How sharply the halo fades as the cursor leaves an edge; lower is a wider falloff. */\n async getEdgeSensitivity(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-edge-sensitivity', 'getEdgeSensitivity');\n }\n\n /** Strength of the interior soft-light bloom, again unitless. */\n async getFillOpacity(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-fill-opacity', 'getFillOpacity');\n }\n\n /**\n * The card fill the author asked for, or `null` when they left it to the theme.\n *\n * The distinction is the whole point: an unset `[backgroundColor]` means the surface\n * token decides, light or dark, and a card that starts declaring its own fill has\n * stopped following the theme.\n */\n async getBackgroundColor(): Promise<string | null> {\n return this.readVar('--wr-border-glow-bg');\n }\n\n /**\n * The halo colour at full strength, or `null` when the theme decides.\n *\n * Comes back as the `hsl(H deg S% L% / A%)` the component assembled from the bare\n * `'H S L'` input, so this also pins that assembly. Be aware the parse is forgiving to\n * a fault: anything that is not three bare numbers — a hex colour, `rgb(…)`, even\n * `hsl(40deg 80% 80%)` with its units — silently becomes the default amber, so a\n * plausible-looking value here does not mean the input was understood.\n */\n async getGlowColor(): Promise<string | null> {\n return this.readVar('--wr-border-glow-color');\n }\n\n /**\n * The seven halo alphas in percent, brightest first, or `null` when no glow colour\n * was ever set.\n *\n * This is the whole of `[glowIntensity]`: the steps are `[100, 60, 50, 40, 30, 20,\n * 10]` multiplied by the intensity and clamped at 100, so `0.5` gives\n * `[50, 30, 25, 20, 15, 10, 5]` and anything above 1 saturates from the top down. The\n * clamp is one-sided — a negative intensity produces negative percentages, which a\n * browser drops as invalid declarations and the halo vanishes — so the numbers are\n * returned as written rather than corrected, which is how a spec can see it.\n */\n async getGlowAlphaRamp(): Promise<number[] | null> {\n const declarations = await this.declarations();\n if (!declarations.has('--wr-border-glow-color')) return null;\n\n return GLOW_VAR_SUFFIXES.map(suffix => {\n const name = `--wr-border-glow-color${suffix}`;\n const raw = declarations.get(name);\n const alpha = raw === undefined ? null : GLOW_ALPHA.exec(raw);\n if (!alpha) {\n throw new Error(\n `WrBorderGlowHarness.getGlowAlphaRamp(): \\`${name}\\` reads \"${raw ?? '(absent)'}\". The seven halo steps ` +\n 'are written together from one glow colour, so a step that is missing or carries no ` / N%` alpha ' +\n 'means the ramp stopped being built — the halo would fade in bands the stylesheet cannot use.'\n );\n }\n return Number.parseFloat(alpha[1]);\n });\n }\n\n /**\n * The seven mesh-gradient colours, in slot order, or `null` when no palette was set.\n *\n * The slots do not map one-to-one onto the palette: they take colours\n * `[0, 1, 2, 0, 1, 2, 1]`, each index clamped to the last entry the palette actually\n * has. So a two-colour palette repeats the second colour rather than reading past the\n * end — which is the case worth a spec, since `undefined` in a `radial-gradient()`\n * kills the whole declaration silently.\n */\n async getGradientColors(): Promise<string[] | null> {\n const declarations = await this.declarations();\n if (!declarations.has('--wr-border-glow-gradient-one')) return null;\n\n return GRADIENT_VAR_SLOTS.map(slot => {\n const name = `--wr-border-glow-gradient-${slot}`;\n const raw = declarations.get(name);\n const colour = raw === undefined ? null : RADIAL_GRADIENT_COLOR.exec(raw);\n if (!colour) {\n throw new Error(\n `WrBorderGlowHarness.getGradientColors(): \\`${name}\\` reads \"${raw ?? '(absent)'}\", which is not the ` +\n '`radial-gradient(at X% Y%, <colour> 0px, …)` the component builds. All seven slots are written from ' +\n 'one palette, so one of them being different means the mapping broke, not the palette.'\n );\n }\n return colour[1];\n });\n }\n\n /**\n * The flat colour behind the mesh, or `null` when no palette was set.\n *\n * Always the palette's FIRST entry. Worth its own method because the base slice is the\n * one that shows where the gradients are transparent, and it drifting to the last\n * colour changes the card's whole cast without touching a gradient.\n */\n async getBaseGradientColor(): Promise<string | null> {\n const raw = await this.readVar('--wr-border-glow-gradient-base');\n if (raw === null) return null;\n\n const colour = LINEAR_GRADIENT_COLOR.exec(raw);\n if (!colour) {\n throw new Error(\n `WrBorderGlowHarness.getBaseGradientColor(): \\`--wr-border-glow-gradient-base\\` reads \"${raw}\", which is ` +\n 'not the `linear-gradient(<colour> 0 100%)` the component builds from the palette.'\n );\n }\n return colour[1];\n }\n\n /**\n * Where the cursor last sat, in degrees: `0` is straight up and the angle grows\n * clockwise. `null` until something has pointed at the card.\n *\n * `null` is a state of its own rather than a stand-in for zero — the stylesheet's own\n * default applies until the first pointer arrives, and dead centre legitimately\n * reports `0`, so a harness that folded the two together could not tell an untouched\n * card from one being pointed at from the middle.\n */\n async getPointerAngle(): Promise<number | null> {\n const raw = await this.readVar('--wr-border-glow-angle');\n return raw === null ? null : Number.parseFloat(raw);\n }\n\n /**\n * How close the cursor is to the perimeter: `0` at dead centre, `100` at the edge.\n * `null` until something has pointed at the card.\n *\n * On a diagonal the NEARER axis wins rather than the two averaging, so a cursor level\n * with the bottom edge reads 100 wherever it is along it.\n */\n async getEdgeProximity(): Promise<number | null> {\n const raw = await this.readVar('--wr-border-glow-edge-proximity');\n return raw === null ? null : Number.parseFloat(raw);\n }\n\n /**\n * Point at the card, in client coordinates.\n *\n * **Refuses on a card with no box, which in a unit test is every card.** The component\n * measures the pointer against its own centre, and with a 0×0 rect that maths\n * degenerates: the centre is the top-left corner, and `1 / Math.min(0, 0)` pins the\n * proximity at 100 for every position on the page. Both would come back as confident\n * numbers describing nothing. Give the host a box first, exactly as the component's own\n * spec does:\n *\n * ```ts\n * const card = fixture.nativeElement.querySelector('wr-border-glow');\n * card.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * A `pointermove` is dispatched on the host itself, so it needs no hit test and cannot\n * miss — the coordinates only ever reach the component's own arithmetic.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n const host = await this.host();\n const { width, height } = await host.getDimensions();\n if (!width || !height) {\n throw new Error(\n `WrBorderGlowHarness.movePointerTo(${clientX}, ${clientY}): this card measures ${width}×${height}, so a ` +\n 'pointer position over it means nothing — the component reads the cursor against the card centre, and ' +\n 'with no box the centre is the top-left corner and the proximity saturates for every coordinate. Stub ' +\n '`getBoundingClientRect` on the host first.'\n );\n }\n await host.dispatchEvent('pointermove', { clientX, clientY, pointerId: 1, isPrimary: true });\n }\n\n /**\n * Whether the one-shot mount sweep is running.\n *\n * The class is added synchronously when the sweep starts and removed when it ends\n * about four seconds later, or when the component is torn down — so a spec asserts\n * that it STARTED, and leaves the angle alone while it does. This is also the\n * reduced-motion assertion: `[animated]=\"true\"` for someone who asked for less motion\n * must leave this `false`, and no angle written at all.\n */\n async isSweeping(): Promise<boolean> {\n return (await this.host()).hasClass('wr-border-glow--sweeping');\n }\n\n /** Whether the halo layer is in the markup — the element the whole outer glow paints on. */\n async hasHaloLayer(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-border-glow__edge')()) !== null;\n }\n\n /**\n * Whether the decoration stays out of the accessibility tree.\n *\n * The halo layer is the component's only decorative NODE — every other layer is a\n * pseudo-element and so was never in the tree to begin with. It carries no text and no\n * name, so losing its `aria-hidden` puts an anonymous element between the reader and\n * the card's content.\n */\n async isDecorationHiddenFromAssistiveTech(): Promise<boolean> {\n return (await (await this.edge()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * The projected content's text, trimmed.\n *\n * Read from `.wr-border-glow__inner` rather than the host, which is the assertion:\n * content that lands as a bare host child instead of inside the wrapper renders\n * underneath the interior bloom, and reading the host would report the same string\n * either way.\n */\n async getContentText(): Promise<string> {\n return (await this.locatorFor('.wr-border-glow__inner')()).text();\n }\n\n /** The host's inline declarations — the component's whole written output. */\n private async declarations(): Promise<Map<string, string>> {\n return parseInlineDeclarations((await (await this.host()).getAttribute('style')) ?? '');\n }\n\n /** One inline custom property, or `null` when the component has not written it. */\n private async readVar(name: string): Promise<string | null> {\n return (await this.declarations()).get(name) ?? null;\n }\n\n /** One inline custom property that is always written, as a number. */\n private async readNumberVar(name: string, method: string): Promise<number> {\n const raw = await this.readVar(name);\n const value = raw === null ? Number.NaN : Number.parseFloat(raw);\n if (Number.isNaN(value)) {\n throw new Error(\n `WrBorderGlowHarness.${method}(): \\`${name}\\` reads \"${raw ?? '(absent)'}\" on the host. The component ` +\n 'writes this one on every input change whatever the inputs say, so a missing or unreadable value means ' +\n 'the variable bag stopped reaching the element at all.'\n );\n }\n return value;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAMH;AACA,MAAM,iBAAiB,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU;AAEjF;AACA,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAU;AAE3F;AACA,MAAM,UAAU,GAAG,2BAA2B;AAE9C;AACA,MAAM,qBAAqB,GAAG,oDAAoD;AAElF;AACA,MAAM,qBAAqB,GAAG,6CAA6C;AAE3E;;;;;;;;AAQG;AACH,SAAS,uBAAuB,CAAC,KAAa,EAAA;AAC5C,IAAA,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB;IAC9C,KAAK,MAAM,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC;QACtC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE;AAClB,QAAA,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE;QAC/C,IAAI,IAAI,KAAK,EAAE;AAAE,YAAA,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9E;AACA,IAAA,OAAO,YAAY;AACrB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCG;AACG,MAAO,mBAAoB,SAAQ,gBAAgB,CAAA;AACvD,IAAA,OAAO,YAAY,GAAG,gBAAgB;;AAGtC,IAAA,OAAO,IAAI,CAAC,OAAA,GAAsC,EAAE,EAAA;AAClD,QAAA,OAAO,IAAI,gBAAgB,CAAC,mBAAmB,EAAE,OAAO;aACrD,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,IAAI,KAC7C,gBAAgB,CAAC,aAAa,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC;aAE/D,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,MAAM,QAAQ,CAAC;IACpH;AAEiB,IAAA,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,uBAAuB,CAAC;;AAGhE,IAAA,MAAM,eAAe,GAAA;QACnB,OAAO,IAAI,CAAC,aAAa,CAAC,yBAAyB,EAAE,iBAAiB,CAAC;IACzE;;AAGA,IAAA,MAAM,aAAa,GAAA;QACjB,OAAO,IAAI,CAAC,aAAa,CAAC,0BAA0B,EAAE,eAAe,CAAC;IACxE;AAEA;;;;;AAKG;AACH,IAAA,MAAM,aAAa,GAAA;QACjB,OAAO,IAAI,CAAC,aAAa,CAAC,8BAA8B,EAAE,eAAe,CAAC;IAC5E;;AAGA,IAAA,MAAM,kBAAkB,GAAA;QACtB,OAAO,IAAI,CAAC,aAAa,CAAC,mCAAmC,EAAE,oBAAoB,CAAC;IACtF;;AAGA,IAAA,MAAM,cAAc,GAAA;QAClB,OAAO,IAAI,CAAC,aAAa,CAAC,+BAA+B,EAAE,gBAAgB,CAAC;IAC9E;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,kBAAkB,GAAA;AACtB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC;IAC5C;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC;IAC/C;AAEA;;;;;;;;;;AAUG;AACH,IAAA,MAAM,gBAAgB,GAAA;AACpB,QAAA,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;AAC9C,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,wBAAwB,CAAC;AAAE,YAAA,OAAO,IAAI;AAE5D,QAAA,OAAO,iBAAiB,CAAC,GAAG,CAAC,MAAM,IAAG;AACpC,YAAA,MAAM,IAAI,GAAG,CAAA,sBAAA,EAAyB,MAAM,EAAE;YAC9C,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAClC,YAAA,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;YAC7D,IAAI,CAAC,KAAK,EAAE;gBACV,MAAM,IAAI,KAAK,CACb,CAAA,0CAAA,EAA6C,IAAI,CAAA,UAAA,EAAa,GAAG,IAAI,UAAU,CAAA,wBAAA,CAA0B;oBACvG,mGAAmG;AACnG,oBAAA,8FAA8F,CACjG;YACH;YACA,OAAO,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACpC,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,iBAAiB,GAAA;AACrB,QAAA,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;AAC9C,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,+BAA+B,CAAC;AAAE,YAAA,OAAO,IAAI;AAEnE,QAAA,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,IAAG;AACnC,YAAA,MAAM,IAAI,GAAG,CAAA,0BAAA,EAA6B,IAAI,EAAE;YAChD,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAClC,YAAA,MAAM,MAAM,GAAG,GAAG,KAAK,SAAS,GAAG,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;YACzE,IAAI,CAAC,MAAM,EAAE;gBACX,MAAM,IAAI,KAAK,CACb,CAAA,2CAAA,EAA8C,IAAI,CAAA,UAAA,EAAa,GAAG,IAAI,UAAU,CAAA,oBAAA,CAAsB;oBACpG,sGAAsG;AACtG,oBAAA,uFAAuF,CAC1F;YACH;AACA,YAAA,OAAO,MAAM,CAAC,CAAC,CAAC;AAClB,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,oBAAoB,GAAA;QACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,gCAAgC,CAAC;QAChE,IAAI,GAAG,KAAK,IAAI;AAAE,YAAA,OAAO,IAAI;QAE7B,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;QAC9C,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,MAAM,IAAI,KAAK,CACb,CAAA,sFAAA,EAAyF,GAAG,CAAA,YAAA,CAAc;AACxG,gBAAA,mFAAmF,CACtF;QACH;AACA,QAAA,OAAO,MAAM,CAAC,CAAC,CAAC;IAClB;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,eAAe,GAAA;QACnB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC;AACxD,QAAA,OAAO,GAAG,KAAK,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;IACrD;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,gBAAgB,GAAA;QACpB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,iCAAiC,CAAC;AACjE,QAAA,OAAO,GAAG,KAAK,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;IACrD;AAEA;;;;;;;;;;;;;;;;;AAiBG;AACH,IAAA,MAAM,aAAa,CAAC,OAAe,EAAE,OAAe,EAAA;AAClD,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE;QAC9B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE;AACpD,QAAA,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE;YACrB,MAAM,IAAI,KAAK,CACb,CAAA,kCAAA,EAAqC,OAAO,CAAA,EAAA,EAAK,OAAO,CAAA,sBAAA,EAAyB,KAAK,CAAA,CAAA,EAAI,MAAM,CAAA,OAAA,CAAS;gBACvG,uGAAuG;gBACvG,uGAAuG;AACvG,gBAAA,4CAA4C,CAC/C;QACH;QACA,MAAM,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC9F;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,0BAA0B,CAAC;IACjE;;AAGA,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,EAAE,MAAM,IAAI;IAC5E;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,mCAAmC,GAAA;AACvC,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,aAAa,CAAC,MAAM,MAAM;IAC3E;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,cAAc,GAAA;AAClB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,wBAAwB,CAAC,EAAE,EAAE,IAAI,EAAE;IACnE;;AAGQ,IAAA,MAAM,YAAY,GAAA;QACxB,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACzF;;IAGQ,MAAM,OAAO,CAAC,IAAY,EAAA;AAChC,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI;IACtD;;AAGQ,IAAA,MAAM,aAAa,CAAC,IAAY,EAAE,MAAc,EAAA;QACtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QACpC,MAAM,KAAK,GAAG,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;AAChE,QAAA,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;YACvB,MAAM,IAAI,KAAK,CACb,CAAA,oBAAA,EAAuB,MAAM,CAAA,MAAA,EAAS,IAAI,CAAA,UAAA,EAAa,GAAG,IAAI,UAAU,CAAA,6BAAA,CAA+B;gBACrG,wGAAwG;AACxG,gBAAA,uDAAuD,CAC1D;QACH;AACA,QAAA,OAAO,KAAK;IACd;;;ACrWF;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-border-glow-testing.mjs","sources":["../../../projects/lib/border-glow/testing/wr-border-glow-harness.ts","../../../projects/lib/border-glow/testing/ngwr-border-glow-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 { WrBorderGlowHarnessFilters } from './interfaces';\n\n/** The seven halo steps, in the order the component writes them: 100%, 60%, 50%, 40%, 30%, 20%, 10%. */\nconst GLOW_VAR_SUFFIXES = ['', '-60', '-50', '-40', '-30', '-20', '-10'] as const;\n\n/** The seven mesh-gradient slots, spelled out rather than numbered — as the component names them. */\nconst GRADIENT_VAR_SLOTS = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'] as const;\n\n/** `hsl(40deg 80% 80% / 65%)` — the alpha, which is the only part `glowIntensity` moves. */\nconst GLOW_ALPHA = /\\/\\s*(-?[\\d.]+)%\\s*\\)\\s*$/;\n\n/** `radial-gradient(at 80% 55%, #c084fc 0px, transparent 50%)` — the colour, not the position. */\nconst RADIAL_GRADIENT_COLOR = /^radial-gradient\\(\\s*at\\s+[^,]+,\\s*(.+?)\\s+0px\\s*,/;\n\n/** `linear-gradient(#c084fc 0 100%)` — the flat base slice. */\nconst LINEAR_GRADIENT_COLOR = /^linear-gradient\\(\\s*(.+?)\\s+0\\s+100%\\s*\\)$/;\n\n/**\n * Split an inline `style` attribute into its declarations.\n *\n * Read from the attribute rather than through `getCssValue()` on purpose. jsdom echoes\n * an inline custom property back through `getComputedStyle`, so the computed read passes\n * here and would answer something else entirely in a browser — where the stylesheet's\n * own per-theme default resolves and \"the author left this to the theme\" quietly becomes\n * a colour. The attribute holds what the component itself wrote, and nothing else.\n */\nfunction parseInlineDeclarations(style: string): Map<string, string> {\n const declarations = new Map<string, string>();\n for (const declaration of style.split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n const name = declaration.slice(0, colon).trim();\n if (name !== '') declarations.set(name, declaration.slice(colon + 1).trim());\n }\n return declarations;\n}\n\n/**\n * Test harness for `<wr-border-glow>` — a card whose border lights up under the cursor.\n *\n * **Nearly all of this component is a bag of custom properties written onto the host,\n * and that is what the harness reads.** The paint itself — the conic mask, the stacked\n * `box-shadow` halo, the soft-light bloom — lives in the stylesheet, so a unit test can\n * never see it. What it CAN see is the arithmetic that feeds it, and that is where the\n * breakages are: the seven-step alpha ramp {@link getGlowAlphaRamp} derives from\n * `glowIntensity`, the `[0,1,2,0,1,2,1]` palette mapping in {@link getGradientColors}\n * with its clamp for short palettes, and the pointer maths behind\n * {@link getPointerAngle} / {@link getEdgeProximity}.\n *\n * **Every custom-property getter reads the inline declaration** — see\n * {@link parseInlineDeclarations} for why a computed read would be a different, and\n * wrong, question.\n *\n * **A colour getter answering `null` means the component has never written that\n * variable**, which is the contract when the input is unset: the stylesheet's per-theme\n * default takes over. Note that it does not un-write them either — setting `[glowColor]`\n * and then putting it back to `null` leaves the last value on the host — so `null` is\n * \"never set\", not \"not set now\".\n *\n * There is deliberately no `getGlowOpacity()`, `isGlowVisible()` or `isHovered()`: the\n * visible strength is a `calc()` in the stylesheet gated behind `:not(:hover)`, and\n * jsdom loads no stylesheet and matches no `:hover`, so any number would be invented.\n * There is no measured halo extent either — `.wr-border-glow__edge` is 0×0 in a test,\n * and {@link getGlowRadius} reads what the component asked for instead. And there is no\n * `getSweepProgress()` / `waitForSweep()`: the mount sweep is a four-second chain of\n * `requestAnimationFrame` tweens, so awaiting it either hangs the spec or samples one\n * frame and reports a value that was never settled. {@link isSweeping} answers the one\n * durable fact — that the sweep started.\n *\n * @example\n * ```ts\n * const card = await loader.getHarness(WrBorderGlowHarness.with({ text: /Hover me/ }));\n *\n * expect(await card.getBorderRadius()).toBe(28);\n * expect(await card.getPointerAngle()).toBeNull(); // nothing has pointed at it yet\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBorderGlowHarness extends ComponentHarness {\n static hostSelector = 'wr-border-glow';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBorderGlowHarnessFilters = {}): HarnessPredicate<WrBorderGlowHarness> {\n return new HarnessPredicate(WrBorderGlowHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getContentText(), text)\n )\n .addOption('sweeping', options.sweeping, async (harness, sweeping) => (await harness.isSweeping()) === sweeping);\n }\n\n private readonly edge = this.locatorFor('.wr-border-glow__edge');\n\n /** The corner radius the stylesheet is told to round to, in pixels. */\n async getBorderRadius(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-radius', 'getBorderRadius');\n }\n\n /** How far past the card edge the halo is told to reach, in pixels. */\n async getGlowRadius(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-padding', 'getGlowRadius');\n }\n\n /**\n * The width of the lit cone, as a share of the perimeter.\n *\n * Unitless by contract — the stylesheet multiplies it — so a `px` creeping in here is\n * a real regression that leaves the mask with a length where it wants a number.\n */\n async getConeSpread(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-cone-spread', 'getConeSpread');\n }\n\n /** How sharply the halo fades as the cursor leaves an edge; lower is a wider falloff. */\n async getEdgeSensitivity(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-edge-sensitivity', 'getEdgeSensitivity');\n }\n\n /** Strength of the interior soft-light bloom, again unitless. */\n async getFillOpacity(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-fill-opacity', 'getFillOpacity');\n }\n\n /**\n * The card fill the author asked for, or `null` when they left it to the theme.\n *\n * The distinction is the whole point: an unset `[backgroundColor]` means the surface\n * token decides, light or dark, and a card that starts declaring its own fill has\n * stopped following the theme.\n */\n async getBackgroundColor(): Promise<string | null> {\n return this.readVar('--wr-border-glow-bg');\n }\n\n /**\n * The halo colour at full strength, or `null` when the theme decides.\n *\n * Comes back as the `hsl(H deg S% L% / A%)` the component assembled from the bare\n * `'H S L'` input, so this also pins that assembly. Be aware the parse is forgiving to\n * a fault: anything that is not three bare numbers — a hex colour, `rgb(…)`, even\n * `hsl(40deg 80% 80%)` with its units — silently becomes the default amber, so a\n * plausible-looking value here does not mean the input was understood.\n */\n async getGlowColor(): Promise<string | null> {\n return this.readVar('--wr-border-glow-color');\n }\n\n /**\n * The seven halo alphas in percent, brightest first, or `null` when no glow colour\n * was ever set.\n *\n * This is the whole of `[glowIntensity]`: the steps are `[100, 60, 50, 40, 30, 20,\n * 10]` multiplied by the intensity and clamped at 100, so `0.5` gives\n * `[50, 30, 25, 20, 15, 10, 5]` and anything above 1 saturates from the top down. The\n * clamp is one-sided — a negative intensity produces negative percentages, which a\n * browser drops as invalid declarations and the halo vanishes — so the numbers are\n * returned as written rather than corrected, which is how a spec can see it.\n */\n async getGlowAlphaRamp(): Promise<number[] | null> {\n const declarations = await this.declarations();\n if (!declarations.has('--wr-border-glow-color')) return null;\n\n return GLOW_VAR_SUFFIXES.map(suffix => {\n const name = `--wr-border-glow-color${suffix}`;\n const raw = declarations.get(name);\n const alpha = raw === undefined ? null : GLOW_ALPHA.exec(raw);\n if (!alpha) {\n throw new Error(\n `WrBorderGlowHarness.getGlowAlphaRamp(): \\`${name}\\` reads \"${raw ?? '(absent)'}\". The seven halo steps ` +\n 'are written together from one glow colour, so a step that is missing or carries no ` / N%` alpha ' +\n 'means the ramp stopped being built — the halo would fade in bands the stylesheet cannot use.'\n );\n }\n return Number.parseFloat(alpha[1]);\n });\n }\n\n /**\n * The seven mesh-gradient colours, in slot order, or `null` when no palette was set.\n *\n * The slots do not map one-to-one onto the palette: they take colours\n * `[0, 1, 2, 0, 1, 2, 1]`, each index clamped to the last entry the palette actually\n * has. So a two-colour palette repeats the second colour rather than reading past the\n * end — which is the case worth a spec, since `undefined` in a `radial-gradient()`\n * kills the whole declaration silently.\n */\n async getGradientColors(): Promise<string[] | null> {\n const declarations = await this.declarations();\n if (!declarations.has('--wr-border-glow-gradient-one')) return null;\n\n return GRADIENT_VAR_SLOTS.map(slot => {\n const name = `--wr-border-glow-gradient-${slot}`;\n const raw = declarations.get(name);\n const colour = raw === undefined ? null : RADIAL_GRADIENT_COLOR.exec(raw);\n if (!colour) {\n throw new Error(\n `WrBorderGlowHarness.getGradientColors(): \\`${name}\\` reads \"${raw ?? '(absent)'}\", which is not the ` +\n '`radial-gradient(at X% Y%, <colour> 0px, …)` the component builds. All seven slots are written from ' +\n 'one palette, so one of them being different means the mapping broke, not the palette.'\n );\n }\n return colour[1];\n });\n }\n\n /**\n * The flat colour behind the mesh, or `null` when no palette was set.\n *\n * Always the palette's FIRST entry. Worth its own method because the base slice is the\n * one that shows where the gradients are transparent, and it drifting to the last\n * colour changes the card's whole cast without touching a gradient.\n */\n async getBaseGradientColor(): Promise<string | null> {\n const raw = await this.readVar('--wr-border-glow-gradient-base');\n if (raw === null) return null;\n\n const colour = LINEAR_GRADIENT_COLOR.exec(raw);\n if (!colour) {\n throw new Error(\n `WrBorderGlowHarness.getBaseGradientColor(): \\`--wr-border-glow-gradient-base\\` reads \"${raw}\", which is ` +\n 'not the `linear-gradient(<colour> 0 100%)` the component builds from the palette.'\n );\n }\n return colour[1];\n }\n\n /**\n * Where the cursor last sat, in degrees: `0` is straight up and the angle grows\n * clockwise. `null` until something has pointed at the card.\n *\n * `null` is a state of its own rather than a stand-in for zero — the stylesheet's own\n * default applies until the first pointer arrives, and dead centre legitimately\n * reports `0`, so a harness that folded the two together could not tell an untouched\n * card from one being pointed at from the middle.\n */\n async getPointerAngle(): Promise<number | null> {\n const raw = await this.readVar('--wr-border-glow-angle');\n return raw === null ? null : Number.parseFloat(raw);\n }\n\n /**\n * How close the cursor is to the perimeter: `0` at dead centre, `100` at the edge.\n * `null` until something has pointed at the card.\n *\n * On a diagonal the NEARER axis wins rather than the two averaging, so a cursor level\n * with the bottom edge reads 100 wherever it is along it.\n */\n async getEdgeProximity(): Promise<number | null> {\n const raw = await this.readVar('--wr-border-glow-edge-proximity');\n return raw === null ? null : Number.parseFloat(raw);\n }\n\n /**\n * Point at the card, in client coordinates.\n *\n * **Refuses on a card with no box, which in a unit test is every card.** The component\n * measures the pointer against its own centre, and with a 0×0 rect that maths\n * degenerates: the centre is the top-left corner, and `1 / Math.min(0, 0)` pins the\n * proximity at 100 for every position on the page. Both would come back as confident\n * numbers describing nothing. Give the host a box first, exactly as the component's own\n * spec does:\n *\n * ```ts\n * const card = fixture.nativeElement.querySelector('wr-border-glow');\n * card.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * A `pointermove` is dispatched on the host itself, so it needs no hit test and cannot\n * miss — the coordinates only ever reach the component's own arithmetic.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n const host = await this.host();\n const { width, height } = await host.getDimensions();\n if (!width || !height) {\n throw new Error(\n `WrBorderGlowHarness.movePointerTo(${clientX}, ${clientY}): this card measures ${width}×${height}, so a ` +\n 'pointer position over it means nothing — the component reads the cursor against the card centre, and ' +\n 'with no box the centre is the top-left corner and the proximity saturates for every coordinate. Stub ' +\n '`getBoundingClientRect` on the host first.'\n );\n }\n await host.dispatchEvent('pointermove', { clientX, clientY, pointerId: 1, isPrimary: true });\n }\n\n /**\n * Whether the one-shot mount sweep is running.\n *\n * The class is added synchronously when the sweep starts and removed when it ends\n * about four seconds later, or when the component is torn down — so a spec asserts\n * that it STARTED, and leaves the angle alone while it does. This is also the\n * reduced-motion assertion: `[animated]=\"true\"` for someone who asked for less motion\n * must leave this `false`, and no angle written at all.\n */\n async isSweeping(): Promise<boolean> {\n return (await this.host()).hasClass('wr-border-glow--sweeping');\n }\n\n /** Whether the halo layer is in the markup — the element the whole outer glow paints on. */\n async hasHaloLayer(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-border-glow__edge')()) !== null;\n }\n\n /**\n * Whether the decoration stays out of the accessibility tree.\n *\n * The halo layer is the component's only decorative NODE — every other layer is a\n * pseudo-element and so was never in the tree to begin with. It carries no text and no\n * name, so losing its `aria-hidden` puts an anonymous element between the reader and\n * the card's content.\n */\n async isDecorationHiddenFromAssistiveTech(): Promise<boolean> {\n return (await (await this.edge()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * The projected content's text, trimmed.\n *\n * Read from `.wr-border-glow__inner` rather than the host, which is the assertion:\n * content that lands as a bare host child instead of inside the wrapper renders\n * underneath the interior bloom, and reading the host would report the same string\n * either way.\n */\n async getContentText(): Promise<string> {\n return (await this.locatorFor('.wr-border-glow__inner')()).text();\n }\n\n /** The host's inline declarations — the component's whole written output. */\n private async declarations(): Promise<Map<string, string>> {\n return parseInlineDeclarations((await (await this.host()).getAttribute('style')) ?? '');\n }\n\n /** One inline custom property, or `null` when the component has not written it. */\n private async readVar(name: string): Promise<string | null> {\n return (await this.declarations()).get(name) ?? null;\n }\n\n /** One inline custom property that is always written, as a number. */\n private async readNumberVar(name: string, method: string): Promise<number> {\n const raw = await this.readVar(name);\n const value = raw === null ? Number.NaN : Number.parseFloat(raw);\n if (Number.isNaN(value)) {\n throw new Error(\n `WrBorderGlowHarness.${method}(): \\`${name}\\` reads \"${raw ?? '(absent)'}\" on the host. The component ` +\n 'writes this one on every input change whatever the inputs say, so a missing or unreadable value means ' +\n 'the variable bag stopped reaching the element at all.'\n );\n }\n return value;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}